feat: validate relay manifest accepted keys

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Jinwoo-H
2026-07-15 22:44:36 -04:00
co-authored by Orca
parent 6358fb466b
commit 11f367e661
6 changed files with 272 additions and 4 deletions
@@ -268,6 +268,7 @@ jobs:
src/main/ssh/ssh-relay-artifact-cache-entry.test.ts \
src/main/ssh/ssh-relay-artifact-cache-in-use-lease.test.ts \
src/main/ssh/ssh-relay-artifact-cache-eviction.test.ts \
src/main/ssh/ssh-relay-manifest-accepted-keys.test.ts \
src/main/ssh/ssh-relay-packaged-manifest.test.ts \
src/main/ssh/ssh-relay-runtime-identity.test.ts \
config/scripts/ssh-relay-runtime-compatibility.test.mjs \
@@ -803,6 +804,7 @@ jobs:
src/main/ssh/ssh-relay-artifact-cache-entry.test.ts `
src/main/ssh/ssh-relay-artifact-cache-in-use-lease.test.ts `
src/main/ssh/ssh-relay-artifact-cache-eviction.test.ts `
src/main/ssh/ssh-relay-manifest-accepted-keys.test.ts `
src/main/ssh/ssh-relay-packaged-manifest.test.ts `
src/main/ssh/ssh-relay-runtime-identity.test.ts `
config/scripts/ssh-relay-runtime-compatibility.test.mjs `
@@ -126,6 +126,7 @@ describe('SSH relay runtime artifact workflow', () => {
'ssh-relay-artifact-cache-entry.test.ts',
'ssh-relay-artifact-cache-in-use-lease.test.ts',
'ssh-relay-artifact-cache-eviction.test.ts',
'ssh-relay-manifest-accepted-keys.test.ts',
'ssh-relay-packaged-manifest.test.ts',
'ssh-relay-runtime-identity.test.ts'
]) {
@@ -9,9 +9,12 @@ keeps commands, hashes, runner identities, timings, and failure details.
A checked box means the work has evidence in the detailed ledger. Design approval alone does not
complete a box.
Active checkpoint: **Milestone 5 / Work Package 4 desktop resolver/cache — disconnected
official-build manifest loader proven on all six native clients; accepted-key/resource-input audit
next, 2026-07-15, Codex implementation owner.** `E-M5-PACKAGED-MANIFEST-LOCAL-001` passes 6/6
Active checkpoint: **Milestone 5 / Work Package 4 desktop resolver/cache — disconnected desktop
accepted-public-key parser locally proven and wired; isolated commit/all-six native proof next,
2026-07-15, Codex implementation owner.** `E-M5-ACCEPTED-KEYS-AUDIT-001` and its RED precede
`E-M5-ACCEPTED-KEYS-LOCAL-001`: 4/4 focused, 19/19 trust, 304/304 non-full-size SSH relay, 12/12
workflow/aggregate, typecheck, and lint gates pass. No production public key file or secret exists,
and no resource or consumer is connected. The prior `E-M5-PACKAGED-MANIFEST-LOCAL-001` passes 6/6
purpose-named tests, 300/300 non-full-size SSH relay tests, 279/279 release-script tests, typecheck,
lint, format, max-lines, and diff gates. `E-M5-PACKAGED-MANIFEST-CI-WIRING-LOCAL-001` pins the suite
into both native job families; exact-head run
@@ -8,7 +8,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 5 / Work Package 4 desktop resolver/cache — **In progress — 2026-07-15, Codex implementation owner: all-six native packaged-manifest source gate closed; audit the next disconnected accepted-key/resource-embedding prerequisite**. `E-M5-PACKAGED-MANIFEST-CI-001` proves the fixed-resource loader on Linux, macOS, and Windows, x64 and arm64, under Node 24.18.0. Keep production keys/secrets, signed embedded bytes, Electron/cache-root/downloader/SSH/settings/tuples/publication/default behavior disconnected while selecting the next purpose-named RED.<br>
Current phase: Milestone 5 / Work Package 4 desktop resolver/cache — **In progress — 2026-07-15, Codex implementation owner: checkpoint and obtain all-six native evidence for the locally proven disconnected accepted-public-key parser**. `E-M5-ACCEPTED-KEYS-LOCAL-001` proves strict protected-aggregate document parity and both native workflow command lists locally. Keep the production key file/seed/environment, signed embedded bytes, builder/resource/consumer/cache/downloader/SSH/settings/tuples/publication/default behavior absent until exact-head native proof passes.<br>
Session checkpoint: **In progress — 2026-07-15, Codex implementation owner** — user authority covers end-to-end PR-contained implementation, commits, pushes, CI, rehearsals, and release writes; merging to `main` remains explicitly prohibited. Commit `b79bcbd04` passes all six native source/full-size cache jobs, PR Checks, and Golden E2E. The artifact workflow is red only for the retained hosted Windows arm64 build-26200 versus required-26100 floor rejection after its primary proof. No Electron, downloader, SSH, setting, tuple, publication, or default-path consumer exists. 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)
@@ -13933,6 +13933,81 @@ config/scripts/ssh-relay-runtime-workflow.test.mjs`.
and immutable resource-input contract. External protected-environment/secret provisioning remains
a separate repository-administrator gate and must not be inferred from source tests.
### E-M5-ACCEPTED-KEYS-AUDIT-001 — Desktop accepted-public-key document parity
- Date: 2026-07-15
- Owner: Codex implementation owner
- Source: exact pushed evidence head `6358fb466b2b2099f4c9add9b8e2f772664ec3ea`.
- Audit: complete reads of the desktop signature/loader modules and tests, protected manifest
workflow, aggregate command/signing handoff and tests, Electron builder config/tests, package
release scripts, and Linux/Windows/macOS release workflow build/package boundaries; targeted `rg`
over accepted keys, public-key encoding, `extraResources`, manifest signing, desktop build inputs,
and release packaging.
- Findings:
- the protected aggregate already defines the authoritative source format: exact
`{schemaVersion, keys}` fields, schema version 1, one to four keys, exact
`{keyId, publicKeyBase64}` entries, canonical base64 for exactly 32 Ed25519 public-key bytes,
derived SHA-256 key IDs, uniqueness, key-ID sorting, and a SHA-256 fingerprint over canonical
JSON;
- the protected workflow deliberately references an absent
`config/ssh-relay-runtime-manifest-accepted-keys.json` and tests ENOENT so no placeholder can be
mistaken for a reviewed trust root. Release-cut and macOS release workflows remain disconnected;
- the desktop signature verifier accepts decoded key bytes but has no strict parser for the
aggregate document. Adding that parser with deterministic test keys is the smallest useful
PR-contained parity boundary before production key review or immutable resource embedding;
- accepted keys must ultimately be compile-time desktop inputs. Loading a mutable adjacent key
file beside the manifest would let the trust root move with untrusted bytes and is not allowed;
- builder mapping, aggregate-to-desktop artifact download, tag checkout dependencies, byte-identical
packaged extraction, real public key review, and protected seed/environment provisioning remain
separate gates.
- Selected package: pure desktop parser returning cloned/sorted accepted key bytes plus the canonical
accepted-key document fingerprint; strict hostile-input tests and release-format parity; no file
I/O, Electron, resource mapping, manifest, signer, secret, cache, network, SSH, mode, or consumer.
### E-M5-ACCEPTED-KEYS-LOCAL-RED-001 — Desktop accepted-key parser is absent
- Date: 2026-07-15
- Owner: Codex implementation owner
- Source: purpose-named `src/main/ssh/ssh-relay-manifest-accepted-keys.test.ts` atop exact pushed
evidence head `6358fb466b2b2099f4c9add9b8e2f772664ec3ea`; production parser deliberately absent.
- Exact command: `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts
--maxWorkers=1 src/main/ssh/ssh-relay-manifest-accepted-keys.test.ts`.
- Environment: macOS 26.2 arm64 build 25C56, Node v26.0.0, pnpm 10.24.0, Vitest 4.1.5.
- Result: required RED; one failed suite / zero collected tests in 174ms Vitest / 1.16s wall,
131,989,504-byte maximum RSS, 96,179,600-byte peak memory footprint, and zero swaps. Import fails
because `./ssh-relay-manifest-accepted-keys` does not exist.
- Contracts encoded before implementation: canonical sort/fingerprint, cloned key bytes, frozen
container/records, one-to-four bound, exact fields/schema, canonical base64/32-byte keys, derived
identity, duplicate rejection, and input-order independence.
- Consumer-disconnection oracle: only the purpose-named test imports the absent module; fixtures use
deterministic test seeds. No production key file/seed/environment, resource mapping, Electron,
cache, network, SSH, setting, tuple, publication, or default call site is added.
### E-M5-ACCEPTED-KEYS-LOCAL-001 — Desktop accepted-key parser and native wiring
- Date: 2026-07-15
- Owner: Codex implementation owner
- Source: working package atop exact pushed evidence head
`6358fb466b2b2099f4c9add9b8e2f772664ec3ea`; implementation/test are
`src/main/ssh/ssh-relay-manifest-accepted-keys.ts` and its purpose-named test.
- Focused command/result: `/usr/bin/time -l pnpm exec vitest run --config
config/vitest.config.ts --maxWorkers=1 src/main/ssh/ssh-relay-manifest-accepted-keys.test.ts` passes
1 file / 4 tests in 301ms Vitest / 1.23s wall, 131,874,816-byte maximum RSS,
96,048,504-byte peak memory footprint, and zero swaps.
- Proven: exact schema/fields; one-to-four bound; canonical 32-byte base64; derived SHA-256 IDs;
duplicate rejection; input-order-independent sorting; cloned bytes; frozen container/records; and
the exact canonical JSON fingerprint used by the protected aggregate.
- Regression commands/results: accepted-keys/signature/packaged-loader trust suites pass 19/19;
complete `src/main/ssh/ssh-relay-*.test.ts` passes 23 files / 304 tests with three declared
full-size skips; workflow plus aggregate-command suites pass 12/12; full `pnpm typecheck` and
`pnpm lint` pass, including 41 reliability gates and no new max-lines bypass.
- Native wiring: the purpose-named suite is present once in each POSIX/PowerShell command list; the
workflow oracle requires exactly two occurrences. Exact-head all-six Node 24 execution remains
required before this source gate closes.
- Consumer-disconnection oracle: repository consumers remain test-only. The protected workflow still
requires the production accepted-key file to be absent; no key/seed/environment, resource bytes,
builder mapping, Electron/cache/network/SSH/mode/tuple/publication/default caller is added.
## Accepted Gaps
No product gap is accepted merely because it appears in this list. Each entry requires explicit
@@ -0,0 +1,88 @@
import { createHash } from 'node:crypto'
import nacl from 'tweetnacl'
import { describe, expect, it } from 'vitest'
import { parseSshRelayManifestAcceptedKeyDocument } from './ssh-relay-manifest-accepted-keys'
import { sshRelayManifestKeyId } from './ssh-relay-manifest-signature'
const first = nacl.sign.keyPair.fromSeed(Uint8Array.from({ length: 32 }, (_, index) => index))
const second = nacl.sign.keyPair.fromSeed(Uint8Array.from({ length: 32 }, (_, index) => 31 - index))
function record(publicKey: Uint8Array) {
return {
keyId: sshRelayManifestKeyId(publicKey),
publicKeyBase64: Buffer.from(publicKey).toString('base64')
}
}
function document(keys = [record(first.publicKey)]) {
return { schemaVersion: 1, keys }
}
describe('SSH relay manifest accepted keys', () => {
it('parses, clones, sorts, freezes, and fingerprints the canonical release document', () => {
const input = document([record(second.publicKey), record(first.publicKey)])
const parsed = parseSshRelayManifestAcceptedKeyDocument(input)
const sortedRecords = [...input.keys].sort((left, right) =>
left.keyId < right.keyId ? -1 : left.keyId > right.keyId ? 1 : 0
)
const canonicalBytes = Buffer.from(
JSON.stringify({ schemaVersion: 1, keys: sortedRecords }),
'utf8'
)
expect(parsed.acceptedKeys.map(({ keyId }) => keyId)).toEqual(
sortedRecords.map(({ keyId }) => keyId)
)
expect(parsed.acceptedKeys[0].publicKey).not.toBe(first.publicKey)
expect(parsed.sha256).toBe(
`sha256:${createHash('sha256').update(canonicalBytes).digest('hex')}`
)
expect(Object.isFrozen(parsed)).toBe(true)
expect(Object.isFrozen(parsed.acceptedKeys)).toBe(true)
expect(parsed.acceptedKeys.every(Object.isFrozen)).toBe(true)
})
it('accepts one to four unique keys independent of input order', () => {
const records = [record(first.publicKey), record(second.publicKey)]
const forward = parseSshRelayManifestAcceptedKeyDocument(document(records))
const reverse = parseSshRelayManifestAcceptedKeyDocument(document(records.toReversed()))
expect(forward.sha256).toBe(reverse.sha256)
expect(forward.acceptedKeys.map(({ keyId }) => keyId)).toEqual(
reverse.acceptedKeys.map(({ keyId }) => keyId)
)
})
it('rejects malformed document shape and key counts', () => {
for (const input of [
null,
[],
{},
{ schemaVersion: 2, keys: [record(first.publicKey)] },
{ schemaVersion: 1, keys: [], extra: true },
{ schemaVersion: 1, keys: [] },
{ schemaVersion: 1, keys: Array.from({ length: 5 }, () => record(first.publicKey)) }
]) {
expect(() => parseSshRelayManifestAcceptedKeyDocument(input)).toThrow(/accepted|key|field/i)
}
})
it('rejects extra fields, non-canonical base64, wrong sizes, identity drift, and duplicates', () => {
const valid = record(first.publicKey)
for (const key of [
{ ...valid, extra: true },
{ ...valid, publicKeyBase64: `${valid.publicKeyBase64}\n` },
{ ...valid, publicKeyBase64: Buffer.alloc(31).toString('base64') },
{ ...valid, keyId: `sha256:${'0'.repeat(64)}` }
]) {
expect(() =>
parseSshRelayManifestAcceptedKeyDocument({ schemaVersion: 1, keys: [key] })
).toThrow(/accepted|key|base64|field|32/i)
}
expect(() =>
parseSshRelayManifestAcceptedKeyDocument(document([valid, structuredClone(valid)]))
).toThrow(/duplicate/i)
})
})
@@ -0,0 +1,99 @@
import { createHash } from 'node:crypto'
import {
sshRelayManifestKeyId,
type SshRelayManifestAcceptedKey
} from './ssh-relay-manifest-signature'
import type { SshRelayDigest } from './ssh-relay-runtime-identity'
const BASE64 = /^[A-Za-z0-9+/]+={0,2}$/u
const MAXIMUM_KEYS = 4
type AcceptedKeyRecord = {
keyId: string
publicKeyBase64: string
}
function assertObject(value: unknown, label: string): asserts value is Record<string, unknown> {
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
throw new Error(`SSH relay manifest ${label} must be an object`)
}
}
function assertExactFields(value: Record<string, unknown>, fields: string[], label: string): void {
const actual = Object.keys(value).sort()
const expected = [...fields].sort()
if (
actual.length !== expected.length ||
actual.some((field, index) => field !== expected[index])
) {
throw new Error(`SSH relay manifest ${label} has unexpected or missing fields`)
}
}
function decodePublicKey(value: unknown): { bytes: Buffer; base64: string } {
if (typeof value !== 'string' || !BASE64.test(value)) {
throw new Error('SSH relay manifest accepted public key is not canonical base64')
}
const bytes = Buffer.from(value, 'base64')
if (bytes.byteLength !== 32 || bytes.toString('base64') !== value) {
throw new Error('SSH relay manifest accepted public key must contain exactly 32 bytes')
}
return { bytes, base64: value }
}
function sha256(bytes: Uint8Array): SshRelayDigest {
return `sha256:${createHash('sha256').update(bytes).digest('hex')}`
}
export function parseSshRelayManifestAcceptedKeyDocument(input: unknown): Readonly<{
acceptedKeys: readonly Readonly<SshRelayManifestAcceptedKey>[]
sha256: SshRelayDigest
}> {
assertObject(input, 'accepted-key document')
assertExactFields(input, ['keys', 'schemaVersion'], 'accepted-key document')
if (
input.schemaVersion !== 1 ||
!Array.isArray(input.keys) ||
input.keys.length === 0 ||
input.keys.length > MAXIMUM_KEYS
) {
throw new Error('SSH relay manifest accepted keys must be a bounded schema-version-1 array')
}
const seen = new Set<string>()
const parsed = input.keys
.map((value, index) => {
assertObject(value, `accepted key ${index}`)
assertExactFields(value, ['keyId', 'publicKeyBase64'], `accepted key ${index}`)
const decoded = decodePublicKey(value.publicKeyBase64)
const publicKey = decoded.bytes
const keyId = sshRelayManifestKeyId(publicKey)
if (value.keyId !== keyId) {
throw new Error('SSH relay manifest accepted key ID disagrees with its public key bytes')
}
if (seen.has(keyId)) {
throw new Error(`Duplicate accepted SSH relay manifest key: ${keyId}`)
}
seen.add(keyId)
return { keyId, publicKeyBase64: decoded.base64, publicKey }
})
.sort((left, right) => (left.keyId < right.keyId ? -1 : left.keyId > right.keyId ? 1 : 0))
// Why: the protected aggregate fingerprints this exact sorted projection, so desktop build
// evidence can prove it embedded the reviewed public-key set without trusting input order.
const canonicalRecords: AcceptedKeyRecord[] = parsed.map(({ keyId, publicKeyBase64 }) => ({
keyId,
publicKeyBase64
}))
const canonicalBytes = Buffer.from(
JSON.stringify({ schemaVersion: 1, keys: canonicalRecords }),
'utf8'
)
const acceptedKeys = Object.freeze(
parsed.map(({ keyId, publicKey }) => Object.freeze({ keyId, publicKey }))
)
return Object.freeze({ acceptedKeys, sha256: sha256(canonicalBytes) })
}
export const SSH_RELAY_MANIFEST_ACCEPTED_KEY_LIMITS = Object.freeze({ maximumKeys: MAXIMUM_KEYS })