mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 18:17:36 +00:00
Desktop, CLI, web and runtime transports now take a PairedRuntimeConnection (endpoint, pinned host key, credential) instead of a PairingOffer. Offers stay on the pairing input path (codes, QR, links, environment add) and are converted with pairedRuntimeConnectionFromOffer. The credential is token-only today; request envelopes still carry the same deviceToken, so the wire is unchanged. This lets device-key and ticket credentials replace the token later without touching every connect call site again. Co-authored-by: m4air <m4air@Mac.localdomain>
156 lines
5.9 KiB
TypeScript
156 lines
5.9 KiB
TypeScript
import { pairedRuntimeConnectionFromOffer } from './paired-runtime-connection'
|
|
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
import { encodePairingOffer } from './pairing'
|
|
import {
|
|
addEnvironmentFromPairingCode,
|
|
getEnvironmentStorePath,
|
|
listEnvironments,
|
|
removeEnvironment
|
|
} from './runtime-environment-store'
|
|
import {
|
|
getRuntimeEnvironmentSidecarPath,
|
|
writeRuntimeEnvironmentSidecarEntry
|
|
} from './runtime-environment-sidecar'
|
|
import { assertRuntimeEnvironmentNotReconciling } from './runtime-environment-reconciliation-record'
|
|
import {
|
|
linkVerifiedRuntimeEnvironmentSshAccess,
|
|
prepareRuntimeEnvironmentSshAccessLink
|
|
} from './runtime-environment-ssh-access-store'
|
|
import { readPersistedEnvironmentStore } from './runtime-environment-store-file'
|
|
import { shippedBuildRewrite } from './runtime-environment-shipped-store.test-fixture'
|
|
|
|
const pairing = {
|
|
v: 2 as const,
|
|
endpoint: 'wss://server.example/runtime',
|
|
publicKeyB64: Buffer.alloc(32, 1).toString('base64'),
|
|
deviceToken: 'private-device-token',
|
|
pairedDeviceId: 'paired-client'
|
|
}
|
|
const tunnel = { sshTargetId: 'target', sshTargetGeneration: 3, localPort: 41000, remotePort: 6768 }
|
|
|
|
describe('runtime environment sidecar across a downgrade', () => {
|
|
let userDataPath: string
|
|
beforeEach(() => {
|
|
userDataPath = mkdtempSync(join(tmpdir(), 'orca-environment-sidecar-'))
|
|
})
|
|
afterEach(() => {
|
|
rmSync(userDataPath, { recursive: true, force: true })
|
|
})
|
|
|
|
function linked() {
|
|
const seeded = addEnvironmentFromPairingCode(userDataPath, {
|
|
name: 'Host',
|
|
pairingCode: encodePairingOffer(pairing),
|
|
now: 100
|
|
})
|
|
const prepared = prepareRuntimeEnvironmentSshAccessLink(userDataPath, {
|
|
expectedEnvironment: seeded,
|
|
requestId: 'link-request',
|
|
...tunnel,
|
|
targetFingerprint: 'target-fingerprint'
|
|
})
|
|
return linkVerifiedRuntimeEnvironmentSshAccess(userDataPath, {
|
|
expectedEnvironment: prepared,
|
|
requestId: 'link-request',
|
|
verifiedRuntimeId: 'host-runtime',
|
|
verifiedConnection: pairedRuntimeConnectionFromOffer({
|
|
...pairing,
|
|
endpoint: 'ws://127.0.0.1:41000/runtime'
|
|
}),
|
|
tunnel,
|
|
now: 90
|
|
})
|
|
}
|
|
|
|
it('keeps SSH access after a shipped build rewrites orca-environments.json', () => {
|
|
const access = linked()
|
|
shippedBuildRewrite(userDataPath, (environments) => {
|
|
environments[0]!.lastUsedAt = 500
|
|
environments[0]!.runtimeId = 'host-runtime'
|
|
})
|
|
const [restored] = listEnvironments(userDataPath)
|
|
expect(restored?.sshAccess).toEqual(access.sshAccess)
|
|
expect(restored?.preferredEndpointId).toBe(access.preferredEndpointId)
|
|
expect(restored?.lastUsedAt).toBe(500)
|
|
})
|
|
|
|
it('reads a link as stale once a shipped build re-pairs the server, and prunes it on the next write', () => {
|
|
const access = linked()
|
|
shippedBuildRewrite(userDataPath, (environments) => {
|
|
environments[0]!.pairingRevision = 200
|
|
})
|
|
const [restored] = listEnvironments(userDataPath)
|
|
expect(restored?.sshAccess).toBeUndefined()
|
|
expect(restored?.preferredEndpointId).toBe(access.sshAccess?.previousPreferredEndpointId)
|
|
expect(restored?.connectionDependency).toBeUndefined()
|
|
|
|
const persisted = readPersistedEnvironmentStore(userDataPath).environments
|
|
writeRuntimeEnvironmentSidecarEntry(userDataPath, persisted, persisted[0]!, null)
|
|
expect(
|
|
JSON.parse(readFileSync(getRuntimeEnvironmentSidecarPath(userDataPath), 'utf8'))
|
|
).toEqual({ version: 1, entries: {} })
|
|
})
|
|
|
|
it('ignores a dangling entry for a server a shipped build removed', () => {
|
|
linked()
|
|
shippedBuildRewrite(userDataPath, (environments) => {
|
|
environments.splice(0, 1)
|
|
})
|
|
expect(listEnvironments(userDataPath)).toEqual([])
|
|
const other = addEnvironmentFromPairingCode(userDataPath, {
|
|
name: 'Other',
|
|
pairingCode: encodePairingOffer(pairing)
|
|
})
|
|
expect(listEnvironments(userDataPath)).toEqual([other])
|
|
})
|
|
|
|
it('refuses ordinary removal while linked, and drops the entry when an unlinked server is removed', () => {
|
|
const access = linked()
|
|
expect(() => removeEnvironment(userDataPath, access.id)).toThrow('Unlink')
|
|
const plain = addEnvironmentFromPairingCode(userDataPath, {
|
|
name: 'Plain',
|
|
pairingCode: encodePairingOffer(pairing)
|
|
})
|
|
removeEnvironment(userDataPath, plain.id)
|
|
expect(listEnvironments(userDataPath).map((entry) => entry.id)).toEqual([access.id])
|
|
})
|
|
|
|
it('fails closed on an unreadable sidecar instead of dropping a pinned host key', () => {
|
|
linked()
|
|
writeFileSync(getRuntimeEnvironmentSidecarPath(userDataPath), '{not json')
|
|
expect(() => listEnvironments(userDataPath)).toThrow('Orca environment links')
|
|
})
|
|
|
|
it('overlays a reconciliation record so lifecycle changes refuse while it exists', () => {
|
|
const seeded = addEnvironmentFromPairingCode(userDataPath, {
|
|
name: 'Host',
|
|
pairingCode: encodePairingOffer(pairing),
|
|
now: 100
|
|
})
|
|
const persisted = readPersistedEnvironmentStore(userDataPath).environments
|
|
writeRuntimeEnvironmentSidecarEntry(userDataPath, persisted, persisted[0]!, {
|
|
reconciliation: {
|
|
version: 1,
|
|
stage: 'prepared',
|
|
requestId: 'reconcile',
|
|
canonicalEnvironmentId: seeded.id,
|
|
runtimeId: 'host-runtime',
|
|
preparedAt: 1,
|
|
registrations: [
|
|
{ environmentId: seeded.id, authorityDigest: 'a'.repeat(64) },
|
|
{ environmentId: 'other', authorityDigest: 'b'.repeat(64) }
|
|
]
|
|
}
|
|
})
|
|
const [restored] = listEnvironments(userDataPath)
|
|
expect(() => assertRuntimeEnvironmentNotReconciling(restored!)).toThrow('reconciliation')
|
|
expect(JSON.parse(readFileSync(getEnvironmentStorePath(userDataPath), 'utf8'))).toEqual({
|
|
version: 1,
|
|
environments: [seeded]
|
|
})
|
|
})
|
|
})
|