dsh 0.2.0-rc.2: in-container update (experimental channel), path-agnostic compose, upstream patches adapted to 0.1.x/0.2.x

This commit is contained in:
hermes committed 2026-10-01 19:21:19 +00:00
commit e6dcef9bf5
302 files changed
+75178

No files matched your search

@@ -0,0 +1 @@
1.0.18
@@ -0,0 +1,454 @@
#!/usr/bin/env node
import { mkdir, readFile, realpath } from 'node:fs/promises'
import { join } from 'node:path'
import { EnvironmentRunner, loadControlPlane } from './lib/lifecycle.mjs'
import { BootstrapRuntime } from './lib/runtime.mjs'
import { createBootstrapControl, listenBootstrapControl } from './lib/control.mjs'
import { createDshLifecycleServer, DshLifecycleBroker, listenDshLifecycle } from './lib/dsh-lifecycle.mjs'
import { JsonlLogManager } from '../../control-plane/modules/log-manager/index.mjs'
import { PlatformPaths } from '../lib/paths.mjs'
import { parseImageInventory, recordsFromImageInventory } from '../lib/deployment-contracts.mjs'
import { DeploymentManager, DeploymentResolutionError } from './lib/deployments.mjs'
import { LocalApiClient } from '../../control-plane/modules/updater/lib/client.mjs'
import {
discardSystemPluginSelection,
linkSystemPluginScope,
listManagedSystemPlugins,
materializeSystemPluginSelection,
pruneSystemPluginSelectionViews,
SystemPluginSelectionStore,
} from '../../control-plane/modules/plugin-manager/system.mjs'
import { replaceSystemPluginView } from '../lib/paths.mjs'
import { verifyRuntimePatches } from '../../control-plane/modules/patch-manager/index.mjs'
import {
materializeSystemSkillCatalog,
retainLegacySystemSkillCatalog,
SystemSkillManager,
} from '../../control-plane/modules/skill-manager/index.mjs'
import { SnapshotClient } from '../../control-plane/modules/updater/lib/snapshot-client.mjs'
import { recoverPlatformUpdateBeforeDshStart } from '../../control-plane/hooks/recovery/index.mjs'
import { recoverUserPluginBeforeDshStart } from '../../control-plane/modules/plugin-manager/user-transaction.mjs'
import { UserPluginJournal } from '../../control-plane/modules/plugin-manager/user-journal.mjs'
import { UserPluginSelectionStore } from '../../control-plane/modules/plugin-manager/user-state.mjs'
import {
outboundProxyEnvironment,
outboundProxyScopeEnabled,
outboundProxyUrl,
clearOutboundProxyEnvironment,
parseOutboundProxyEnvironment,
} from '../lib/outbound-proxy.mjs'
import { parseAccessEvidence } from '../lib/access-evidence.mjs'
const dataRoot = process.env.DSH_PLATFORM_DATA ?? '/data/platform'
const runRoot = process.env.DSH_PLATFORM_RUN ?? '/run/dsh-platform'
const paths = new PlatformPaths(dataRoot, runRoot)
const logs = new JsonlLogManager({
root: paths.logsRoot,
maxBytes: Number(process.env.DSH_LOG_MAX_BYTES ?? 104857600),
retentionDays: Number(process.env.DSH_LOG_RETENTION_DAYS ?? 14),
output: { stdout: process.stdout, stderr: process.stderr },
})
logs.on('error', error => { void logs.diagnostic('log-manager', 'capture.failed', { error }) })
const seedRoot = process.env.DSH_PLATFORM_SEED ?? '/opt/dsh-platform/seed'
const inventory = parseImageInventory(await readFile(join(seedRoot, 'inventory.json')))
const imageRecords = recordsFromImageInventory(inventory)
const dshHome = process.env.DSH_HOME ?? '/data/dsh'
const accessEvidence = parseAccessEvidence(process.env.DSH_LEGACY_INSTALLATION_EVIDENCE)
await logs.diagnostic('bootstrap', 'bootstrap.starting', {
bootstrapVersion: process.env.DSH_BOOTSTRAP_VERSION ?? null,
imageBuildId: inventory.imageBuildId,
targetSequence: inventory.targetSequence,
})
const deployments = new DeploymentManager({ paths, seedRoot, inventory })
const trust = new LocalApiClient(paths.trustSocket)
const outboundProxy = new LocalApiClient(paths.proxyControlSocket)
const snapshots = new SnapshotClient(new LocalApiClient(paths.snapshotSocket), 'dsh')
const access = new LocalApiClient(paths.accessSocket)
try {
await deployments.recoverActivation(trust)
} catch (error) {
await logs.diagnostic('bootstrap', 'deployment.activation-recovery.failed', { error })
throw error
}
let imagePlan
let previousPlan
let planningError = null
try {
imagePlan = await deployments.prepareImage(imageRecords.deployment)
await deployments.publishStatus({ plan: imagePlan, currentId: imagePlan.target })
} catch (error) {
planningError = error
await logs.diagnostic('bootstrap', 'deployment.image-plan.failed', { error })
if (error instanceof DeploymentResolutionError) {
try {
previousPlan = await deployments.preparePreviousRecovery()
await deployments.publishStatus({ currentId: previousPlan.target, operation: 'recovering' })
} catch (recoveryError) {
planningError = new AggregateError([error, recoveryError], 'Deployment resolution and previous recovery failed')
await logs.diagnostic('bootstrap', 'deployment.previous-plan.failed', { error: planningError })
}
}
if (previousPlan === undefined) await deployments.publishStatus({
recoveryMode: {
reason: error instanceof Error ? error.message : 'Deployment resolution failed',
failedRecordId: null,
},
})
}
await linkSystemPluginScope({
dshHome: process.env.DSH_HOME ?? '/data/dsh',
viewRoot: join(paths.viewsRoot, 'system-plugins'),
})
const systemPluginSelections = new SystemPluginSelectionStore(join(paths.deploymentStateRoot, 'system-plugins.json'))
await mkdir(paths.systemSkillCatalogsRoot, { recursive: true })
const legacySystemSkillRoot = () => retainLegacySystemSkillCatalog({
sourceRoot: join(seedRoot, 'bootstrap', inventory.bootstrap.id, 'control-plane', 'skills'),
outputRoot: paths.systemSkillCatalogsRoot,
})
const selectedSystemSkillRoot = async () => {
const selected = await deployments.selected()
if (selected === null) throw new Error('no Deployment is selected')
return await materializeSystemSkillCatalog({
environmentRoot: selected.paths.environment,
outputRoot: paths.systemSkillCatalogsRoot,
}) ?? await legacySystemSkillRoot()
}
const systemSkillManager = new SystemSkillManager({
sourceRoot: await selectedSystemSkillRoot(),
viewRoot: paths.systemSkillsView,
statePath: paths.systemSkillStatePath,
})
await systemSkillManager.initialize()
const systemSkills = {
list: () => systemSkillManager.list(),
configure: (skillId, action) => deployments.exclusive(() => systemSkillManager.configure(skillId, action)),
}
const applySystemPluginSelection = async () => {
const selected = await deployments.selected()
if (selected === null) return []
const materialized = await materializeSystemPluginSelection({
environmentRoot: selected.paths.environment,
sourceRoot: selected.paths['system-plugins'],
outputRoot: paths.systemPluginViewsRoot,
selectionStore: systemPluginSelections,
})
await replaceSystemPluginView(paths, materialized.path)
await pruneSystemPluginSelectionViews({ outputRoot: paths.systemPluginViewsRoot, keepPath: materialized.path })
await logs.diagnostic('bootstrap', 'system-plugin.view.applied', {
pluginCount: materialized.plugins.length,
})
return materialized.plugins
}
const applyDeploymentResources = async () => {
await applySystemPluginSelection()
const skills = await systemSkillManager.useSourceRoot(await selectedSystemSkillRoot())
await logs.diagnostic('bootstrap', 'system-skill.view.applied', { skillCount: skills.length })
}
const prepareSystemPluginSelection = async () => {
const previous = await realpath(join(paths.systemPluginViewsRoot, 'current'))
const selected = await deployments.selected()
if (selected === null) throw new Error('no Deployment is selected')
const materialized = await materializeSystemPluginSelection({
environmentRoot: selected.paths.environment,
sourceRoot: selected.paths['system-plugins'],
outputRoot: paths.systemPluginViewsRoot,
selectionStore: systemPluginSelections,
})
await logs.diagnostic('bootstrap', 'system-plugin.view.prepared', {
pluginCount: materialized.plugins.length,
})
return Object.freeze({
activate: async () => {
await replaceSystemPluginView(paths, materialized.path)
await logs.diagnostic('bootstrap', 'system-plugin.view.activated')
},
commit: async () => {
await pruneSystemPluginSelectionViews({
outputRoot: paths.systemPluginViewsRoot,
keepPath: materialized.path,
})
await logs.diagnostic('bootstrap', 'system-plugin.view.committed')
},
rollback: async () => {
await replaceSystemPluginView(paths, previous)
await pruneSystemPluginSelectionViews({ outputRoot: paths.systemPluginViewsRoot, keepPath: previous })
await logs.diagnostic('bootstrap', 'system-plugin.view.rolled-back', { level: 'warning' })
},
})
}
const capture = (child, source, declaration) => logs.capture(
child,
source,
declaration,
{ acceptForwarded: ['gateway', 'platform-management'].includes(source) },
)
const proxyLaunchToken = process.env.DSH_PROXY_LAUNCH_TOKEN
delete process.env.DSH_PROXY_LAUNCH_TOKEN
const accessLaunchToken = process.env.DSH_ACCESS_LAUNCH_TOKEN
delete process.env.DSH_ACCESS_LAUNCH_TOKEN
const reportLifecycle = (message, fields) => logs.diagnostic(fields.componentId ?? 'bootstrap', message, fields)
const managedNetworkEnvironment = async scope => {
try {
const result = await outboundProxy.request('GET', `/v1/environment?scope=${encodeURIComponent(scope)}`)
return clearOutboundProxyEnvironment(parseOutboundProxyEnvironment(result.environment, scope))
} catch (error) {
await logs.diagnostic('bootstrap', 'outbound-proxy.environment.fallback', {
scope,
error,
level: 'warning',
})
try {
const state = JSON.parse(await readFile(paths.proxyRoutingStatePath, 'utf8'))
return outboundProxyEnvironment(scope, { enabled: outboundProxyScopeEnabled(state, scope) })
} catch {
return outboundProxyEnvironment(scope)
}
}
}
let runtime
const dshLifecycleBroker = new DshLifecycleBroker({
report: reportLifecycle,
shouldTerminate: async () => {
const lifecycle = runtime?.status().dshLifecycle
return ['stopping', 'restarting'].includes(lifecycle?.state)
|| environment?.stopping === true
|| await deployments.activation() !== undefined
},
})
const controlPlane = new EnvironmentRunner({
environmentRoot: join(import.meta.dirname, '..', '..', 'control-plane'),
loader: loadControlPlane,
capture,
prepareService: async component => {
if (component.id === 'access-manager') return {
environment: { DSH_ACCESS_LAUNCH_TOKEN: accessLaunchToken },
release: () => {},
}
if (component.id === 'outbound-proxy') return {
environment: { DSH_PROXY_LAUNCH_TOKEN: proxyLaunchToken },
release: () => {},
}
const environment = component.id === 'gateway'
? outboundProxyEnvironment('platform', { enabled: false })
: await managedNetworkEnvironment('platform')
return { environment: { ...environment, NODE_USE_ENV_PROXY: '1' }, release: () => {} }
},
recoverableComponents: ['access-manager', 'outbound-proxy'],
report: reportLifecycle,
startupGroups: [
['gateway', 'access-manager', 'outbound-proxy'],
['platform-recovery'],
['platform-management'],
],
})
const environment = new EnvironmentRunner({
environmentRoot: join(paths.viewsRoot, 'environment'),
capture,
prepareService: async component => {
const launch = dshLifecycleBroker.prepareLaunch(component.id)
if (component.id !== 'dsh-runtime') return launch
const environment = await managedNetworkEnvironment('sharedDsh')
return {
...launch,
environment: {
...launch.environment,
...environment,
NODE_USE_ENV_PROXY: '1',
DSH_PLATFORM_AGENT_PROXY_URL: outboundProxyUrl('agentNetwork'),
},
}
},
report: reportLifecycle,
})
runtime = new BootstrapRuntime({
controlPlane,
environment,
validateDeployment: async () => {
await verifyRuntimePatches({
runtimeRoot: join(paths.viewsRoot, 'runtime'),
environmentRoot: join(paths.viewsRoot, 'environment'),
})
},
prepareDeployment: applyDeploymentResources,
beforeReady: async () => {
await access.request('POST', '/v1/classify', { token: accessLaunchToken, evidence: accessEvidence })
await logs.diagnostic('bootstrap', 'access.classification.submitted')
},
onEnvironmentVerified: async () => dshLifecycleBroker.verify(),
onDshAvailable: async () => dshLifecycleBroker.publish(),
ownsDshLifecycle: async () => await deployments.activation() !== undefined,
onDshRecovered: async () => {
await deployments.publishStatus({ recoveryMode: null })
await logs.diagnostic('bootstrap', 'dsh.recovery-mode.cleared')
},
report: reportLifecycle,
onEnvironmentFatal: async error => {
const state = await deployments.state().catch(() => ({ current: null }))
const reason = error instanceof Error ? error.message : String(error)
await deployments.publishStatus({
recoveryMode: { reason, failedRecordId: state.current },
})
await logs.diagnostic('bootstrap', 'environment.recovery-mode.entered', { error, failedRecordId: state.current })
},
})
const systemPlugins = {
list: async () => {
const deploymentRoot = await realpath(paths.deploymentView).catch(error => (
error?.code === 'ENOENT' ? undefined : Promise.reject(error)
))
if (deploymentRoot === undefined) return []
const activeRoot = await realpath(join(paths.systemPluginViewsRoot, 'current')).catch(error => (
error?.code === 'ENOENT' ? undefined : Promise.reject(error)
))
return listManagedSystemPlugins({
environmentRoot: join(deploymentRoot, 'environment'),
sourceRoot: join(deploymentRoot, 'system-plugins'),
selectionStore: systemPluginSelections,
activeRoot,
})
},
mutate: (pluginId, action, recovery = false) => deployments.exclusive(async () => {
const resolved = await deployments.selected()
if (resolved === null) throw new Error('no Deployment is selected')
const before = await systemPlugins.list()
const pluginIds = before.map(plugin => plugin.id)
const previousSelection = await systemPluginSelections.read(pluginIds)
try {
await systemPluginSelections[recovery ? 'recover' : 'configure'](pluginIds, pluginId, action)
return systemPlugins.list()
} catch (error) {
try {
await systemPluginSelections.write(pluginIds, previousSelection)
} catch (rollbackError) {
throw new AggregateError([error, rollbackError], 'System Plugin operation and rollback failed')
}
throw error
}
}),
discard: () => deployments.exclusive(async () => {
const resolved = await deployments.selected()
if (resolved === null) throw new Error('no Deployment is selected')
const activeRoot = await realpath(join(paths.systemPluginViewsRoot, 'current'))
return discardSystemPluginSelection({
environmentRoot: resolved.paths.environment,
sourceRoot: resolved.paths['system-plugins'],
selectionStore: systemPluginSelections,
activeRoot,
})
}),
prepare: prepareSystemPluginSelection,
}
systemPlugins.configure = (pluginId, action) => systemPlugins.mutate(pluginId, action)
systemPlugins.recover = (pluginId, action) => systemPlugins.mutate(pluginId, action, true)
const server = createBootstrapControl(runtime, { deployments, trust, systemPlugins, systemSkills, snapshots })
const dshLifecycleServer = createDshLifecycleServer(dshLifecycleBroker)
await listenBootstrapControl(server, paths.bootstrapSocket)
await listenDshLifecycle(dshLifecycleServer, paths.dshLifecycleSocket)
let imageCandidateHealthy = true
try {
const recoveryStartedAt = Date.now()
const updateRecovery = await recoverPlatformUpdateBeforeDshStart({ dataRoot, runRoot, dshHome })
const userPluginRecovery = await recoverUserPluginBeforeDshStart({
journal: new UserPluginJournal(paths.userPluginJournalPath),
report: (message, fields) => logs.diagnostic('user-plugin-manager', message, fields),
selectionStore: new UserPluginSelectionStore(paths.userPluginStatePath),
snapshots: new SnapshotClient(new LocalApiClient(paths.snapshotSocket), 'user-plugin'),
})
await logs.diagnostic('bootstrap', 'startup-recovery.completed', {
elapsedMs: Date.now() - recoveryStartedAt,
updatePhase: updateRecovery?.phase ?? null,
userPluginPhase: userPluginRecovery?.phase ?? null,
})
await runtime.start({
allowRecovery: true,
onEnvironmentFailure: async () => {
imageCandidateHealthy = false
if (previousPlan !== undefined) return false
if (planningError !== null) return false
if (imagePlan?.fallback !== null && imagePlan?.fallback !== undefined) {
return deployments.rejectImage(imagePlan)
}
try {
previousPlan = await deployments.preparePreviousRecovery(imagePlan?.target)
return true
} catch (error) {
await logs.diagnostic('bootstrap', 'deployment.previous-plan.failed', { error })
return false
}
},
})
} catch (error) {
await logs.diagnostic('bootstrap', 'bootstrap.start.failed', { error })
server.close()
dshLifecycleServer.close()
throw error
}
if (runtime.recoveryMode === null) {
if (imageCandidateHealthy && imagePlan !== undefined) await deployments.acceptImage(imagePlan)
else if (previousPlan !== undefined) {
try {
await deployments.acceptPreviousRecovery(previousPlan, tokens => trust.activate(tokens))
planningError = null
} catch (error) {
await runtime.suspend('dsh-runtime').catch(suspendError => logs.diagnostic('bootstrap', 'dsh-runtime.suspend.failed', {
error: suspendError,
cause: error instanceof Error ? error.message : String(error),
}))
await logs.diagnostic('bootstrap', 'deployment.previous-activation.failed', { error })
runtime.enterRecovery(error)
}
}
}
const recoveryReason = runtime.recoveryMode ?? (previousPlan === undefined && planningError instanceof Error
? planningError.message
: null)
const recoveryMode = recoveryReason === null ? null : {
reason: recoveryReason,
failedRecordId: imagePlan?.target ?? null,
}
await deployments.publishStatus({ plan: imagePlan, recoveryMode })
runtime.markStartupComplete()
if (recoveryMode === null) await dshLifecycleBroker.publish()
await logs.diagnostic('bootstrap', 'platform.ready', { recoveryMode: recoveryMode !== null })
process.send?.({ type: 'ready', bootstrapApi: 1 })
const onRecoveryMessage = message => {
if (message?.type !== 'recover-image-baseline' || typeof message.requestId !== 'string') return
void deployments.recoverImageBaseline(imageRecords.deployment, {
healthCheck: () => runtime.reload(),
activateReceipts: tokens => trust.activate(tokens),
}).then(
async slots => {
await logs.diagnostic('bootstrap', 'image-baseline.recovery.completed', { requestId: message.requestId })
process.send?.({ type: 'recovery-result', requestId: message.requestId, slots })
},
async error => {
await logs.diagnostic('bootstrap', 'image-baseline.recovery.failed', { error, requestId: message.requestId })
process.send?.({
type: 'recovery-result', requestId: message.requestId,
error: error instanceof Error ? error.message : 'image baseline recovery failed',
})
},
)
}
process.on('message', onRecoveryMessage)
let resolveSignal
const signal = new Promise(resolve => { resolveSignal = resolve })
const onSignal = () => resolveSignal({ type: 'signal' })
process.once('SIGINT', onSignal)
process.once('SIGTERM', onSignal)
const outcome = await Promise.race([
signal,
runtime.fatal.then(error => ({ type: 'fatal', error })),
])
if (outcome.type === 'fatal') await logs.diagnostic('bootstrap', 'bootstrap.fatal', { error: outcome.error })
else await logs.diagnostic('bootstrap', 'bootstrap.stopping')
dshLifecycleBroker.beginShutdown()
server.close()
dshLifecycleServer.close()
await runtime.stop().catch(error => logs.diagnostic('bootstrap', 'bootstrap.stop.failed', { error }))
await logs.diagnostic('bootstrap', 'bootstrap.stopped')
process.off('message', onRecoveryMessage)
if (process.connected) process.disconnect()
if (outcome.type === 'fatal') throw outcome.error
@@ -0,0 +1,170 @@
import { createServer } from 'node:http'
import { chmod, mkdir, rm } from 'node:fs/promises'
import { dirname } from 'node:path'
import { PlatformGarbageCollector } from './gc.mjs'
async function jsonBody(request) {
const chunks = []
let size = 0
for await (const chunk of request) {
size += chunk.byteLength
if (size > 256 * 1024) throw new Error('Bootstrap request body is too large')
chunks.push(chunk)
}
return size === 0 ? {} : JSON.parse(Buffer.concat(chunks).toString('utf8'))
}
function send(response, status, value) {
response.writeHead(status, { 'content-type': 'application/json; charset=utf-8' })
response.end(`${JSON.stringify(value)}\n`)
}
export function createBootstrapControl(runner, { deployments, trust, systemPlugins, systemSkills, snapshots } = {}) {
return createServer(async (request, response) => {
let pathname = 'invalid-url'
try {
pathname = new URL(request.url ?? '/', 'http://bootstrap.internal').pathname
if (request.method === 'GET' && pathname === '/v1/status') send(response, 200, {
...runner.status(),
bootstrapVersion: process.env.DSH_BOOTSTRAP_VERSION ?? '1.0.0',
})
else if (request.method === 'POST' && pathname === '/v1/reload') send(response, 200, await runner.reload())
else if (request.method === 'GET' && pathname === '/v1/health') send(response, 200, await runner.health())
else if (request.method === 'GET' && pathname === '/v1/system-plugins' && systemPlugins !== undefined) {
send(response, 200, { plugins: await systemPlugins.list() })
} else if (request.method === 'POST' && pathname === '/v1/system-plugins/action' && systemPlugins !== undefined) {
const body = await jsonBody(request)
send(response, 200, { plugins: await systemPlugins.configure(body.id, body.action) })
} else if (request.method === 'POST' && pathname === '/v1/system-plugins/recovery-action' && systemPlugins !== undefined) {
const body = await jsonBody(request)
send(response, 200, { plugins: await systemPlugins.recover(body.id, body.action) })
} else if (request.method === 'POST' && pathname === '/v1/system-plugins/discard' && systemPlugins !== undefined) {
send(response, 200, { plugins: await systemPlugins.discard() })
} else if (request.method === 'GET' && pathname === '/v1/system-skills' && systemSkills !== undefined) {
send(response, 200, { skills: await systemSkills.list() })
} else if (request.method === 'POST' && pathname === '/v1/system-skills/action' && systemSkills !== undefined) {
const body = await jsonBody(request)
send(response, 200, { skills: await systemSkills.configure(body.skillId, body.action) })
}
else if (request.method === 'GET' && pathname === '/v1/deployments/current' && deployments !== undefined) {
const state = await deployments.state()
send(response, 200, { record: state.current === null ? null : await deployments.record(state.current) })
} else if (request.method === 'POST' && pathname === '/v1/deployments/activate' && deployments !== undefined && trust !== undefined) {
const body = await jsonBody(request)
const slots = await deployments.activateManaged(body.record, {
healthCheck: () => runner.reload(),
activateReceipts: tokens => trust.activate(tokens),
})
send(response, 200, { slots })
} else if (request.method === 'POST' && pathname === '/v1/deployments/rollback' && deployments !== undefined && trust !== undefined) {
const body = await jsonBody(request)
const state = await deployments.state()
if (state.previous === null) throw new Error('no previous Deployment exists')
if (body.recordId !== undefined && body.recordId !== state.previous) throw new Error('requested Deployment is not previous')
const slots = await deployments.rollback({
healthCheck: () => runner.reload(),
activateReceipts: tokens => trust.activate(tokens),
})
send(response, 200, { slots })
} else if (request.method === 'GET' && pathname === '/v1/deployments/rollback-plan' && deployments !== undefined) {
send(response, 200, await deployments.rollbackPlan())
} else if (request.method === 'POST' && pathname === '/v1/deployments/candidate' && deployments !== undefined) {
const body = await jsonBody(request)
const record = await deployments.stageCandidate(body.record, () => runner.reload())
send(response, 200, { recordId: record.id })
} else if (request.method === 'POST' && pathname === '/v1/deployments/candidate/commit' && deployments !== undefined && trust !== undefined) {
const body = await jsonBody(request)
const slots = await deployments.commitCandidate(body.recordId, tokens => trust.activate(tokens))
send(response, 200, { slots })
} else if (request.method === 'POST' && pathname === '/v1/deployments/candidate/cancel' && deployments !== undefined) {
send(response, 200, await deployments.cancelCandidate())
} else if (request.method === 'POST' && pathname === '/v1/platform/gc' && deployments !== undefined) {
send(response, 200, await new PlatformGarbageCollector({ paths: deployments.paths, deployments, snapshots }).collect())
} else if (request.method === 'POST' && pathname === '/v1/deployments/runtime/reset' && deployments !== undefined) {
send(response, 200, await deployments.resetCurrentRuntime({
pauseDsh: () => runner.pause('dsh-runtime'),
restartDsh: () => runner.restart('dsh-runtime'),
}))
}
else {
const operation = /^\/v1\/components\/([a-z0-9][a-z0-9._-]{0,127})\/(pause|suspend|resume|restart)$/.exec(pathname)
if (request.method === 'POST' && operation !== null) {
const [componentId, action] = operation.slice(1)
if (componentId === 'dsh-runtime' && action === 'resume' && deployments !== undefined) {
const status = await deployments.exclusive(async () => {
await deployments.setOperation('starting')
try {
const value = await runner.resume(componentId)
await deployments.publishStatus()
return value
} catch (error) {
await deployments.publishStatus({
recoveryMode: runner.status().recoveryMode
?? (error instanceof Error ? error.message : String(error)),
})
throw error
}
})
send(response, 200, status)
} else if (componentId === 'dsh-runtime' && action === 'restart' && deployments !== undefined) {
const status = await deployments.exclusive(async () => {
await deployments.setOperation('restarting')
let transaction
try {
transaction = await systemPlugins?.prepare?.()
const value = await runner.restart(componentId, {
skipPreparation: true,
recoverStart: true,
beforeStart: () => transaction?.activate?.(),
onStartFailure: () => transaction?.rollback?.(),
})
if (transaction?.commit !== undefined) {
try {
await transaction.commit()
} catch (error) {
await runner.record?.('system-plugin.view.commit.failed', { error, level: 'warning' })
}
}
await deployments.publishStatus()
return value
} catch (error) {
try {
await deployments.publishStatus({
operation: 'restart-failed',
recoveryMode: runner.status().recoveryMode
?? (error instanceof Error ? error.message : String(error)),
})
} catch (statusError) {
await runner.record?.('deployment.status.failed', {
error: statusError,
level: 'warning',
originalError: error instanceof Error ? error.message : String(error),
})
}
throw error
}
})
send(response, 200, status)
} else send(response, 200, await runner[action](componentId))
} else send(response, 404, { error: 'not found' })
}
} catch (error) {
await runner.record?.('bootstrap.request.failed', {
error,
method: request.method ?? null,
pathname,
})
send(response, 500, { error: error instanceof Error ? error.message : 'Bootstrap operation failed' })
}
})
}
export async function listenBootstrapControl(server, socketPath) {
await mkdir(dirname(socketPath), { recursive: true })
await rm(socketPath, { force: true })
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(socketPath, resolve)
})
await chmod(socketPath, 0o600)
}
@@ -0,0 +1,808 @@
import { lstat, mkdir, readFile, rename, rm, symlink } from 'node:fs/promises'
import { join, resolve } from 'node:path'
import { randomUUID } from 'node:crypto'
import { canonicalJson } from '../../lib/canonical-json.mjs'
import {
deriveRecordId,
parseDeploymentRecord,
parseSlots,
recordsFromImageInventory,
} from '../../lib/deployment-contracts.mjs'
import { durableCreate, durableReplace } from '../../lib/atomic.mjs'
import { readDeploymentStatus, writeDeploymentStatus } from '../../lib/deployment-status.mjs'
import { replaceDeploymentView, replaceSystemPluginView } from '../../lib/paths.mjs'
import { hashTree, hashTreeContent } from '../../lib/tree-hash.mjs'
import { compareDshVersions } from '../../lib/supported-target.mjs'
import { TrustError } from '../../lib/validation.mjs'
import { buildRuntime, cloneTree, loadEnvironmentPatchSet } from '../../../control-plane/modules/patch-manager/index.mjs'
const KINDS = Object.freeze(['environment', 'pristine', 'runtime', 'system-plugins'])
export class DeploymentResolutionError extends Error {
constructor(message, cause) {
super(message, { cause })
this.name = 'DeploymentResolutionError'
}
}
function sameDeployment(left, right) {
return left.dshVersion === right.dshVersion
&& left.environmentVersion === right.environmentVersion
&& KINDS.every(kind => {
const field = kind === 'system-plugins' ? 'systemPlugins' : kind
return left[field].sha256 === right[field].sha256
})
}
function validateCandidate(from, candidate, experimental) {
if (experimental) {
if (candidate.authority !== 'experimental') throw new TrustError('probation candidate must use Experimental authority')
if (candidate.targetSequence !== from.targetSequence) throw new TrustError('Experimental candidate must retain the Stable targetSequence')
if (compareDshVersions(candidate.dshVersion, from.dshVersion) <= 0) {
throw new TrustError('Experimental candidate DSH must advance the current version')
}
if (
candidate.environment.sha256 !== from.environment.sha256
|| candidate.systemPlugins.sha256 !== from.systemPlugins.sha256
) throw new TrustError('Experimental candidate must retain the current Environment and System Plugins')
return
}
if (candidate.authority !== 'stable') throw new TrustError('managed release activation requires Stable authority')
if (candidate.targetSequence < from.targetSequence) throw new TrustError('Stable candidate targetSequence would roll back')
if (from.authority === 'stable' && candidate.targetSequence === from.targetSequence && !sameDeployment(from, candidate)) {
throw new TrustError('Stable candidate conflicts with current content at the same targetSequence')
}
if (from.authority === 'experimental' && compareDshVersions(candidate.dshVersion, from.dshVersion) < 0) {
throw new TrustError('Stable candidate has not caught up with Experimental DSH')
}
}
async function readOptional(path) {
try { return await readFile(path) } catch (error) {
if (error?.code === 'ENOENT') return undefined
throw error
}
}
async function exists(path) {
return lstat(path).then(() => true, error => error?.code === 'ENOENT' ? false : Promise.reject(error))
}
async function replaceRuntimeDirectory(staging, destination) {
const backup = `${destination}.${randomUUID()}.reset-backup`
const hadDestination = await exists(destination)
if (hadDestination) await rename(destination, backup)
try {
await rename(staging, destination)
} catch (error) {
if (hadDestination) await rename(backup, destination).catch(() => {})
throw error
}
return Object.freeze({
commit: () => hadDestination ? rm(backup, { recursive: true, force: true }) : Promise.resolve(),
rollback: async () => {
await rm(destination, { recursive: true, force: true })
if (hadDestination) await rename(backup, destination)
},
})
}
export class DeploymentManager {
constructor({ paths, seedRoot, inventory }) {
this.paths = paths
this.seedRoot = resolve(seedRoot)
this.inventory = inventory
this.imageRecord = recordsFromImageInventory(inventory).deployment
this.stateRoot = paths.deploymentStateRoot
this.recordsRoot = join(this.stateRoot, 'records')
this.slotsPath = join(this.stateRoot, 'slots.json')
this.activationPath = join(this.stateRoot, 'activation.json')
this.queue = Promise.resolve()
}
exclusive(operation) {
const result = this.queue.then(operation, operation)
this.queue = result.then(() => undefined, () => undefined)
return result
}
recordPath(id) {
return join(this.recordsRoot, `${id}.json`)
}
async writeRecord(value) {
const record = parseDeploymentRecord(value)
const bytes = canonicalJson(record)
const path = this.recordPath(record.id)
const existing = await readOptional(path)
if (existing === undefined) await durableCreate(path, bytes)
else if (!existing.equals(bytes)) throw new TrustError('Deployment Record ID identifies conflicting content')
return record
}
async repairImageRecord(value) {
const record = parseDeploymentRecord(value)
const bytes = canonicalJson(record)
if (!bytes.equals(canonicalJson(this.imageRecord))) {
throw new TrustError('image recovery Record differs from the current image inventory')
}
await durableReplace(this.recordPath(record.id), bytes)
return record
}
async record(id) {
const bytes = await readOptional(this.recordPath(id))
if (bytes === undefined) throw new TrustError(`Deployment Record ${id} does not exist`)
return parseDeploymentRecord(bytes)
}
async state() {
const bytes = await readOptional(this.slotsPath)
if (bytes === undefined) return Object.freeze({ generation: 0, current: null, previous: null })
return parseSlots(bytes, 'deployment-record', 'Deployment slots')
}
availableInCurrentImage(record) {
return KINDS.every(kind => {
const reference = record[kind === 'system-plugins' ? 'systemPlugins' : kind]
return reference.storage !== 'image' || reference.imageBuildId === this.inventory.imageBuildId
})
}
imageRecordFromAnotherImage(record) {
return KINDS.every(kind => {
const reference = record[kind === 'system-plugins' ? 'systemPlugins' : kind]
return reference.storage === 'image' && reference.imageBuildId !== this.inventory.imageBuildId
})
}
async rollbackPlan() {
const state = await this.state()
const current = state.current === null ? null : await this.record(state.current)
let previous = state.previous === null ? null : await this.record(state.previous)
if (previous !== null && !this.availableInCurrentImage(previous)) previous = null
return Object.freeze({ current, previous })
}
async commit(current, previous) {
const state = await this.state()
if (current === previous) previous = null
const slots = parseSlots({
schema: 1,
generation: state.generation + 1,
current,
previous,
}, 'deployment-record', 'Deployment slots')
await durableReplace(this.slotsPath, canonicalJson(slots))
return slots
}
imageAsset(reference) {
if (reference.imageBuildId !== this.inventory.imageBuildId) {
throw new TrustError('Deployment Image Reference belongs to a different image')
}
const expected = reference.kind === 'system-plugins'
? this.inventory.deployment.systemPlugins
: this.inventory.deployment[reference.kind]
if (expected === undefined || reference.id !== expected.id || reference.sha256 !== expected.sha256) {
throw new TrustError(`Deployment Image Reference for ${reference.kind} is absent from inventory`)
}
const roots = {
environment: 'environments',
pristine: 'pristine',
runtime: 'runtimes',
'system-plugins': 'system-plugins',
}
return join(this.seedRoot, roots[reference.kind], reference.id)
}
storeAsset(reference) {
const roots = {
environment: this.paths.environmentsRoot,
pristine: this.paths.pristineRoot,
runtime: this.paths.runtimesRoot,
'system-plugins': this.paths.systemPluginsRoot,
}
return join(roots[reference.kind], reference.id)
}
async resolveReference(reference) {
const path = reference.storage === 'image' ? this.imageAsset(reference) : this.storeAsset(reference)
const details = await lstat(path)
if (!details.isDirectory() || details.isSymbolicLink()) {
throw new TrustError(`resolved ${reference.kind} must be an immutable directory`)
}
// Image bytes are bound to the signed inventory during image assembly. Rehashing a large
// Runtime on every boot would defeat zero-copy startup; managed objects are checked here.
if (reference.storage === 'store' && await hashTree(path) !== reference.sha256) {
throw new TrustError(`resolved ${reference.kind} content hash differs from its Record`)
}
return path
}
async resolveRecord(recordId) {
const record = await this.record(recordId)
const entries = await Promise.all(KINDS.map(async kind => [kind, await this.resolveReference(record[kind === 'system-plugins' ? 'systemPlugins' : kind])]))
return Object.freeze({ record, paths: Object.freeze(Object.fromEntries(entries)) })
}
async sameImageDeployment(current, image) {
if (sameDeployment(current, image)) return true
if (
current.dshVersion !== image.dshVersion
|| current.environmentVersion !== image.environmentVersion
|| current.pristine.sha256 !== image.pristine.sha256
|| current.runtime.sha256 !== image.runtime.sha256
|| current.systemPlugins.sha256 !== image.systemPlugins.sha256
) return false
const [currentEnvironment, imageEnvironment] = await Promise.all([
this.resolveReference(current.environment),
this.resolveReference(image.environment),
])
const [currentContent, imageContent] = await Promise.all([
hashTreeContent(currentEnvironment),
hashTreeContent(imageEnvironment),
])
return currentContent === imageContent
}
async selected() {
const recordId = this.selectedRecordId ?? (await this.state()).current
return recordId === null ? null : this.resolveRecord(recordId)
}
async prepareView(recordId) {
const resolved = await this.resolveRecord(recordId)
const destination = join(this.paths.deploymentViewsRoot, recordId)
const temporary = join(this.paths.deploymentViewsRoot, `.${recordId}.${randomUUID()}.tmp`)
await mkdir(temporary, { recursive: false })
try {
for (const kind of KINDS) await symlink(resolved.paths[kind], join(temporary, kind), 'dir')
try {
await rename(temporary, destination)
} catch (error) {
if (error?.code !== 'EEXIST' && error?.code !== 'ENOTEMPTY') throw error
}
} finally {
await rm(temporary, { recursive: true, force: true })
}
return Object.freeze({ ...resolved, view: destination })
}
async select(recordId) {
const candidate = await this.prepareView(recordId)
await replaceDeploymentView(this.paths, candidate.view)
await replaceSystemPluginView(this.paths)
this.selectedRecordId = recordId
return candidate
}
async initialize(record) {
const plan = await this.prepareImage(record)
return this.acceptImage(plan)
}
async prepareImage(record) {
return this.exclusive(async () => {
const image = await this.writeRecord(record)
const state = await this.state()
if (state.current === null) {
await this.select(image.id)
return Object.freeze({
baseGeneration: 0,
target: image.id,
fallback: null,
action: 'initialize',
imageBehindCurrent: false,
requiresExperimentalRebuild: false,
})
}
const current = await this.record(state.current)
let target = current.id
let action = 'retain'
let imageBehindCurrent = false
let requiresExperimentalRebuild = false
if (
image.authority === 'development'
&& image.targetSequence === 0
&& current.id !== image.id
&& compareDshVersions(image.dshVersion, current.dshVersion) >= 0
) {
target = image.id
action = 'development-refresh'
} else if (
image.targetSequence < current.targetSequence
&& current.receiptTokens.length === 0
&& this.imageRecordFromAnotherImage(current)
) {
target = image.id
action = 'image-rollback'
} else if (current.authority === 'experimental') {
const dshCaughtUp = compareDshVersions(image.dshVersion, current.dshVersion) >= 0
if (image.targetSequence >= current.targetSequence && dshCaughtUp) {
target = image.id
action = 'stable-caught-up'
} else {
imageBehindCurrent = image.targetSequence < current.targetSequence
requiresExperimentalRebuild = image.targetSequence > current.targetSequence && !dshCaughtUp
}
} else if (image.targetSequence > current.targetSequence) {
target = image.id
action = 'image-forward'
} else if (image.targetSequence < current.targetSequence) {
imageBehindCurrent = true
} else if (!await this.sameImageDeployment(current, image)) {
throw new TrustError('Deployment image conflicts with current content at the same targetSequence')
} else if (current.id !== image.id) {
target = image.id
action = 'prefer-image'
}
let candidateError = null
try {
await this.select(target)
} catch (error) {
if (target === current.id) {
throw new DeploymentResolutionError('current Deployment assets could not be resolved', error)
}
try {
await this.select(current.id)
} catch (fallbackError) {
throw new DeploymentResolutionError(
'image candidate and current Deployment assets could not be resolved',
new AggregateError([error, fallbackError]),
)
}
target = current.id
action = 'image-rejected'
candidateError = error instanceof Error ? error.message : 'image candidate could not be resolved'
}
return Object.freeze({
baseGeneration: state.generation,
target,
fallback: target === current.id || action === 'development-refresh' ? null : current.id,
action,
imageBehindCurrent,
requiresExperimentalRebuild,
candidateError,
})
})
}
async acceptImage(plan) {
return this.exclusive(async () => {
const state = await this.state()
if (state.generation !== plan.baseGeneration) throw new TrustError('Deployment slots changed during image startup')
if (state.current === plan.target) return state
return this.commit(plan.target, plan.action === 'development-refresh' ? null : state.current)
})
}
async rejectImage(plan) {
return this.exclusive(async () => {
const state = await this.state()
if (state.generation !== plan.baseGeneration) throw new TrustError('Deployment slots changed during image startup')
if (plan.fallback === null) return false
await this.select(plan.fallback)
return true
})
}
async preparePreviousRecovery(failedRecordId) {
return this.exclusive(async () => {
const state = await this.state()
const failed = failedRecordId ?? state.current
if (failed === null || state.current !== failed) {
throw new TrustError('failed Deployment is no longer current')
}
if (state.previous === null || state.previous === failed) {
throw new TrustError('no previous Deployment exists')
}
await this.record(failed)
await this.select(state.previous)
return Object.freeze({
baseGeneration: state.generation,
failed,
target: state.previous,
})
})
}
async acceptPreviousRecovery(plan, activateReceipts) {
return this.exclusive(async () => {
const state = await this.state()
if (state.generation !== plan.baseGeneration || state.current !== plan.failed || state.previous !== plan.target) {
throw new TrustError('Deployment slots changed during automatic recovery')
}
const previous = await this.record(plan.target)
await activateReceipts(previous.receiptTokens)
await this.writeActivation({ phase: 'receipts-active', from: plan.failed, to: plan.target })
const committed = await this.commit(plan.target, plan.failed)
await this.clearActivation()
return committed
})
}
async activate(recordId, healthCheck) {
return this.exclusive(async () => {
await this.record(recordId)
const state = await this.state()
if (state.current === recordId) {
await this.select(recordId)
return state
}
await this.select(recordId)
try {
await healthCheck()
} catch (error) {
if (state.current !== null) {
await this.select(state.current)
await healthCheck().catch(() => {})
}
throw error
}
return this.commit(recordId, state.current)
})
}
async rollback({ healthCheck, activateReceipts }) {
return this.exclusive(async () => {
const state = await this.state()
if (state.previous === null) throw new TrustError('no previous Deployment exists')
const from = await this.record(state.current)
const previous = await this.record(state.previous)
await this.writeActivation({ phase: 'prepared', from: from.id, to: previous.id })
await this.publishStatus({ operation: 'recovering' })
let receiptsActive = false
try {
await this.select(previous.id)
await healthCheck()
await this.writeActivation({ phase: 'healthy', from: from.id, to: previous.id })
await activateReceipts(previous.receiptTokens)
receiptsActive = true
await this.writeActivation({ phase: 'receipts-active', from: from.id, to: previous.id })
const committed = await this.commit(previous.id, from.id)
await this.clearActivation()
await this.publishStatus()
return committed
} catch (error) {
if (receiptsActive) await activateReceipts(from.receiptTokens).catch(() => {})
await this.select(from.id).catch(() => {})
await healthCheck().catch(() => {})
await this.clearActivation().catch(() => {})
await this.publishStatus().catch(() => {})
throw error
}
})
}
async materializeRecord(recordId) {
const record = await this.record(recordId)
const fields = {
environment: 'environment',
pristine: 'pristine',
runtime: 'runtime',
systemPlugins: 'system-plugins',
}
const references = {}
for (const [field, kind] of Object.entries(fields)) {
const reference = record[field]
if (reference.storage === 'store') {
references[field] = reference
continue
}
const source = await this.resolveReference(reference)
const id = `image-${kind}-${reference.sha256}`
const destination = this.storeAsset({ kind, id })
if (!await exists(destination)) {
const temporary = `${destination}.${randomUUID()}.tmp`
try {
await cloneTree(source, temporary)
if (await hashTree(temporary) !== reference.sha256) throw new TrustError(`materialized ${kind} differs from Image Reference`)
await rename(temporary, destination)
} finally {
await rm(temporary, { recursive: true, force: true })
}
} else if (await hashTree(destination) !== reference.sha256) {
throw new TrustError(`materialized ${kind} conflicts with Managed Store`)
}
references[field] = { storage: 'store', kind, id, sha256: reference.sha256 }
}
const content = {
schema: 1,
authority: record.authority,
targetSequence: record.targetSequence,
dshVersion: record.dshVersion,
environmentVersion: record.environmentVersion,
...references,
receiptTokens: [...record.receiptTokens],
snapshotId: record.snapshotId,
}
return this.writeRecord({ ...content, id: deriveRecordId('deployment-record', content) })
}
async materializeCurrent() {
return this.exclusive(async () => {
const state = await this.state()
if (state.current === null) throw new TrustError('no current Deployment exists')
const current = await this.record(state.current)
const references = [current.environment, current.pristine, current.runtime, current.systemPlugins]
if (references.every(reference => reference.storage === 'store')) return current
const materialized = await this.materializeRecord(current.id)
await this.select(materialized.id)
await this.commit(materialized.id, state.previous)
return materialized
})
}
async resetCurrentRuntime({ pauseDsh, restartDsh }) {
return this.exclusive(async () => {
const state = await this.state()
if (state.current === null) throw new TrustError('no current Deployment exists')
const current = await this.record(state.current)
const [pristineRoot, environmentRoot] = await Promise.all([
this.resolveReference(current.pristine),
this.resolveReference(current.environment),
])
const patchSet = await loadEnvironmentPatchSet(environmentRoot)
const stagingId = `runtime-reset-${randomUUID()}`
const versionsRoot = this.paths.runtimesRoot
const staging = join(versionsRoot, stagingId)
const destinationId = current.runtime.storage === 'store'
? current.runtime.id
: `reset-${current.runtime.sha256}`
const destination = join(this.paths.runtimesRoot, destinationId)
let replacement
let paused = false
await this.publishStatus({ operation: 'runtime-reset-building' })
try {
await buildRuntime({
pristineRoot,
versionsRoot,
runtimeId: stagingId,
patchPaths: patchSet.paths,
})
await this.publishStatus({ operation: 'runtime-reset-verifying' })
const sha256 = await hashTree(staging)
if (sha256 !== current.runtime.sha256) {
throw new TrustError('rebuilt Runtime differs from the current Deployment Record')
}
await this.publishStatus({ operation: 'runtime-reset-switching' })
await pauseDsh()
paused = true
replacement = await replaceRuntimeDirectory(staging, destination)
const runtime = Object.freeze({
storage: 'store', kind: 'runtime', id: destinationId, sha256,
})
const content = {
schema: 1,
authority: current.authority,
targetSequence: current.targetSequence,
dshVersion: current.dshVersion,
environmentVersion: current.environmentVersion,
environment: current.environment,
pristine: current.pristine,
runtime,
systemPlugins: current.systemPlugins,
receiptTokens: [...current.receiptTokens],
snapshotId: current.snapshotId,
}
const repaired = current.runtime.storage === 'store'
? current
: await this.writeRecord({ ...content, id: deriveRecordId('deployment-record', content) })
await this.select(repaired.id)
await this.publishStatus({ operation: 'runtime-reset-starting' })
await restartDsh()
const slots = repaired.id === current.id
? state
: await this.commit(repaired.id, state.previous)
await replacement.commit()
await this.publishStatus({ recoveryMode: null })
return Object.freeze({ recordId: repaired.id, slots })
} catch (error) {
if (replacement !== undefined) await replacement.rollback().catch(() => {})
if (paused) {
await this.publishStatus({ operation: 'runtime-reset-recovering' }).catch(() => {})
await this.select(current.id).catch(() => {})
await restartDsh().catch(() => {})
}
await this.publishStatus({ operation: 'runtime-reset-failed' }).catch(() => {})
throw error
} finally {
await rm(staging, { recursive: true, force: true })
}
})
}
async activation() {
const bytes = await readOptional(this.activationPath)
if (bytes === undefined) return undefined
const value = JSON.parse(bytes.toString('utf8'))
if (
value?.schema !== 1
|| !['prepared', 'healthy', 'probation', 'receipts-active'].includes(value.phase)
|| typeof value.from !== 'string'
|| typeof value.to !== 'string'
) throw new TrustError('Deployment activation journal is invalid')
return Object.freeze(value)
}
async writeActivation(value) {
const journal = Object.freeze({ schema: 1, ...value, updatedAt: new Date().toISOString() })
await durableReplace(this.activationPath, canonicalJson(journal))
return journal
}
async clearActivation() {
await rm(this.activationPath, { force: true })
}
async recoverActivation(trust) {
const journal = await this.activation()
if (journal === undefined) return null
const [from, to, active] = await Promise.all([
this.record(journal.from),
this.record(journal.to),
trust.activeReceipts(),
])
const activeTokens = new Set(active.receipts.map(receipt => receipt.token))
const candidateReceiptsActive = to.receiptTokens.every(token => activeTokens.has(token))
if (journal.phase === 'receipts-active' || (journal.phase === 'healthy' && candidateReceiptsActive)) {
await this.select(to.id)
const state = await this.state()
if (state.current !== to.id) await this.commit(to.id, from.id)
await this.clearActivation()
return Object.freeze({ action: 'committed', recordId: to.id })
}
await trust.activate(from.receiptTokens)
await this.select(from.id)
const state = await this.state()
if (state.current !== from.id) await this.commit(from.id, state.current)
await this.clearActivation()
return Object.freeze({ action: 'rolled-back', recordId: from.id })
}
async activateManaged(value, { healthCheck, activateReceipts }) {
const candidate = await this.writeRecord(value)
await this.materializeCurrent()
const state = await this.state()
const from = await this.record(state.current)
if (candidate.id === from.id) return state
validateCandidate(from, candidate, false)
await this.writeActivation({ phase: 'prepared', from: from.id, to: candidate.id })
await this.publishStatus({ operation: 'switching' })
let receiptsActive = false
try {
await this.select(candidate.id)
await healthCheck()
await this.writeActivation({ phase: 'healthy', from: from.id, to: candidate.id })
await activateReceipts(candidate.receiptTokens)
receiptsActive = true
await this.writeActivation({ phase: 'receipts-active', from: from.id, to: candidate.id })
const committed = await this.commit(candidate.id, from.id)
await this.clearActivation()
await this.publishStatus()
return committed
} catch (error) {
await this.publishStatus({ operation: 'recovering' }).catch(() => {})
if (receiptsActive) await activateReceipts(from.receiptTokens).catch(() => {})
await this.select(from.id).catch(() => {})
await healthCheck().catch(() => {})
await this.clearActivation().catch(() => {})
await this.publishStatus().catch(() => {})
throw error
}
}
async stageCandidate(value, healthCheck) {
const candidate = await this.writeRecord(value)
await this.materializeCurrent()
const state = await this.state()
const from = await this.record(state.current)
validateCandidate(from, candidate, true)
await this.writeActivation({ phase: 'prepared', from: from.id, to: candidate.id })
try {
await this.select(candidate.id)
await healthCheck()
await this.writeActivation({ phase: 'probation', from: from.id, to: candidate.id })
return candidate
} catch (error) {
await this.select(from.id).catch(() => {})
await this.clearActivation().catch(() => {})
throw error
}
}
async commitCandidate(recordId, activateReceipts) {
const journal = await this.activation()
if (journal?.phase !== 'probation' || journal.to !== recordId) {
throw new TrustError('Deployment candidate is not in probation')
}
const candidate = await this.record(recordId)
await activateReceipts(candidate.receiptTokens)
await this.writeActivation({ phase: 'receipts-active', from: journal.from, to: journal.to })
const committed = await this.commit(candidate.id, journal.from)
await this.clearActivation()
await this.publishStatus()
return committed
}
async cancelCandidate() {
const journal = await this.activation()
if (journal === undefined) return Object.freeze({ cancelled: false, slots: await this.state() })
const from = await this.record(journal.from)
await this.select(from.id)
await this.clearActivation()
await this.publishStatus()
return Object.freeze({ cancelled: true, slots: await this.state() })
}
async recoverImageBaseline(value, { healthCheck, activateReceipts }) {
const previousStatus = await readDeploymentStatus(this.paths.deploymentStatusPath)
await this.publishStatus({ operation: 'recovering', recoveryMode: previousStatus.recoveryMode })
const image = await this.repairImageRecord(value)
await this.prepareView(image.id)
const state = await this.state()
await this.select(image.id)
try {
await healthCheck()
await activateReceipts(image.receiptTokens)
const committed = state.current === image.id ? state : await this.commit(image.id, state.current)
await this.clearActivation()
await this.publishStatus({ recoveryMode: null })
return committed
} catch (error) {
if (state.current !== null) await this.select(state.current).catch(() => {})
await this.publishStatus({
recoveryMode: error instanceof Error ? error.message : 'image baseline recovery failed',
}).catch(() => {})
throw error
}
}
describe(record) {
const references = [record.environment, record.pristine, record.runtime, record.systemPlugins]
return Object.freeze({
recordId: record.id,
source: references.every(reference => reference.storage === 'image') ? 'image' : 'managed',
authority: record.authority,
targetSequence: record.targetSequence,
dsh: record.dshVersion,
environment: record.environmentVersion,
})
}
async publishStatus({ plan = null, recoveryMode = null, currentId, operation = null } = {}) {
const state = await this.state().catch(() => ({ current: null }))
const selectedId = currentId === undefined ? state.current : currentId
const current = selectedId === null
? null
: await this.record(selectedId).then(record => this.describe(record), () => null)
const value = Object.freeze({
platformLayout: 1,
imageBaseline: Object.freeze({
imageBuildId: this.inventory.imageBuildId,
targetSequence: this.inventory.targetSequence,
dsh: this.inventory.deployment.dshVersion,
environment: this.inventory.deployment.environmentVersion,
}),
current,
imageBehindCurrent: plan?.imageBehindCurrent
?? (current !== null && current.targetSequence > this.inventory.targetSequence),
recoveryMode,
operation,
})
return writeDeploymentStatus(this.paths.deploymentStatusPath, value)
}
async setOperation(operation) {
const status = await readDeploymentStatus(this.paths.deploymentStatusPath)
return writeDeploymentStatus(this.paths.deploymentStatusPath, Object.freeze({
...status,
operation,
}))
}
}
@@ -0,0 +1,206 @@
import { randomBytes, randomUUID, timingSafeEqual } from 'node:crypto'
import { chmod, mkdir, rm } from 'node:fs/promises'
import { createServer } from 'node:http'
import { dirname } from 'node:path'
const MAX_BODY_BYTES = 4 * 1024
function exactObject(value, keys, name) {
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
throw new Error(`${name} must be an object`)
}
const actual = Object.keys(value).sort()
const expected = [...keys].sort()
if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
throw new Error(`${name} fields are invalid`)
}
return value
}
async function jsonBody(request) {
const chunks = []
let size = 0
for await (const chunk of request) {
size += chunk.byteLength
if (size > MAX_BODY_BYTES) throw new Error('lifecycle request body is too large')
chunks.push(chunk)
}
if (size === 0) throw new Error('lifecycle request body is required')
return JSON.parse(Buffer.concat(chunks).toString('utf8'))
}
function send(response, status, value) {
response.writeHead(status, { 'content-type': 'application/json; charset=utf-8' })
response.end(`${JSON.stringify(value)}\n`)
}
function matchesSecret(actual, expected) {
if (typeof actual !== 'string' || typeof expected !== 'string') return false
const left = Buffer.from(actual)
const right = Buffer.from(expected)
return left.byteLength === right.byteLength && timingSafeEqual(left, right)
}
export class DshLifecycleBroker {
constructor({ shouldTerminate = async () => false, report = async () => {} } = {}) {
this.shouldTerminate = shouldTerminate
this.report = report
this.launch = null
this.session = null
this.published = false
this.shuttingDown = false
}
record(message, fields = {}) {
return Promise.resolve().then(() => this.report(message, fields)).catch(() => {})
}
prepareLaunch(componentId) {
if (componentId !== 'dsh-runtime') return Object.freeze({ environment: Object.freeze({}), release: () => {} })
const launch = Object.freeze({ id: randomUUID(), token: randomBytes(32).toString('base64url') })
this.launch = launch
this.session = null
this.published = false
void this.record('dsh.launch.authorized')
return Object.freeze({
environment: Object.freeze({ DSH_PLATFORM_LAUNCH_TOKEN: launch.token }),
release: () => this.release(launch.id),
})
}
release(launchId) {
if (this.launch?.id !== launchId) return
this.launch = null
this.session = null
void this.record('dsh.launch.released')
}
claim(launchToken) {
if (this.launch === null || this.session !== null || !matchesSecret(launchToken, this.launch.token)) {
throw Object.assign(new Error('DSH launch token is invalid or already consumed'), { statusCode: 409 })
}
const sessionId = randomUUID()
this.session = Object.freeze({ id: sessionId, launchId: this.launch.id, ready: false, verified: false })
this.launch = Object.freeze({ ...this.launch, token: null })
void this.record('dsh.launch.claimed')
return Object.freeze({ sessionId })
}
ready(sessionId, readyUrl = null) {
if (this.session === null || !matchesSecret(sessionId, this.session.id)) {
throw Object.assign(new Error('DSH lifecycle session is invalid'), { statusCode: 409 })
}
if (readyUrl !== null) {
let parsed
try {
parsed = new URL(readyUrl)
} catch {
throw Object.assign(new Error('DSH Web readiness URL is invalid'), { statusCode: 400 })
}
if (parsed.protocol !== 'http:' || parsed.hostname !== '127.0.0.1'
|| parsed.pathname !== '/' || parsed.username !== '' || parsed.password !== ''
|| parsed.hash !== '' || parsed.searchParams.getAll('token').length !== 1) {
throw Object.assign(new Error('DSH Web readiness URL is invalid'), { statusCode: 400 })
}
}
if (!this.session.ready) {
this.session = Object.freeze({ ...this.session, ready: true, readyUrl })
void this.record('dsh.launch.ready')
}
return Object.freeze({ ready: true })
}
verify() {
if (this.session?.ready !== true) throw new Error('DSH lifecycle is not ready for verification')
if (!this.session.verified) {
this.session = Object.freeze({ ...this.session, verified: true })
return this.record('dsh.launch.verified')
}
}
publish() {
if (this.session?.verified !== true) throw new Error('DSH lifecycle is not verified for publication')
if (!this.published) {
this.published = true
return this.record('platform.public-ready')
}
}
readiness() {
const ready = this.session?.ready === true
const verified = this.session?.verified === true
return Object.freeze({
generation: this.session?.id ?? null,
publicReady: ready && verified && this.published,
ready,
readyUrl: ready ? this.session.readyUrl ?? null : null,
verified,
})
}
async signal(sessionId, signal) {
if (signal !== 'SIGTERM') throw Object.assign(new Error('lifecycle signal is invalid'), { statusCode: 400 })
if (this.session === null || !matchesSecret(sessionId, this.session.id)) {
throw Object.assign(new Error('DSH lifecycle session is invalid'), { statusCode: 409 })
}
const disposition = this.session.ready !== true || this.shuttingDown || await this.shouldTerminate()
? 'terminate'
: 'request-restart'
await this.record('dsh.signal.disposition', { disposition, signal })
return Object.freeze({ disposition })
}
beginShutdown() {
this.published = false
this.shuttingDown = true
}
}
export function createDshLifecycleServer(broker) {
return createServer(async (request, response) => {
let pathname = 'invalid-url'
try {
pathname = new URL(request.url ?? '/', 'http://dsh-lifecycle.internal').pathname
if (request.method !== 'POST') {
send(response, 404, { error: 'not found' })
return
}
if (pathname === '/v1/runtime/claim') {
const body = exactObject(await jsonBody(request), ['launchToken'], 'claim request')
send(response, 200, broker.claim(body.launchToken))
} else if (pathname === '/v1/runtime/ready') {
const value = await jsonBody(request)
const body = Object.hasOwn(value, 'readyUrl')
? exactObject(value, ['readyUrl', 'sessionId'], 'ready request')
: exactObject(value, ['sessionId'], 'ready request')
send(response, 200, broker.ready(body.sessionId, body.readyUrl ?? null))
} else if (pathname === '/v1/runtime/readiness') {
exactObject(await jsonBody(request), [], 'readiness request')
send(response, 200, broker.readiness())
} else if (pathname === '/v1/runtime/signal') {
const body = exactObject(await jsonBody(request), ['sessionId', 'signal'], 'signal request')
send(response, 200, await broker.signal(body.sessionId, body.signal))
} else send(response, 404, { error: 'not found' })
} catch (error) {
await broker.record('dsh.lifecycle-request.failed', {
error,
method: request.method ?? null,
pathname,
level: error?.statusCode === 409 ? 'warning' : 'error',
})
send(response, Number.isInteger(error?.statusCode) ? error.statusCode : 400, {
error: error instanceof Error ? error.message : 'invalid lifecycle request',
})
}
})
}
export async function listenDshLifecycle(server, socketPath) {
await mkdir(dirname(socketPath), { recursive: true })
await rm(socketPath, { force: true })
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(socketPath, resolve)
})
await chmod(socketPath, 0o600)
}
@@ -0,0 +1,148 @@
import { lstat, readFile, readdir, rm } from 'node:fs/promises'
import { join } from 'node:path'
import { parseDeploymentRecord } from '../../lib/deployment-contracts.mjs'
async function jsonOptional(path) {
try { return JSON.parse(await readFile(path, 'utf8')) } catch (error) {
if (error?.code === 'ENOENT') return undefined
throw error
}
}
async function files(root, suffix = '') {
try { return (await readdir(root)).filter(name => !name.startsWith('.') && name.endsWith(suffix)).sort() } catch (error) {
if (error?.code === 'ENOENT') return []
throw error
}
}
async function removeUnmarked(root, retained, reserved = new Set()) {
const removed = []
for (const name of await files(root)) {
if (reserved.has(name) || retained.has(name)) continue
const path = join(root, name)
const details = await lstat(path)
if (!details.isDirectory() || details.isSymbolicLink()) continue
await rm(path, { recursive: true, force: true })
removed.push(name)
}
return removed
}
async function deploymentRecords(root) {
const records = new Map()
for (const name of await files(root, '.json')) {
const record = parseDeploymentRecord(await readFile(join(root, name)))
records.set(record.id, record)
}
return records
}
function recordIds(value, output) {
if (typeof value === 'string' && value.startsWith('deployment-record-')) output.add(value)
else if (Array.isArray(value)) for (const entry of value) recordIds(entry, output)
else if (value !== null && typeof value === 'object') for (const entry of Object.values(value)) recordIds(entry, output)
}
function heldRecords(channel, records, roots) {
const held = [...(channel?.holds ?? []), ...(channel?.experimentalBlocked === null || channel?.experimentalBlocked === undefined
? []
: [channel.experimentalBlocked])]
for (const hold of held) {
for (const record of records.values()) {
if (
record.dshVersion === hold.dshVersion
&& (hold.environmentVersion === null || hold.environmentVersion === undefined || record.environmentVersion === hold.environmentVersion)
) roots.add(record.id)
}
}
}
export class PlatformGarbageCollector {
constructor({ paths, deployments, snapshots }) {
this.paths = paths
this.deployments = deployments
this.snapshots = snapshots
}
async removeSnapshots(retained) {
if (this.snapshots?.remove === undefined) throw new Error('privileged snapshot cleanup is unavailable')
const removed = []
for (const name of await files(join(this.paths.snapshotsRoot, 'versions'))) {
if (retained.has(name)) continue
const details = await lstat(join(this.paths.snapshotsRoot, 'versions', name))
if (!details.isDirectory() || details.isSymbolicLink()) continue
if (await this.snapshots.remove(name)) removed.push(name)
}
return removed
}
async roots(records) {
const roots = new Set()
const slots = await this.deployments.state()
if (slots.current !== null) roots.add(slots.current)
if (slots.previous !== null) roots.add(slots.previous)
recordIds(await jsonOptional(this.deployments.activationPath), roots)
const transaction = await jsonOptional(join(this.paths.updaterStateRoot, 'transaction.json'))
recordIds(transaction, roots)
heldRecords(await jsonOptional(join(this.paths.updaterStateRoot, 'channel.json')), records, roots)
return { roots, transaction }
}
async collectDeployments() {
const records = await deploymentRecords(this.deployments.recordsRoot)
const { roots, transaction } = await this.roots(records)
const retained = {
environments: new Set(),
pristine: new Set(),
runtimes: new Set(),
systemPlugins: new Set(),
snapshots: new Set(),
}
for (const id of roots) {
const record = records.get(id)
if (record === undefined) continue
for (const [field, output] of [
['environment', retained.environments],
['pristine', retained.pristine],
['runtime', retained.runtimes],
['systemPlugins', retained.systemPlugins],
]) if (record[field].storage === 'store') output.add(record[field].id)
if (record.snapshotId !== null) retained.snapshots.add(record.snapshotId)
}
if (typeof transaction?.snapshotId === 'string') retained.snapshots.add(transaction.snapshotId)
const reserved = new Set(['current', 'previous', 'versions'])
const removed = {
environments: await removeUnmarked(this.paths.environmentsRoot, retained.environments, reserved),
pristine: await removeUnmarked(this.paths.pristineRoot, retained.pristine, reserved),
runtimes: await removeUnmarked(this.paths.runtimesRoot, retained.runtimes, reserved),
systemPlugins: await removeUnmarked(this.paths.systemPluginsRoot, retained.systemPlugins, reserved),
snapshots: await this.removeSnapshots(retained.snapshots),
records: [],
}
for (const [id] of records) {
if (!roots.has(id)) {
await rm(join(this.deployments.recordsRoot, `${id}.json`), { force: true })
removed.records.push(id)
}
}
return removed
}
async cleanCache() {
const removed = []
for (const name of await files(this.paths.downloadsRoot)) {
await rm(join(this.paths.downloadsRoot, name), { recursive: true, force: true })
removed.push(name)
}
return removed
}
async collect() {
return Object.freeze({
deployments: await this.collectDeployments(),
cache: await this.cleanCache(),
})
}
}
@@ -0,0 +1,572 @@
import { spawn } from 'node:child_process'
import { once } from 'node:events'
import { request } from 'node:http'
import { readFile } from 'node:fs/promises'
import { join, resolve } from 'node:path'
import { artifactForReference, parseComponentManifest, parseEnvironmentManifest } from '../../lib/contracts.mjs'
import { exactKeys, parseJsonDocument, plainObject, TrustError } from '../../lib/validation.mjs'
const STARTUP_HEALTH_RETRY_MS = 100
function delay(milliseconds) {
return new Promise(resolveDelay => {
const timer = setTimeout(resolveDelay, milliseconds)
timer.unref?.()
})
}
function terminate(child, graceMs = 5_000) {
if (child.exitCode !== null || child.signalCode !== null) return Promise.resolve()
return new Promise(resolveTerminate => {
const timer = setTimeout(() => {
if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL')
}, graceMs)
child.once('exit', () => {
clearTimeout(timer)
resolveTerminate()
})
child.kill('SIGTERM')
})
}
function runCommand(spec, options, captureOutput = true) {
return new Promise((resolveCommand, reject) => {
const child = options.spawnImpl(spec.executable, spec.args, {
env: options.environment,
stdio: ['ignore', 'pipe', 'pipe'],
})
if (captureOutput) options.capture(child, options.componentId, options.logging)
else {
child.stdout?.resume()
child.stderr?.resume()
}
const timer = setTimeout(() => child.kill('SIGKILL'), spec.timeoutSeconds * 1000)
child.once('error', reject)
child.once('exit', (code, signal) => {
clearTimeout(timer)
if (code === 0) resolveCommand()
else reject(new Error(`${options.componentId} command failed (code=${String(code)}, signal=${String(signal)})`))
})
})
}
function probeHttp(health) {
return new Promise(resolveProbe => {
const req = request({ hostname: health.host, port: health.port, path: health.path }, response => {
response.resume()
resolveProbe((response.statusCode ?? 500) < 500)
})
req.setTimeout(Math.min(health.intervalSeconds * 1000, 1_000), () => req.destroy())
req.once('error', () => resolveProbe(false))
req.end()
})
}
async function waitForHealth(component, running, options) {
if (component.health === null) return
const deadline = Date.now() + component.health.timeoutSeconds * 1000
while (Date.now() < deadline) {
if (running?.child.exitCode !== null) throw new Error(`${component.id} exited before health check passed`)
const healthy = component.health.type === 'http'
? await probeHttp(component.health)
: await runCommand(component.health.command, options, false).then(() => true, () => false)
if (healthy) return
await delay(Math.min(component.health.intervalSeconds * 1000, STARTUP_HEALTH_RETRY_MS))
}
if (running?.child !== undefined && running.child.exitCode !== null) {
throw new Error(`${component.id} exited before health check passed`)
}
throw new Error(`${component.id} health check timed out`)
}
function defaultCapture(child, componentId) {
for (const [stream, source] of [[child.stdout, 'stdout'], [child.stderr, 'stderr']]) {
stream?.on('data', chunk => process.stdout.write(`[${componentId}:${source}] ${chunk.toString()}`))
}
}
export class ComponentExitError extends Error {
constructor(componentId, code, signal) {
super(`${componentId} exited unexpectedly (code=${String(code)}, signal=${String(signal)})`)
this.name = 'ComponentExitError'
this.componentId = componentId
this.exitCode = code
this.signal = signal
}
}
export async function loadEnvironment(root) {
const environmentRoot = resolve(root)
const manifest = parseEnvironmentManifest(await readFile(join(environmentRoot, 'environment.manifest.json')))
const components = []
for (const reference of manifest.components) {
const artifact = artifactForReference(manifest, reference)
const component = parseComponentManifest(await readFile(join(environmentRoot, 'artifacts', artifact.id)))
if (component.id !== reference.id) {
throw new Error(`component ${reference.id} differs from the Environment manifest`)
}
components.push(component)
}
return Object.freeze({ root: environmentRoot, manifest, components: Object.freeze(components) })
}
export async function loadControlPlane(root) {
const controlPlaneRoot = resolve(root)
const definition = parseJsonDocument(await readFile(join(controlPlaneRoot, 'definition.json')), 'control plane')
exactKeys(definition, ['components', 'schema'], 'control plane')
if (definition.schema !== 1) throw new TrustError('control plane schema must be 1')
if (!Array.isArray(definition.components) || definition.components.length === 0) {
throw new TrustError('control plane components must be a non-empty array')
}
const components = []
const ids = new Set()
for (const [index, value] of definition.components.entries()) {
const reference = plainObject(value, `control plane components[${String(index)}]`)
exactKeys(reference, ['id', 'source'], `control plane components[${String(index)}]`)
if (typeof reference.id !== 'string' || !/^[a-z0-9][a-z0-9._-]{0,127}$/.test(reference.id)) {
throw new TrustError('control plane component ID is invalid')
}
if (ids.has(reference.id)) throw new TrustError('control plane component IDs must be unique')
if (typeof reference.source !== 'string' || reference.source.startsWith('/')) {
throw new TrustError('control plane component source is invalid')
}
const source = resolve(controlPlaneRoot, reference.source)
if (source === controlPlaneRoot || !source.startsWith(`${controlPlaneRoot}/`)) {
throw new TrustError('control plane component source escapes its root')
}
const component = parseComponentManifest(await readFile(source))
if (component.id !== reference.id) throw new TrustError(`control plane component ${reference.id} differs from its manifest`)
ids.add(reference.id)
components.push(component)
}
return Object.freeze({
root: controlPlaneRoot,
manifest: Object.freeze({ version: null }),
components: Object.freeze(components),
})
}
export class EnvironmentRunner {
constructor({
environmentRoot,
spawnImpl = spawn,
capture = defaultCapture,
loader = loadEnvironment,
prepareService = async () => ({ environment: {}, release: () => {} }),
recoverableComponents = [],
recoveryDelaysMs = [0, 2_000, 5_000],
report = () => {},
startupGroups = null,
}) {
if (!Array.isArray(recoverableComponents)
|| recoverableComponents.some(value => typeof value !== 'string' || value.length === 0)) {
throw new Error('Recoverable component IDs must be non-empty strings')
}
if (!Array.isArray(recoveryDelaysMs) || recoveryDelaysMs.length === 0
|| recoveryDelaysMs.some(value => !Number.isFinite(value) || value < 0)) {
throw new Error('Component recovery delays must be non-negative durations')
}
if (startupGroups !== null && (!Array.isArray(startupGroups) || startupGroups.length === 0
|| startupGroups.some(group => !Array.isArray(group) || group.length === 0
|| group.some(value => typeof value !== 'string' || value.length === 0)))) {
throw new Error('Startup groups must contain non-empty component ID lists')
}
this.environmentRoot = environmentRoot
this.spawnImpl = spawnImpl
this.capture = capture
this.loader = loader
this.prepareService = prepareService
this.recoverableComponents = new Set(recoverableComponents)
this.recoveryDelaysMs = Object.freeze([...recoveryDelaysMs])
this.report = report
this.startupGroups = startupGroups === null ? null : Object.freeze(
startupGroups.map(group => Object.freeze([...group])),
)
this.running = []
this.environment = undefined
this.operation = Promise.resolve()
this.stopping = false
this.componentRecoveries = new Map()
this.fatalListeners = new Set()
this.fatal = new Promise(resolveFatal => { this.resolveFatal = resolveFatal })
}
onFatal(listener) {
this.fatalListeners.add(listener)
return () => this.fatalListeners.delete(listener)
}
emitFatal(error) {
this.resolveFatal(error)
for (const listener of this.fatalListeners) {
try { listener(error) } catch {}
}
}
emitLifecycle(message, fields = {}) {
void Promise.resolve().then(() => this.report(message, fields)).catch(() => {})
}
serialized(operation) {
const result = this.operation.then(operation, operation)
this.operation = result.then(() => undefined, () => undefined)
return result
}
recoverable(component) {
return this.recoverableComponents.has(component.id) && component.type === 'service'
}
scheduleRecovery(component, originalError) {
if (this.stopping || !this.recoverable(component) || this.componentRecoveries.has(component.id)) return
const recover = () => this.recoverComponentUnlocked(component, originalError)
const pending = this.serialized(recover).finally(() => {
if (this.componentRecoveries.get(component.id) === pending) this.componentRecoveries.delete(component.id)
})
this.componentRecoveries.set(component.id, pending)
void pending.catch(() => {})
}
async recoverComponentUnlocked(component, originalError) {
const stale = this.running.find(value => value.component.id === component.id)
if (stale !== undefined) {
try { await this.stopComponentUnlocked(stale) } catch {}
}
for (let index = 0; index < this.recoveryDelaysMs.length; index += 1) {
if (this.stopping) return
const delayMs = this.recoveryDelaysMs[index]
if (delayMs > 0) await delay(delayMs)
if (this.stopping) return
const attempt = index + 1
this.emitLifecycle('component.recovery.started', {
componentId: component.id,
attempt,
maxAttempts: this.recoveryDelaysMs.length,
})
try {
await this.startComponentUnlocked(component, true)
this.emitLifecycle('component.recovery.completed', {
componentId: component.id,
attempt,
maxAttempts: this.recoveryDelaysMs.length,
})
return
} catch (error) {
this.emitLifecycle('component.recovery.failed', {
componentId: component.id,
attempt,
maxAttempts: this.recoveryDelaysMs.length,
error: error instanceof Error ? error.message : String(error),
level: 'warning',
})
}
}
this.emitLifecycle('component.isolated', {
componentId: component.id,
error: originalError instanceof Error ? originalError.message : String(originalError),
level: 'error',
})
}
commandOptions(component) {
return {
spawnImpl: this.spawnImpl,
capture: this.capture,
componentId: component.id,
logging: component.logging,
environment: { ...process.env, ...component.environment },
}
}
async phase(component, name) {
const spec = component.lifecycle[name]
if (spec !== null) await runCommand(spec, this.commandOptions(component))
}
startupSequence() {
if (this.startupGroups === null) return this.environment.components.map(component => [component])
const components = new Map(this.environment.components.map(component => [component.id, component]))
const seen = new Set()
const groups = this.startupGroups.map(group => group.map(componentId => {
const component = components.get(componentId)
if (component === undefined) throw new Error(`Startup group component ${componentId} does not exist`)
if (seen.has(componentId)) throw new Error(`Startup group component ${componentId} is duplicated`)
seen.add(componentId)
return component
}))
const missing = this.environment.components.filter(component => !seen.has(component.id))
if (missing.length > 0) {
throw new Error(`Startup groups omit components: ${missing.map(component => component.id).join(', ')}`)
}
return groups
}
start() {
return this.serialized(() => this.startUnlocked())
}
async startUnlocked() {
if (this.running.length > 0) throw new Error('Environment is already running')
this.environment = await this.loader(this.environmentRoot)
try {
const startupGroups = this.startupSequence()
const recoveries = []
for (const component of this.environment.components) await this.phase(component, 'prepare')
for (const group of startupGroups) {
const results = await Promise.allSettled(group.map(component => this.startComponentUnlocked(component)))
const failures = []
for (const [index, result] of results.entries()) {
if (result.status === 'fulfilled') continue
const component = group[index]
if (!this.recoverable(component)) {
failures.push(result.reason)
continue
}
this.emitLifecycle('component.isolated', {
componentId: component.id,
error: result.reason instanceof Error ? result.reason.message : String(result.reason),
level: 'error',
})
recoveries.push({ component, error: result.reason })
}
if (failures.length === 1) throw failures[0]
if (failures.length > 1) throw new AggregateError(failures, 'Startup group failed')
}
for (const recovery of recoveries) this.scheduleRecovery(recovery.component, recovery.error)
return this.status()
} catch (error) {
try {
await this.stopUnlocked()
} catch (cleanupError) {
this.emitLifecycle('environment.cleanup.failed', {
error: cleanupError,
level: 'warning',
originalError: error instanceof Error ? error.message : String(error),
})
}
throw error
}
}
async startComponentUnlocked(component, prepare = false) {
if (this.running.some(running => running.component.id === component.id)) {
throw new Error(`component ${component.id} is already running`)
}
const startedAt = Date.now()
this.emitLifecycle('component.starting', { componentId: component.id, componentType: component.type })
let running
let releaseService = () => {}
try {
if (prepare) await this.phase(component, 'prepare')
await this.phase(component, 'preStart')
if (component.type === 'service') {
const options = this.commandOptions(component)
const launch = await this.prepareService(component)
let released = false
const release = () => {
if (released) return
released = true
launch.release?.()
}
releaseService = release
const child = this.spawnImpl(component.command.executable, component.command.args, {
env: { ...options.environment, ...launch.environment },
stdio: ['ignore', 'pipe', 'pipe'],
})
this.capture(child, component.id, component.logging)
await Promise.race([
once(child, 'spawn'),
once(child, 'error').then(([error]) => { throw error }),
])
running = { component, child, ready: false, release }
this.emitLifecycle('component.spawned', { componentId: component.id, pid: child.pid ?? null })
child.once('exit', (code, signal) => {
release()
if (running.ready && !this.stopping && this.running.includes(running)) {
const error = new ComponentExitError(component.id, code, signal)
this.emitLifecycle('component.exited', {
componentId: component.id,
code,
signal,
error: error.message,
level: 'error',
})
if (this.recoverable(component)) this.scheduleRecovery(component, error)
else this.emitFatal(error)
}
})
} else {
await runCommand(component.command, this.commandOptions(component))
running = { component, child: undefined }
}
this.running.push(running)
this.running.sort((left, right) => (
this.environment.components.indexOf(left.component) - this.environment.components.indexOf(right.component)
))
await waitForHealth(component, running, this.commandOptions(component))
await this.phase(component, 'postStart')
if (running.child !== undefined && running.child.exitCode !== null) {
throw new Error(`${component.id} exited before startup completed`)
}
running.ready = true
this.emitLifecycle('component.ready', { componentId: component.id, elapsedMs: Date.now() - startedAt })
return running
} catch (error) {
this.emitLifecycle('component.start.failed', {
componentId: component.id,
elapsedMs: Date.now() - startedAt,
error: error instanceof Error ? error.message : String(error),
level: 'error',
})
if (running !== undefined) {
try {
await this.stopComponentUnlocked(running)
} catch (cleanupError) {
this.emitLifecycle('component.cleanup.failed', {
componentId: component.id,
error: cleanupError,
level: 'warning',
originalError: error instanceof Error ? error.message : String(error),
})
}
} else if (component.type === 'service') {
releaseService()
}
throw error
}
}
async stopComponentUnlocked(running) {
const { component, child } = running
const failures = []
this.emitLifecycle('component.stopping', { componentId: component.id, pid: child?.pid ?? null })
this.running = this.running.filter(value => value !== running)
for (const phase of ['preStop', 'stop']) {
try { await this.phase(component, phase) } catch (error) { failures.push(error) }
}
if (child !== undefined) {
try { await terminate(child) } catch (error) { failures.push(error) }
}
try { await this.phase(component, 'postStop') } catch (error) { failures.push(error) }
if (failures.length > 0) {
this.emitLifecycle('component.stop.failed', {
componentId: component.id,
errors: failures.map(error => error instanceof Error ? error.message : String(error)),
level: 'error',
})
throw new AggregateError(failures, `component ${component.id} shutdown failed`)
}
this.emitLifecycle('component.stopped', { componentId: component.id })
}
stop() {
this.stopping = true
return this.serialized(() => this.stopUnlocked())
}
async stopUnlocked() {
this.stopping = true
const failures = []
for (const running of [...this.running].reverse()) {
try { await this.stopComponentUnlocked(running) } catch (error) { failures.push(error) }
}
this.running = []
this.stopping = false
if (failures.length > 0) throw new AggregateError(failures, 'Environment shutdown failed')
}
reload() {
return this.serialized(async () => {
await this.stopUnlocked()
return this.startUnlocked()
})
}
suspend(componentId) {
return this.serialized(async () => {
const running = this.running.find(value => value.component.id === componentId)
if (running === undefined) throw new Error(`component ${componentId} is not running`)
if (running.component.type !== 'service') throw new Error(`component ${componentId} is not a service`)
await this.stopComponentUnlocked(running)
return this.status()
})
}
pause(componentId, { allowMissing = false } = {}) {
return this.serialized(async () => {
const running = this.running.find(value => value.component.id === componentId)
const component = running?.component ?? this.environment?.components.find(value => value.id === componentId)
if (component === undefined) {
if (allowMissing) return this.status()
throw new Error(`component ${componentId} does not exist`)
}
if (component.type !== 'service') throw new Error(`component ${componentId} is not a service`)
if (running === undefined) return this.status()
await this.stopComponentUnlocked(running)
return this.status()
})
}
resume(componentId) {
return this.serialized(async () => {
if (this.environment === undefined) throw new Error('Environment is not loaded')
const component = this.environment.components.find(value => value.id === componentId)
if (component === undefined) throw new Error(`component ${componentId} does not exist`)
if (this.running.some(value => value.component.id === componentId)) return this.status()
await this.startComponentUnlocked(component, true)
return this.status()
})
}
restart(componentId, { beforeStart, recoverStart = false, onStartFailure } = {}) {
return this.serialized(async () => {
if (this.environment === undefined) this.environment = await this.loader(this.environmentRoot)
const running = this.running.find(value => value.component.id === componentId)
const component = running?.component
?? this.environment.components.find(value => value.id === componentId)
if (component === undefined) throw new Error(`component ${componentId} does not exist`)
if (component.type !== 'service') throw new Error(`component ${componentId} is not a service`)
if (running !== undefined) await this.stopComponentUnlocked(running)
try {
await beforeStart?.()
await this.startComponentUnlocked(component, true)
return this.status()
} catch (error) {
const failures = [error]
try { await onStartFailure?.(error) } catch (recoveryError) { failures.push(recoveryError) }
if (recoverStart && failures.length === 1) {
try { await this.startComponentUnlocked(component, true) } catch (recoveryError) { failures.push(recoveryError) }
}
if (failures.length > 1) throw new AggregateError(failures, `component ${componentId} restart recovery failed`)
throw error
}
})
}
health() {
return this.serialized(async () => {
const components = await Promise.all((this.environment?.components ?? []).map(async component => {
const running = this.running.find(value => value.component.id === component.id)
if (running === undefined) return { id: component.id, running: false, healthy: false }
const healthy = component.health === null
? running.child === undefined || running.child.exitCode === null
: component.health.type === 'http'
? await probeHttp(component.health)
: await runCommand(component.health.command, this.commandOptions(component), false).then(() => true, () => false)
return { id: component.id, running: true, healthy }
}))
return { healthy: components.length > 0 && components.every(component => component.healthy), components }
})
}
status() {
return Object.freeze({
environmentVersion: this.environment?.manifest.version ?? null,
components: this.running.map(({ component, child }) => ({
id: component.id,
type: component.type,
pid: child?.pid ?? null,
})),
})
}
}
@@ -0,0 +1,347 @@
export class BootstrapRuntime {
constructor({
controlPlane,
environment,
validateDeployment = async () => {},
prepareDeployment = async () => {},
beforeReady = async () => {},
onEnvironmentVerified = async () => {},
onDshAvailable = async () => {},
onEnvironmentFatal = async () => {},
onDshRecovered = async () => {},
ownsDshLifecycle = async () => false,
recoveryDelaysMs = [0, 2_000, 5_000],
sleep = (milliseconds, signal) => new Promise(resolve => {
if (milliseconds === 0 || signal?.aborted) return resolve()
const timer = setTimeout(resolve, milliseconds)
timer.unref?.()
signal?.addEventListener('abort', () => { clearTimeout(timer); resolve() }, { once: true })
}),
report = async () => {},
}) {
if (!Array.isArray(recoveryDelaysMs) || recoveryDelaysMs.length !== 3
|| recoveryDelaysMs.some(value => !Number.isFinite(value) || value < 0)) {
throw new Error('DSH recovery delays must contain three non-negative durations')
}
this.controlPlane = controlPlane
this.environment = environment
this.fatal = controlPlane.fatal
this.recoveryMode = null
this.startupComplete = false
this.dshLifecycle = Object.freeze({
state: 'starting', action: 'start', taskId: null, attempt: 0, maxAttempts: 3,
error: null, updatedAt: new Date().toISOString(),
})
this.recovery = Promise.resolve()
this.recoveryAbort = new AbortController()
this.validateDeployment = validateDeployment
this.prepareDeployment = prepareDeployment
this.beforeReady = beforeReady
this.onEnvironmentVerified = onEnvironmentVerified
this.onDshAvailable = onDshAvailable
this.onEnvironmentFatal = onEnvironmentFatal
this.onDshRecovered = onDshRecovered
this.ownsDshLifecycle = ownsDshLifecycle
this.recoveryDelaysMs = Object.freeze([...recoveryDelaysMs])
this.sleep = sleep
this.report = report
const handleEnvironmentFatal = error => {
const recover = () => this.handleEnvironmentFatal(error)
this.recovery = this.recovery.then(recover, recover).catch(recoveryError => this.record(
'environment.recovery.failed', { error: recoveryError, originalError: String(error) },
))
}
if (typeof environment.onFatal === 'function') {
this.offEnvironmentFatal = environment.onFatal(handleEnvironmentFatal)
} else {
void environment.fatal.then(handleEnvironmentFatal)
this.offEnvironmentFatal = undefined
}
}
async lifecycleOwned() {
try {
return await this.ownsDshLifecycle()
} catch (error) {
await this.record('dsh.recovery-ownership.failed', { error, level: 'warning' })
return true
}
}
async handleEnvironmentFatal(error) {
if (error?.componentId !== 'dsh-runtime') return this.isolateEnvironmentFailure(error)
const message = error instanceof Error ? error.message : String(error)
if (await this.lifecycleOwned()) {
this.publishDshLifecycle({ state: 'recovering', action: 'auto-recover', attempt: 0, error: message })
await this.record('dsh.recovery.deferred', { error, reason: 'deployment-transaction-active', level: 'warning' })
return
}
for (let index = 0; index < this.recoveryDelaysMs.length; index += 1) {
const attempt = index + 1
await this.sleep(this.recoveryDelaysMs[index], this.recoveryAbort.signal)
if (this.recoveryAbort.signal.aborted) return
if (await this.lifecycleOwned()) {
this.publishDshLifecycle({ state: 'recovering', action: 'auto-recover', attempt: attempt - 1, error: message })
await this.record('dsh.recovery.deferred', { error, reason: 'deployment-transaction-active', level: 'warning' })
return
}
this.publishDshLifecycle({ state: 'recovering', action: 'auto-recover', attempt, error: message })
await this.record('dsh.recovery.attempt.started', { attempt, maxAttempts: this.recoveryDelaysMs.length })
try {
await this.validateDeployment()
await this.prepareDeployment()
await this.environment.restart('dsh-runtime')
await this.onEnvironmentVerified()
this.recoveryMode = null
this.publishDshLifecycle({ state: 'running', action: null, attempt: 0, error: null })
let recoveryStatusPublished = true
try {
await this.onDshRecovered()
} catch (statusError) {
recoveryStatusPublished = false
await this.record('dsh.recovery-status.failed', { error: statusError, level: 'warning' })
}
if (recoveryStatusPublished) await this.onDshAvailable()
await this.record('dsh.recovery.completed', { attempt, maxAttempts: this.recoveryDelaysMs.length })
return
} catch (recoveryError) {
await this.record('dsh.recovery.attempt.failed', {
attempt,
error: recoveryError,
maxAttempts: this.recoveryDelaysMs.length,
level: 'warning',
})
}
}
await this.isolateEnvironmentFailure(error)
}
async isolateEnvironmentFailure(error) {
this.recoveryMode = error instanceof Error ? error.message : String(error)
this.publishDshLifecycle({
state: 'failed', action: null,
attempt: error?.componentId === 'dsh-runtime' ? this.recoveryDelaysMs.length : 0,
error: this.recoveryMode,
})
try {
await this.onEnvironmentFatal(error)
} catch (reportError) {
await this.record('environment.recovery-report.failed', { error: reportError, originalError: String(error) })
}
try {
await this.environment.stop()
} catch (stopError) {
await this.record('environment.recovery-stop.failed', { error: stopError, originalError: String(error) })
}
}
record(message, fields = {}) {
return Promise.resolve().then(() => this.report(message, fields)).catch(() => {})
}
publishDshLifecycle(value) {
this.dshLifecycle = Object.freeze({
...this.dshLifecycle,
...value,
updatedAt: new Date().toISOString(),
})
return this.dshLifecycle
}
async stopControlPlane(phase, cause) {
try {
await this.controlPlane.stop()
} catch (error) {
await this.record('control-plane.cleanup.failed', {
error,
phase,
originalError: cause instanceof Error ? cause.message : String(cause),
})
}
}
async stopEnvironment(phase, cause) {
try {
await this.environment.stop()
} catch (error) {
await this.record('environment.cleanup.failed', {
error,
phase,
originalError: cause instanceof Error ? cause.message : String(cause),
})
}
}
async startEnvironment({ onEnvironmentFailure, allowRecovery }) {
try {
await this.validateDeployment()
await this.prepareDeployment()
await this.environment.start()
await this.onEnvironmentVerified()
} catch (error) {
let retry
try {
retry = await onEnvironmentFailure?.(error)
} catch (recoveryError) {
const failure = new AggregateError([error, recoveryError], 'Deployment failed and its fallback could not be resolved')
if (allowRecovery) {
this.recoveryMode = failure.message
return
}
throw failure
}
if (retry === true) {
try {
await this.validateDeployment()
await this.prepareDeployment()
await this.environment.start()
await this.onEnvironmentVerified()
} catch (fallbackError) {
const failure = new AggregateError([error, fallbackError], 'Deployment candidate and fallback both failed')
if (allowRecovery) this.recoveryMode = failure.message
else throw failure
}
} else if (allowRecovery) {
this.recoveryMode = error instanceof Error ? error.message : 'Deployment failed to start'
} else throw error
}
}
async start({ onEnvironmentFailure, allowRecovery = false } = {}) {
const readiness = Promise.resolve().then(async () => {
await this.controlPlane.start()
await this.beforeReady()
})
const environment = Promise.resolve().then(() => this.startEnvironment({ onEnvironmentFailure, allowRecovery }))
const results = await Promise.allSettled([readiness, environment])
const failures = results.filter(result => result.status === 'rejected').map(result => result.reason)
if (failures.length > 0) {
const failure = failures.length === 1 ? failures[0] : new AggregateError(failures, 'Platform startup failed')
await Promise.all([
this.stopEnvironment('concurrent-start', failure),
this.stopControlPlane('concurrent-start', failure),
])
throw failure
}
return this.status()
}
async stop() {
this.offEnvironmentFatal?.()
this.recoveryAbort.abort()
await this.recovery
const failures = []
try { await this.environment.stop() } catch (error) { failures.push(error) }
try { await this.controlPlane.stop() } catch (error) { failures.push(error) }
if (failures.length > 0) throw new AggregateError(failures, 'Bootstrap shutdown failed')
}
async reload() {
await this.validateDeployment()
await this.prepareDeployment()
const status = await this.environment.reload()
await this.onEnvironmentVerified()
this.recoveryMode = null
if (this.environment.status().components.some(value => value.id === 'dsh-runtime')) {
this.publishDshLifecycle({ state: 'running', action: null, attempt: 0, error: null })
await this.onDshAvailable()
}
return status
}
async suspend(componentId) {
if (componentId !== 'dsh-runtime') return this.environment.suspend(componentId)
this.publishDshLifecycle({ state: 'stopping', action: 'stop', attempt: 0, error: null })
try {
const status = await this.environment.suspend(componentId)
this.publishDshLifecycle({ state: 'stopped', action: null, attempt: 0, error: null })
return status
} catch (error) {
this.publishDshLifecycle({
state: 'failed', action: null,
error: error instanceof Error ? error.message : String(error),
})
throw error
}
}
async pause(componentId) {
if (componentId !== 'dsh-runtime') return this.environment.pause(componentId)
this.publishDshLifecycle({ state: 'stopping', action: 'stop', attempt: 0, error: null })
try {
const status = await this.environment.pause(componentId, { allowMissing: true })
this.publishDshLifecycle({ state: 'stopped', action: null, attempt: 0, error: null })
return status
} catch (error) {
this.publishDshLifecycle({
state: 'failed', action: null,
error: error instanceof Error ? error.message : String(error),
})
throw error
}
}
async resume(componentId, options) {
if (componentId === 'dsh-runtime') this.publishDshLifecycle({ state: 'starting', action: 'start', attempt: 0, error: null })
try {
if (componentId === 'dsh-runtime') {
if (options?.skipValidation !== true) await this.validateDeployment()
if (options?.skipPreparation !== true) await this.prepareDeployment()
}
const status = await this.environment.resume(componentId)
if (componentId === 'dsh-runtime') {
await this.onEnvironmentVerified()
this.recoveryMode = null
this.publishDshLifecycle({ state: 'running', action: null, attempt: 0, error: null })
await this.onDshAvailable()
}
return status
} catch (error) {
if (componentId === 'dsh-runtime') this.enterRecovery(error)
throw error
}
}
async restart(componentId, options) {
if (componentId === 'dsh-runtime') this.publishDshLifecycle({ state: 'restarting', action: 'restart', attempt: 0, error: null })
try {
if (componentId === 'dsh-runtime') {
if (options?.skipValidation !== true) await this.validateDeployment()
if (options?.skipPreparation !== true) await this.prepareDeployment()
}
const status = await this.environment.restart(componentId, options)
if (componentId === 'dsh-runtime') {
await this.onEnvironmentVerified()
this.recoveryMode = null
this.publishDshLifecycle({ state: 'running', action: null, attempt: 0, error: null })
await this.onDshAvailable()
}
return status
} catch (error) {
if (componentId === 'dsh-runtime') this.enterRecovery(error)
throw error
}
}
health() { return this.environment.health() }
markStartupComplete() {
this.startupComplete = true
if (this.recoveryMode === null && this.environment.status().components.some(value => value.id === 'dsh-runtime')) {
this.publishDshLifecycle({ state: 'running', action: null, attempt: 0, error: null })
} else if (this.recoveryMode !== null) {
this.publishDshLifecycle({ state: 'failed', action: null, error: this.recoveryMode })
}
}
enterRecovery(error) {
this.recoveryMode = error instanceof Error ? error.message : String(error)
this.publishDshLifecycle({ state: 'failed', action: null, attempt: 0, error: this.recoveryMode })
}
status() {
const environment = this.environment.status()
return Object.freeze({
...environment,
controlPlane: this.controlPlane.status().components,
recoveryMode: this.recoveryMode,
dshLifecycle: this.dshLifecycle,
startupComplete: this.startupComplete,
})
}
}
Whitespace-only changes.
@@ -0,0 +1,12 @@
export async function consumeInternalCapability(access, { token, audience, method, target }) {
try {
const result = await access.request('POST', '/v1/capabilities/consume', {
token, audience, method, target,
})
return result.authorized === true
} catch (error) {
if (error?.statusCode === 401) return false
if (!Number.isInteger(error?.statusCode) || error.statusCode >= 500) error.statusCode = 503
throw error
}
}
@@ -0,0 +1,63 @@
import { lstat, readdir } from 'node:fs/promises'
import { join } from 'node:path'
const EVIDENCE_KEYS = Object.freeze([
'dshSettings',
'webProfile',
'dshSessions',
'userSkills',
'deploymentState',
'managementState',
'updaterState',
'legacyAuthenticationConfigured',
])
async function exists(path) {
return lstat(path).then(() => true, error => error?.code === 'ENOENT' ? false : Promise.reject(error))
}
async function nonEmptyDirectory(path) {
try { return (await readdir(path)).length > 0 }
catch (error) { return error?.code === 'ENOENT' ? false : Promise.reject(error) }
}
export function parseAccessEvidence(value) {
let parsed
try { parsed = JSON.parse(value) } catch { throw new Error('legacy installation evidence is invalid') }
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)
|| Object.keys(parsed).length !== EVIDENCE_KEYS.length
|| EVIDENCE_KEYS.some(key => typeof parsed[key] !== 'boolean')) {
throw new Error('legacy installation evidence is invalid')
}
return Object.freeze(Object.fromEntries(EVIDENCE_KEYS.map(key => [key, parsed[key]])))
}
export async function collectAccessEvidence({ dshHome, paths, legacyAuthenticationConfigured = false }) {
const [
dshSettings,
webProfile,
dshSessions,
userSkills,
deploymentState,
managementState,
updaterState,
] = await Promise.all([
exists(join(dshHome, 'settings.yaml')),
exists(join(dshHome, 'profiles', 'web', 'package.json')),
nonEmptyDirectory(join(dshHome, 'sessions')),
nonEmptyDirectory(join(dshHome, 'skills')),
exists(join(paths.deploymentStateRoot, 'slots.json')),
nonEmptyDirectory(paths.managementStateRoot),
nonEmptyDirectory(paths.updaterStateRoot),
])
return Object.freeze({
dshSettings,
webProfile,
dshSessions,
userSkills,
deploymentState,
managementState,
updaterState,
legacyAuthenticationConfigured: legacyAuthenticationConfigured === true,
})
}
@@ -0,0 +1,76 @@
import { exactKeys, isoTimestamp, parseJsonDocument, plainObject, positiveSafeInteger, TrustError } from './validation.mjs'
export const MANIFEST_MEDIA_TYPE = 'application/vnd.dsh-platform.manifest.v1+json'
export const SIGNATURE_MEDIA_TYPE = 'application/vnd.dsh-platform.signature.v1+json'
function nonNegativeSafeInteger(value, label) {
if (!Number.isSafeInteger(value) || value < 0) {
throw new TrustError(`${label} must be a non-negative safe integer`)
}
return value
}
export function parseArtifactDescriptor(value, label = 'artifact') {
const object = plainObject(value, label)
exactKeys(object, ['id', 'mediaType', 'sha256', 'size', 'url'], label)
if (typeof object.id !== 'string' || !/^[a-z0-9][a-z0-9._-]{0,127}$/.test(object.id)) {
throw new TrustError(`${label}.id is invalid`)
}
if (typeof object.mediaType !== 'string' || !/^[\x21-\x7e]{1,127}$/.test(object.mediaType)) {
throw new TrustError(`${label}.mediaType is invalid`)
}
if (typeof object.sha256 !== 'string' || !/^[a-f0-9]{64}$/.test(object.sha256)) {
throw new TrustError(`${label}.sha256 must be a SHA-256 hex digest`)
}
const size = nonNegativeSafeInteger(object.size, `${label}.size`)
let url
try {
url = new URL(object.url)
} catch {
throw new TrustError(`${label}.url must be an HTTPS URL`)
}
if (url.protocol !== 'https:' || url.username !== '' || url.password !== '') {
throw new TrustError(`${label}.url must be an HTTPS URL without credentials`)
}
return Object.freeze({
id: object.id,
mediaType: object.mediaType,
sha256: object.sha256,
size,
url: url.href,
})
}
export function parseArtifactList(value, label) {
if (!Array.isArray(value)) throw new TrustError(`${label} must be an array`)
const artifacts = value.map((entry, index) => parseArtifactDescriptor(entry, `${label}[${String(index)}]`))
if (new Set(artifacts.map(artifact => artifact.id)).size !== artifacts.length) {
throw new TrustError(`${label} must not contain duplicate IDs`)
}
return Object.freeze(artifacts)
}
export function parseReleaseManifest(bytes) {
const object = parseJsonDocument(bytes, 'release manifest')
const common = ['artifacts', 'issuedAt', 'keyringGeneration', 'manifestType', 'schema', 'targetSequence', 'version']
const specific = object.manifestType === 'bootstrap'
? ['bootstrapApi', 'entrypoint']
: object.manifestType === 'environment'
? ['bootstrapApi', 'components', 'patches', 'systemPlugins']
: []
exactKeys(object, [...common, ...specific], 'release manifest')
if (object.schema !== 1) throw new TrustError('release manifest schema must be 1')
if (typeof object.manifestType !== 'string' || !/^[a-z][a-z0-9-]{0,63}$/.test(object.manifestType)) {
throw new TrustError('release manifest manifestType is invalid')
}
if (typeof object.version !== 'string' || object.version === '') {
throw new TrustError('release manifest version is invalid')
}
return Object.freeze({
document: object,
keyringGeneration: positiveSafeInteger(object.keyringGeneration, 'release manifest keyringGeneration'),
targetSequence: positiveSafeInteger(object.targetSequence, 'release manifest targetSequence'),
issuedAt: isoTimestamp(object.issuedAt, 'release manifest issuedAt'),
artifacts: parseArtifactList(object.artifacts, 'release manifest artifacts'),
})
}
@@ -0,0 +1,46 @@
import { link, mkdir, open, rename, rm } from 'node:fs/promises'
import { randomUUID } from 'node:crypto'
import { dirname } from 'node:path'
async function syncDirectory(path) {
const handle = await open(path, 'r')
try {
await handle.sync()
} finally {
await handle.close()
}
}
async function temporaryFile(path, bytes, mode) {
await mkdir(dirname(path), { recursive: true })
const temporary = `${path}.${randomUUID()}.tmp`
const handle = await open(temporary, 'wx', mode)
try {
await handle.writeFile(bytes)
await handle.sync()
} finally {
await handle.close()
}
return temporary
}
export async function durableReplace(path, bytes, mode = 0o600) {
const temporary = await temporaryFile(path, bytes, mode)
try {
await rename(temporary, path)
await syncDirectory(dirname(path))
} catch (error) {
await rm(temporary, { force: true })
throw error
}
}
export async function durableCreate(path, bytes, mode = 0o600) {
const temporary = await temporaryFile(path, bytes, mode)
try {
await link(temporary, path)
await syncDirectory(dirname(path))
} finally {
await rm(temporary, { force: true })
}
}
@@ -0,0 +1,31 @@
export function parseBootstrapVersion(value, label = 'Bootstrap VERSION') {
if (typeof value !== 'string') throw new Error(`${label} is invalid`)
const normalized = value.endsWith('\r\n')
? value.slice(0, -2)
: value.endsWith('\n') ? value.slice(0, -1) : value
if (!/^\d+\.\d+\.\d+$/.test(normalized)) {
throw new Error(`${label} is invalid`)
}
if (normalized.split('.').some(part => !Number.isSafeInteger(Number(part)))) {
throw new Error(`${label} is invalid`)
}
return normalized
}
function compareBootstrapVersions(left, right) {
const leftParts = left.split('.').map(Number)
const rightParts = right.split('.').map(Number)
for (let index = 0; index < leftParts.length; index += 1) {
if (leftParts[index] !== rightParts[index]) return leftParts[index] - rightParts[index]
}
return 0
}
export function validateBootstrapContentTransition({ previousVersion, previousSha256, nextVersion, nextSha256 }) {
parseBootstrapVersion(previousVersion, 'previous Bootstrap version')
parseBootstrapVersion(nextVersion, 'next Bootstrap version')
if (previousSha256 === nextSha256) return
if (compareBootstrapVersions(nextVersion, previousVersion) <= 0) {
throw new Error('Bootstrap content changed without increasing Bootstrap VERSION')
}
}
@@ -0,0 +1,23 @@
import { TrustError } from './validation.mjs'
function normalize(value) {
if (value === null || typeof value === 'string' || typeof value === 'boolean') return value
if (typeof value === 'number') {
if (!Number.isSafeInteger(value)) throw new TrustError('canonical JSON numbers must be safe integers')
return value
}
if (Array.isArray(value)) return value.map(normalize)
if (typeof value === 'object') {
const result = {}
for (const key of Object.keys(value).sort()) {
if (value[key] === undefined) throw new TrustError('canonical JSON cannot contain undefined')
result[key] = normalize(value[key])
}
return result
}
throw new TrustError(`canonical JSON cannot contain ${typeof value}`)
}
export function canonicalJson(value) {
return Buffer.from(`${JSON.stringify(normalize(value))}\n`)
}
@@ -0,0 +1,265 @@
import { exactKeys, isoTimestamp, parseJsonDocument, plainObject, positiveSafeInteger, TrustError } from './validation.mjs'
import { parseArtifactList } from './artifact-contracts.mjs'
function identifier(value, label) {
if (typeof value !== 'string' || !/^[a-z0-9][a-z0-9._-]{0,127}$/.test(value)) {
throw new TrustError(`${label} is invalid`)
}
return value
}
function version(value, label) {
if (typeof value !== 'string' || value === '' || value.length > 128 || /[\0/\\]/.test(value)) {
throw new TrustError(`${label} is invalid`)
}
return value
}
function exactReference(value, label) {
const object = plainObject(value, label)
exactKeys(object, ['manifestArtifactId', 'signatureArtifactId', 'version'], label)
const manifestArtifactId = identifier(object.manifestArtifactId, `${label}.manifestArtifactId`)
const signatureArtifactId = identifier(object.signatureArtifactId, `${label}.signatureArtifactId`)
if (manifestArtifactId === signatureArtifactId) throw new TrustError(`${label} manifest and signature Artifacts must differ`)
return Object.freeze({
version: version(object.version, `${label}.version`),
manifestArtifactId,
signatureArtifactId,
})
}
function dshReference(value) {
const object = plainObject(value, 'stable.desired.dsh')
exactKeys(object, ['integrity', 'version'], 'stable.desired.dsh')
if (typeof object.integrity !== 'string' || !/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(object.integrity)) {
throw new TrustError('stable.desired.dsh.integrity must be npm SHA-512 integrity')
}
return Object.freeze({
version: version(object.version, 'stable.desired.dsh.version'),
integrity: object.integrity,
})
}
function registryKey(value, label) {
const object = plainObject(value, label)
exactKeys(object, ['algorithm', 'expires', 'keyId', 'publicKey'], label)
if (object.algorithm !== 'ECDSA-P256-SHA256') throw new TrustError(`${label}.algorithm is unsupported`)
if (typeof object.keyId !== 'string' || !/^SHA256:[A-Za-z0-9+/]{43}$/.test(object.keyId)) {
throw new TrustError(`${label}.keyId is invalid`)
}
if (typeof object.publicKey !== 'string' || !/^[A-Za-z0-9+/]+={0,2}$/.test(object.publicKey)) {
throw new TrustError(`${label}.publicKey is invalid`)
}
if (object.expires !== null) isoTimestamp(object.expires, `${label}.expires`)
return Object.freeze({ ...object })
}
export function parseOfficialDshPolicy(value, label = 'official DSH policy') {
const object = plainObject(value, label)
exactKeys(object, ['keys', 'packageName', 'registry'], label)
if (object.registry !== 'https://registry.npmjs.org/') {
throw new TrustError(`${label}.registry must be the official npm registry`)
}
if (object.packageName !== '@deepseek-ai/dsh') {
throw new TrustError(`${label}.packageName must be @deepseek-ai/dsh`)
}
if (!Array.isArray(object.keys) || object.keys.length === 0) throw new TrustError(`${label}.keys must not be empty`)
const keys = object.keys.map((entry, index) => registryKey(entry, `${label}.keys[${String(index)}]`))
if (new Set(keys.map(key => key.keyId)).size !== keys.length) throw new TrustError(`${label}.keys must be unique`)
return Object.freeze({ registry: object.registry, packageName: object.packageName, keys: Object.freeze(keys) })
}
export function parseStable(bytes) {
const object = parseJsonDocument(bytes, 'stable')
exactKeys(
object,
['artifacts', 'desired', 'issuedAt', 'keyringGeneration', 'officialDshPolicy', 'schema', 'targetSequence', 'updateApi'],
'stable',
)
if (object.schema !== 1) throw new TrustError('stable.schema must be 1')
if (object.updateApi !== 1) throw new TrustError('stable.updateApi must be 1')
const desired = plainObject(object.desired, 'stable.desired')
exactKeys(desired, ['bootstrap', 'dsh', 'environment'], 'stable.desired')
const artifacts = parseArtifactList(object.artifacts, 'stable.artifacts')
const parsedDesired = Object.freeze({
bootstrap: exactReference(desired.bootstrap, 'stable.desired.bootstrap'),
environment: exactReference(desired.environment, 'stable.desired.environment'),
dsh: dshReference(desired.dsh),
})
const artifactIds = new Set(artifacts.map(artifact => artifact.id))
for (const artifactId of [
parsedDesired.bootstrap.manifestArtifactId,
parsedDesired.bootstrap.signatureArtifactId,
parsedDesired.environment.manifestArtifactId,
parsedDesired.environment.signatureArtifactId,
]) {
if (!artifactIds.has(artifactId)) throw new TrustError(`stable desired Artifact ${JSON.stringify(artifactId)} is missing`)
}
return Object.freeze({
document: object,
keyringGeneration: positiveSafeInteger(object.keyringGeneration, 'stable.keyringGeneration'),
targetSequence: positiveSafeInteger(object.targetSequence, 'stable.targetSequence'),
issuedAt: isoTimestamp(object.issuedAt, 'stable.issuedAt'),
artifacts,
desired: parsedDesired,
officialDshPolicy: parseOfficialDshPolicy(object.officialDshPolicy),
})
}
function stringArray(value, label) {
if (!Array.isArray(value) || value.some(entry => typeof entry !== 'string' || entry.includes('\0'))) {
throw new TrustError(`${label} must be a string array`)
}
return Object.freeze([...value])
}
function stringMap(value, label) {
const object = plainObject(value, label)
const result = {}
for (const [key, entry] of Object.entries(object)) {
if (!/^[A-Z_][A-Z0-9_]*$/.test(key) || typeof entry !== 'string' || entry.includes('\0')) {
throw new TrustError(`${label} contains an invalid environment entry`)
}
result[key] = entry
}
return Object.freeze(result)
}
function command(value, label) {
const object = plainObject(value, label)
exactKeys(object, ['args', 'executable', 'timeoutSeconds'], label)
if (typeof object.executable !== 'string' || !object.executable.startsWith('/')) {
throw new TrustError(`${label}.executable must be an absolute path`)
}
return Object.freeze({
executable: object.executable,
args: stringArray(object.args, `${label}.args`),
timeoutSeconds: positiveSafeInteger(object.timeoutSeconds, `${label}.timeoutSeconds`),
})
}
function optionalCommand(value, label) {
return value === null ? null : command(value, label)
}
function parseHealth(value) {
if (value === null) return null
const object = plainObject(value, 'component.health')
if (object.type === 'http') {
exactKeys(object, ['host', 'intervalSeconds', 'path', 'port', 'timeoutSeconds', 'type'], 'component.health')
if (object.host !== '127.0.0.1') throw new TrustError('HTTP health host must be 127.0.0.1')
if (!Number.isInteger(object.port) || object.port < 1 || object.port > 65535) throw new TrustError('HTTP health port is invalid')
if (typeof object.path !== 'string' || !object.path.startsWith('/')) throw new TrustError('HTTP health path is invalid')
} else if (object.type === 'exec') {
exactKeys(object, ['command', 'intervalSeconds', 'timeoutSeconds', 'type'], 'component.health')
command(object.command, 'component.health.command')
} else throw new TrustError('component.health.type must be http or exec')
positiveSafeInteger(object.intervalSeconds, 'component.health.intervalSeconds')
positiveSafeInteger(object.timeoutSeconds, 'component.health.timeoutSeconds')
return Object.freeze(object)
}
export function parseComponentManifest(bytes) {
const object = parseJsonDocument(bytes, 'component')
exactKeys(
object,
['command', 'environment', 'health', 'id', 'lifecycle', 'logging', 'schema', 'type'],
'component',
)
if (object.schema !== 1) throw new TrustError('component.schema must be 1')
if (!['service', 'oneshot', 'hook'].includes(object.type)) throw new TrustError('component.type is invalid')
const lifecycle = plainObject(object.lifecycle, 'component.lifecycle')
const phases = ['prepare', 'postStart', 'postStop', 'preStart', 'preStop', 'stop']
exactKeys(lifecycle, phases, 'component.lifecycle')
const logging = plainObject(object.logging, 'component.logging')
exactKeys(logging, ['stderr', 'stdout'], 'component.logging')
if (typeof logging.stdout !== 'boolean' || typeof logging.stderr !== 'boolean') {
throw new TrustError('component logging flags must be booleans')
}
return Object.freeze({
schema: object.schema,
id: identifier(object.id, 'component.id'),
type: object.type,
command: command(object.command, 'component.command'),
environment: stringMap(object.environment, 'component.environment'),
lifecycle: Object.freeze(Object.fromEntries(phases.map(phase => [
phase,
optionalCommand(lifecycle[phase], `component.lifecycle.${phase}`),
]))),
health: parseHealth(object.health),
logging: Object.freeze({ stdout: logging.stdout, stderr: logging.stderr }),
})
}
function orderedReferences(value, artifacts, label) {
if (!Array.isArray(value)) throw new TrustError(`${label} must be an array`)
const result = value.map((entry, index) => {
const object = plainObject(entry, `${label}[${String(index)}]`)
exactKeys(object, ['id', 'sha256'], `${label}[${String(index)}]`)
if (typeof object.sha256 !== 'string' || !/^[a-f0-9]{64}$/.test(object.sha256)) {
throw new TrustError(`${label}[${String(index)}].sha256 must be a SHA-256 hex digest`)
}
if (!artifacts.some(artifact => artifact.sha256 === object.sha256)) {
throw new TrustError(`${label}[${String(index)}] references a missing Artifact hash`)
}
return Object.freeze({
id: identifier(object.id, `${label}[${String(index)}].id`),
sha256: object.sha256,
})
})
if (new Set(result.map(item => item.id)).size !== result.length) throw new TrustError(`${label} IDs must be unique`)
return Object.freeze(result)
}
export function parseBootstrapManifest(bytes) {
const manifest = parseCommonManifest(bytes, 'bootstrap')
exactKeys(manifest.object, [...manifest.commonKeys, 'bootstrapApi', 'entrypoint'], 'bootstrap manifest')
if (manifest.object.bootstrapApi !== 1) throw new TrustError('bootstrapApi must be 1')
if (typeof manifest.object.entrypoint !== 'string' || !manifest.object.entrypoint.startsWith('/')) {
throw new TrustError('bootstrap entrypoint must be absolute')
}
return Object.freeze({ ...manifest.common, bootstrapApi: 1, entrypoint: manifest.object.entrypoint })
}
export function parseEnvironmentManifest(bytes) {
const manifest = parseCommonManifest(bytes, 'environment')
exactKeys(
manifest.object,
[...manifest.commonKeys, 'bootstrapApi', 'components', 'patches', 'systemPlugins'],
'environment manifest',
)
if (manifest.object.bootstrapApi !== 1) throw new TrustError('environment bootstrapApi must be 1')
return Object.freeze({
...manifest.common,
bootstrapApi: 1,
components: orderedReferences(manifest.object.components, manifest.common.artifacts, 'environment components'),
patches: orderedReferences(manifest.object.patches, manifest.common.artifacts, 'environment patches'),
systemPlugins: orderedReferences(manifest.object.systemPlugins, manifest.common.artifacts, 'environment systemPlugins'),
})
}
export function artifactForReference(manifest, reference) {
const artifact = manifest.artifacts.find(candidate => candidate.sha256 === reference.sha256)
if (artifact === undefined) throw new TrustError(`resource ${JSON.stringify(reference.id)} references a missing Artifact hash`)
return artifact
}
function parseCommonManifest(bytes, expectedType) {
const object = parseJsonDocument(bytes, `${expectedType} manifest`)
const commonKeys = ['artifacts', 'issuedAt', 'keyringGeneration', 'manifestType', 'schema', 'targetSequence', 'version']
if (object.schema !== 1) throw new TrustError(`${expectedType} manifest schema must be 1`)
if (object.manifestType !== expectedType) throw new TrustError(`manifestType must be ${expectedType}`)
return {
object,
commonKeys,
common: {
schema: 1,
manifestType: expectedType,
version: version(object.version, `${expectedType} manifest version`),
keyringGeneration: positiveSafeInteger(object.keyringGeneration, `${expectedType} keyringGeneration`),
targetSequence: positiveSafeInteger(object.targetSequence, `${expectedType} targetSequence`),
issuedAt: isoTimestamp(object.issuedAt, `${expectedType} issuedAt`),
artifacts: parseArtifactList(object.artifacts, `${expectedType} artifacts`),
},
}
}
@@ -0,0 +1,286 @@
import { createHash } from 'node:crypto'
import { canonicalJson } from './canonical-json.mjs'
import { exactKeys, parseJsonDocument, plainObject, TrustError } from './validation.mjs'
export const PLATFORM_LAYOUT = 1
export const BOOTSTRAP_API = 1
export const UPDATE_API = 1
const REFERENCE_KINDS = new Set([
'bootstrap',
'environment',
'pristine',
'runtime',
'system-plugins',
])
const AUTHORITIES = new Set(['stable', 'experimental', 'development'])
const SHA256 = /^[a-f0-9]{64}$/
const IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/
const IMAGE_BUILD_ID = /^sha256:[a-f0-9]{64}$/
function identifier(value, label) {
if (typeof value !== 'string' || !IDENTIFIER.test(value)) throw new TrustError(`${label} is invalid`)
return value
}
function sha256(value, label) {
if (typeof value !== 'string' || !SHA256.test(value)) throw new TrustError(`${label} must be a SHA-256 hex digest`)
return value
}
function imageBuildId(value, label = 'imageBuildId') {
if (typeof value !== 'string' || !IMAGE_BUILD_ID.test(value)) throw new TrustError(`${label} is invalid`)
return value
}
function version(value, label) {
if (typeof value !== 'string' || value === '' || value.length > 128 || /[\0/\\]/.test(value)) {
throw new TrustError(`${label} is invalid`)
}
return value
}
function nonNegativeInteger(value, label) {
if (!Number.isSafeInteger(value) || value < 0) throw new TrustError(`${label} must be a non-negative safe integer`)
return value
}
function positiveInteger(value, label) {
if (!Number.isSafeInteger(value) || value < 1) throw new TrustError(`${label} must be a positive safe integer`)
return value
}
function authority(value, sequence, label = 'authority') {
if (!AUTHORITIES.has(value)) throw new TrustError(`${label} is invalid`)
if (value === 'development' && sequence !== 0) throw new TrustError('development authority requires targetSequence 0')
if (value !== 'development' && sequence === 0) throw new TrustError(`${value} authority requires a positive targetSequence`)
return value
}
function parseBytesOrObject(value, label) {
return Buffer.isBuffer(value) ? parseJsonDocument(value, label) : plainObject(value, label)
}
function contentHash(value) {
return createHash('sha256').update(canonicalJson(value)).digest('hex')
}
export function deriveRecordId(prefix, recordWithoutId) {
identifier(prefix, 'record prefix')
return `${prefix}-${contentHash(recordWithoutId)}`
}
export function deriveImageBuildId(inventoryWithoutId) {
return `sha256:${contentHash(inventoryWithoutId)}`
}
function parseInventoryAsset(value, kind, label) {
const object = plainObject(value, label)
exactKeys(object, ['id', 'sha256'], label)
return Object.freeze({
id: identifier(object.id, `${label}.id`),
sha256: sha256(object.sha256, `${label}.sha256`),
kind,
})
}
export function parseArtifactReference(value, label = 'artifact reference') {
const object = plainObject(value, label)
if (object.storage === 'image') {
exactKeys(object, ['id', 'imageBuildId', 'kind', 'sha256', 'storage'], label)
imageBuildId(object.imageBuildId, `${label}.imageBuildId`)
} else if (object.storage === 'store') {
exactKeys(object, ['id', 'kind', 'sha256', 'storage'], label)
} else {
throw new TrustError(`${label}.storage must be image or store`)
}
if (!REFERENCE_KINDS.has(object.kind)) throw new TrustError(`${label}.kind is invalid`)
return Object.freeze({
storage: object.storage,
...(object.storage === 'image' ? { imageBuildId: object.imageBuildId } : {}),
kind: object.kind,
id: identifier(object.id, `${label}.id`),
sha256: sha256(object.sha256, `${label}.sha256`),
})
}
export function parseImageInventory(value) {
const object = parseBytesOrObject(value, 'image inventory')
exactKeys(object, [
'authority', 'bootstrap', 'bootstrapApi', 'deployment', 'imageBuildId', 'platformRevision',
'schema', 'targetSequence', 'updateApi',
], 'image inventory')
if (object.schema !== 1) throw new TrustError('image inventory schema must be 1')
if (object.bootstrapApi !== BOOTSTRAP_API) throw new TrustError('image inventory bootstrapApi must be 1')
if (object.updateApi !== UPDATE_API) throw new TrustError('image inventory updateApi must be 1')
const targetSequence = nonNegativeInteger(object.targetSequence, 'image inventory targetSequence')
const parsedAuthority = authority(object.authority, targetSequence, 'image inventory authority')
identifier(object.platformRevision, 'image inventory platformRevision')
const bootstrap = plainObject(object.bootstrap, 'image inventory bootstrap')
exactKeys(bootstrap, ['id', 'sha256', 'version'], 'image inventory bootstrap')
const parsedBootstrap = Object.freeze({
version: version(bootstrap.version, 'image inventory bootstrap.version'),
...parseInventoryAsset({ id: bootstrap.id, sha256: bootstrap.sha256 }, 'bootstrap', 'image inventory bootstrap artifact'),
})
const deployment = plainObject(object.deployment, 'image inventory deployment')
exactKeys(deployment, [
'dshVersion', 'environment', 'environmentVersion', 'id', 'pristine', 'runtime', 'systemPlugins',
], 'image inventory deployment')
const parsedDeployment = Object.freeze({
id: identifier(deployment.id, 'image inventory deployment.id'),
dshVersion: version(deployment.dshVersion, 'image inventory deployment.dshVersion'),
environmentVersion: version(deployment.environmentVersion, 'image inventory deployment.environmentVersion'),
environment: parseInventoryAsset(deployment.environment, 'environment', 'image inventory deployment.environment'),
pristine: parseInventoryAsset(deployment.pristine, 'pristine', 'image inventory deployment.pristine'),
runtime: parseInventoryAsset(deployment.runtime, 'runtime', 'image inventory deployment.runtime'),
systemPlugins: parseInventoryAsset(deployment.systemPlugins, 'system-plugins', 'image inventory deployment.systemPlugins'),
})
const withoutId = { ...object }
delete withoutId.imageBuildId
const expected = deriveImageBuildId(withoutId)
imageBuildId(object.imageBuildId)
if (object.imageBuildId !== expected) throw new TrustError('image inventory imageBuildId does not match its canonical content')
return Object.freeze({
schema: 1,
imageBuildId: object.imageBuildId,
authority: parsedAuthority,
platformRevision: object.platformRevision,
targetSequence,
bootstrapApi: BOOTSTRAP_API,
updateApi: UPDATE_API,
bootstrap: parsedBootstrap,
deployment: parsedDeployment,
document: Object.freeze(object),
})
}
function parseRecord(value, label, fields) {
const object = parseBytesOrObject(value, label)
exactKeys(object, ['id', 'schema', ...fields], label)
if (object.schema !== 1) throw new TrustError(`${label} schema must be 1`)
return object
}
function verifyRecordId(object, prefix, label) {
const withoutId = { ...object }
delete withoutId.id
const expected = deriveRecordId(prefix, withoutId)
if (object.id !== expected) throw new TrustError(`${label} id does not match its canonical content`)
return object.id
}
export function parseBootstrapRecord(value) {
const object = parseRecord(value, 'Bootstrap Record', [
'artifact', 'bootstrapApi', 'targetSequence', 'version',
])
if (object.bootstrapApi !== BOOTSTRAP_API) throw new TrustError('Bootstrap Record bootstrapApi must be 1')
const parsed = {
schema: 1,
id: object.id,
version: version(object.version, 'Bootstrap Record version'),
bootstrapApi: BOOTSTRAP_API,
targetSequence: nonNegativeInteger(object.targetSequence, 'Bootstrap Record targetSequence'),
artifact: parseArtifactReference(object.artifact, 'Bootstrap Record artifact'),
}
verifyRecordId(object, 'bootstrap-record', 'Bootstrap Record')
return Object.freeze(parsed)
}
function stringList(value, label) {
if (!Array.isArray(value) || value.some(entry => typeof entry !== 'string' || entry === '' || entry.includes('\0'))) {
throw new TrustError(`${label} must be a string array`)
}
if (new Set(value).size !== value.length) throw new TrustError(`${label} must not contain duplicates`)
return Object.freeze([...value])
}
export function parseDeploymentRecord(value) {
const object = parseRecord(value, 'Deployment Record', [
'authority', 'dshVersion', 'environment', 'environmentVersion', 'pristine', 'receiptTokens',
'runtime', 'snapshotId', 'systemPlugins', 'targetSequence',
])
const targetSequence = nonNegativeInteger(object.targetSequence, 'Deployment Record targetSequence')
const parsed = {
schema: 1,
id: object.id,
authority: authority(object.authority, targetSequence, 'Deployment Record authority'),
targetSequence,
dshVersion: version(object.dshVersion, 'Deployment Record dshVersion'),
environmentVersion: version(object.environmentVersion, 'Deployment Record environmentVersion'),
environment: parseArtifactReference(object.environment, 'Deployment Record environment'),
pristine: parseArtifactReference(object.pristine, 'Deployment Record pristine'),
runtime: parseArtifactReference(object.runtime, 'Deployment Record runtime'),
systemPlugins: parseArtifactReference(object.systemPlugins, 'Deployment Record systemPlugins'),
receiptTokens: stringList(object.receiptTokens, 'Deployment Record receiptTokens'),
snapshotId: object.snapshotId === null ? null : identifier(object.snapshotId, 'Deployment Record snapshotId'),
}
if (parsed.environment.kind !== 'environment' || parsed.pristine.kind !== 'pristine'
|| parsed.runtime.kind !== 'runtime' || parsed.systemPlugins.kind !== 'system-plugins') {
throw new TrustError('Deployment Record references use the wrong kind')
}
const imageIds = [parsed.environment, parsed.pristine, parsed.runtime, parsed.systemPlugins]
.filter(reference => reference.storage === 'image')
.map(reference => reference.imageBuildId)
if (new Set(imageIds).size > 1) throw new TrustError('Deployment Record image references must use one imageBuildId')
verifyRecordId(object, 'deployment-record', 'Deployment Record')
return Object.freeze(parsed)
}
export function parseSlots(value, recordPrefix, label = 'slots') {
const object = parseBytesOrObject(value, label)
exactKeys(object, ['current', 'generation', 'previous', 'schema'], label)
if (object.schema !== 1) throw new TrustError(`${label} schema must be 1`)
const parseId = (entry, field) => entry === null ? null : identifier(entry, `${label}.${field}`)
const current = parseId(object.current, 'current')
const previous = parseId(object.previous, 'previous')
if (current === null) throw new TrustError(`${label}.current must not be null`)
if (!current.startsWith(`${recordPrefix}-`) || (previous !== null && !previous.startsWith(`${recordPrefix}-`))) {
throw new TrustError(`${label} contains an unexpected Record ID`)
}
if (current === previous) throw new TrustError(`${label} current and previous must differ`)
return Object.freeze({ schema: 1, generation: positiveInteger(object.generation, `${label}.generation`), current, previous })
}
function withRecordId(prefix, content) {
const document = { ...content }
return Object.freeze({ ...document, id: deriveRecordId(prefix, document) })
}
export function recordsFromImageInventory(value) {
const inventory = value?.document === undefined ? parseImageInventory(value) : value
const imageReference = asset => ({
storage: 'image',
imageBuildId: inventory.imageBuildId,
kind: asset.kind,
id: asset.id,
sha256: asset.sha256,
})
const bootstrap = withRecordId('bootstrap-record', {
schema: 1,
version: inventory.bootstrap.version,
bootstrapApi: BOOTSTRAP_API,
targetSequence: inventory.targetSequence,
artifact: imageReference(inventory.bootstrap),
})
const deployment = withRecordId('deployment-record', {
schema: 1,
authority: inventory.authority,
targetSequence: inventory.targetSequence,
dshVersion: inventory.deployment.dshVersion,
environmentVersion: inventory.deployment.environmentVersion,
environment: imageReference(inventory.deployment.environment),
pristine: imageReference(inventory.deployment.pristine),
runtime: imageReference(inventory.deployment.runtime),
systemPlugins: imageReference(inventory.deployment.systemPlugins),
receiptTokens: [],
snapshotId: null,
})
return Object.freeze({
bootstrap: parseBootstrapRecord(bootstrap),
deployment: parseDeploymentRecord(deployment),
})
}
@@ -0,0 +1,22 @@
import { readFile } from 'node:fs/promises'
import { durableReplace } from './atomic.mjs'
import { canonicalJson } from './canonical-json.mjs'
export async function readDeploymentStatus(path) {
try {
const value = JSON.parse(await readFile(path, 'utf8'))
return value !== null && typeof value === 'object' && !Array.isArray(value)
? value
: { platformLayout: 1, imageBaseline: null, current: null, imageBehindCurrent: false, recoveryMode: 'status-invalid' }
} catch (error) {
if (error?.code === 'ENOENT') {
return { platformLayout: 1, imageBaseline: null, current: null, imageBehindCurrent: false, recoveryMode: 'status-unavailable' }
}
throw error
}
}
export async function writeDeploymentStatus(path, value) {
await durableReplace(path, canonicalJson(value))
return value
}
@@ -0,0 +1,143 @@
import { execFile } from 'node:child_process'
import { readFile, readdir } from 'node:fs/promises'
import { promisify } from 'node:util'
import { join } from 'node:path'
const execFileAsync = promisify(execFile)
export const DSH_COMPATIBILITY_PACKAGES = Object.freeze({
'@deepseek-ai/dsh-api-remotes': Object.freeze(['.', './client']),
'@deepseek-ai/dsh-app-boot': Object.freeze(['.']),
'@deepseek-ai/dsh-client-connection': Object.freeze(['.', './client']),
'@deepseek-ai/dsh-client-locale': Object.freeze(['.', './client']),
'@deepseek-ai/dsh-client-ui-settings': Object.freeze(['.', './client']),
'@deepseek-ai/dsh-client-ui-theme': Object.freeze(['.', './client']),
'@deepseek-ai/dsh-host-directory-picker-browse': Object.freeze(['.']),
'@deepseek-ai/dsh-llm': Object.freeze(['.', './remote']),
'@deepseek-ai/dsh-settings': Object.freeze(['.']),
'@deepseek-ai/dsh-web-app': Object.freeze(['.', './startup', './cordis.patch.yml']),
})
const DSH_DIRECT_COMPATIBILITY_PACKAGES = Object.freeze([
'@deepseek-ai/dsh-app-boot',
'@deepseek-ai/dsh-web-app',
])
export const DSH_WEB_ROWS = Object.freeze({
connection: '@deepseek-ai/dsh-client-connection',
'plugin-inventory': '@deepseek-ai/dsh-host-plugin-inventory',
'settings-controller': '@deepseek-ai/dsh-api-settings-controller',
webserver: '@deepseek-ai/dsh-host-webserver',
'web-runtime': '@deepseek-ai/dsh-web-app',
})
export const DSH_REMOTE_METHODS = Object.freeze([
'@deepseek-ai/dsh-api-settings-controller#settings/describe',
'@deepseek-ai/dsh-llm#llm/listConfigurableProviders',
'@deepseek-ai/dsh-llm#llm/listProviders',
])
export const DSH_STATIC_CLIENT_MODULES = Object.freeze([
'@deepseek-ai/dsh-client-ui-primitives',
'react',
'react-dom',
])
function packagePath(root, packageName, file = 'package.json') {
return join(root, 'node_modules', ...packageName.split('/'), file)
}
async function json(path) {
return JSON.parse(await readFile(path, 'utf8'))
}
function requireObject(value, description) {
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
throw new Error(`${description} is invalid`)
}
return value
}
function requireText(source, expected, description) {
if (!source.includes(expected)) throw new Error(`${description} is missing ${JSON.stringify(expected)}`)
}
function requireStaticModule(source, name) {
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
const pattern = name === 'react'
? /(?:^|[{,])["']?react["']?:/
: new RegExp(`["']${escaped}["']:`)
if (!pattern.test(source)) throw new Error(`DSH Web static module table is missing ${JSON.stringify(name)}`)
}
function exported(manifest, subpath) {
const exports = requireObject(manifest.exports, `${manifest.name} exports`)
return Object.hasOwn(exports, subpath)
}
function webRowPattern(id, packageName) {
const escapedId = id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
const escapedPackage = packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
return new RegExp(`(?:^|\\n)- id: ${escapedId}\\n(?: {2}[^\\n]*\\n)*? {2}name: ['\"]?${escapedPackage}['\"]?(?:\\n|$)`)
}
export async function verifyDshCompatibility({ packageRoot, expectedVersion, home }) {
if (typeof packageRoot !== 'string' || packageRoot === '') throw new TypeError('DSH package root is required')
if (typeof expectedVersion !== 'string' || expectedVersion === '') throw new TypeError('Expected DSH version is required')
if (typeof home !== 'string' || home === '') throw new TypeError('Temporary DSH home is required')
const manifest = await json(join(packageRoot, 'package.json'))
if (manifest.name !== '@deepseek-ai/dsh') throw new Error('DSH package name is invalid')
if (manifest.version !== expectedVersion) {
throw new Error(`DSH package version ${JSON.stringify(manifest.version)} does not match ${JSON.stringify(expectedVersion)}`)
}
if (manifest.bin?.dsh !== 'lib/bin.js') throw new Error('DSH command entry is not lib/bin.js')
const dependencies = requireObject(manifest.dependencies, 'DSH dependencies')
for (const packageName of DSH_DIRECT_COMPATIBILITY_PACKAGES) {
if (typeof dependencies[packageName] !== 'string') throw new Error(`DSH does not declare ${packageName}`)
}
for (const [packageName, requiredExports] of Object.entries(DSH_COMPATIBILITY_PACKAGES)) {
const dependency = await json(packagePath(packageRoot, packageName))
if (dependency.name !== packageName || dependency.version !== expectedVersion) {
throw new Error(`${packageName} is not installed at DSH version ${expectedVersion}`)
}
for (const subpath of requiredExports) {
if (!exported(dependency, subpath)) throw new Error(`${packageName} does not export ${subpath}`)
}
}
const remotes = await readFile(packagePath(packageRoot, '@deepseek-ai/dsh-api-remotes', 'lib/client.js'), 'utf8')
for (const method of DSH_REMOTE_METHODS) requireText(remotes, method, 'DSH Remote registry')
const frontendRoot = packagePath(packageRoot, '@deepseek-ai/dsh-web-frontend', 'dist/assets')
const frontend = (await Promise.all((await readdir(frontendRoot))
.filter(name => name.endsWith('.js'))
.map(name => readFile(join(frontendRoot, name), 'utf8')))).join('\n')
for (const moduleName of DSH_STATIC_CLIENT_MODULES) {
requireStaticModule(frontend, moduleName)
}
const { stdout, stderr } = await execFileAsync(
process.execPath,
[join(packageRoot, 'lib/bin.js'), '--profile', 'web', '--dump-default-config'],
{
env: { ...process.env, DSH_HOME: home },
maxBuffer: 8 * 1024 * 1024,
timeout: 30_000,
},
)
if (stderr.trim() !== '') throw new Error(`DSH config dump wrote to stderr: ${stderr.trim()}`)
for (const [id, packageName] of Object.entries(DSH_WEB_ROWS)) {
if (!webRowPattern(id, packageName).test(stdout)) {
throw new Error(`DSH Web profile does not contain ${id} (${packageName})`)
}
}
return Object.freeze({
name: manifest.name,
version: manifest.version,
packages: Object.keys(DSH_COMPATIBILITY_PACKAGES).length,
remoteMethods: DSH_REMOTE_METHODS.length,
webRows: Object.keys(DSH_WEB_ROWS).length,
})
}
@@ -0,0 +1,15 @@
import { compareDshVersions } from './supported-target.mjs'
export const OFFICIAL_DSH_PACKAGE = '@deepseek-ai/dsh'
export const OFFICIAL_NPM_REGISTRY = 'https://registry.npmjs.org/'
export const OFFICIAL_DSH_METADATA_LIMIT = 20 * 1024 * 1024
export const OFFICIAL_DSH_TARBALL_LIMIT = 512 * 1024 * 1024
export function officialDshMetadataUrl() {
return new URL(encodeURIComponent(OFFICIAL_DSH_PACKAGE), OFFICIAL_NPM_REGISTRY)
}
export function officialDshTarballUrl(version) {
compareDshVersions(version, version)
return new URL(`${OFFICIAL_DSH_PACKAGE}/-/dsh-${version}.tgz`, OFFICIAL_NPM_REGISTRY)
}
@@ -0,0 +1,98 @@
const LOOPBACK = '127.0.0.1'
export const OUTBOUND_PROXY_PORTS = Object.freeze({
updates: 17891,
platform: 17892,
dshCore: 17893,
dshPlugins: 17894,
agentNetwork: 17895,
managementTerminal: 17896,
modelApi: 17897,
sharedDsh: 17898,
})
export const OUTBOUND_PROXY_SCOPES = Object.freeze(Object.keys(OUTBOUND_PROXY_PORTS))
export const PLATFORM_NO_PROXY = Object.freeze(['localhost', '127.0.0.1', '::1'])
export const OUTBOUND_PROXY_ENVIRONMENT_KEYS = Object.freeze([
'HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy',
'ALL_PROXY', 'all_proxy',
])
export function outboundProxyUrl(scope) {
const port = OUTBOUND_PROXY_PORTS[scope]
if (port === undefined) throw new Error(`outbound proxy scope ${String(scope)} is invalid`)
return `http://${LOOPBACK}:${String(port)}`
}
export function outboundProxyScopeEnabled(configuration, scope, providerId) {
if (configuration?.enabled !== true) return false
if (scope === 'sharedDsh') return configuration.scopes?.dshCore === true || configuration.scopes?.dshPlugins === true
if (scope === 'modelApi') {
if (providerId === undefined) return false
const policy = configuration.modelApi?.providers?.[providerId] ?? configuration.modelApi?.default
return policy?.proxyEnabled === true
}
return configuration.scopes?.[scope] === true
}
export function outboundProxyEnvironment(scope, {
noProxy = PLATFORM_NO_PROXY,
allProxy = false,
enabled = true,
} = {}) {
if (!Array.isArray(noProxy) || noProxy.some(value => typeof value !== 'string' || value.length === 0)) {
throw new Error('outbound proxy NO_PROXY entries are invalid')
}
if (typeof allProxy !== 'boolean') throw new Error('outbound proxy ALL_PROXY setting is invalid')
const proxy = outboundProxyUrl(scope)
const bypass = [...new Set(noProxy)].join(',')
if (typeof enabled !== 'boolean') throw new Error('outbound proxy enabled setting is invalid')
return Object.freeze({
...Object.fromEntries(OUTBOUND_PROXY_ENVIRONMENT_KEYS.map(key => [key, undefined])),
NO_PROXY: bypass,
no_proxy: bypass,
...(enabled ? {
HTTP_PROXY: proxy,
HTTPS_PROXY: proxy,
http_proxy: proxy,
https_proxy: proxy,
...(allProxy ? { ALL_PROXY: proxy, all_proxy: proxy } : {}),
} : {}),
})
}
export function parseOutboundProxyEnvironment(value, scope) {
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
throw new Error('outbound proxy environment response is invalid')
}
const expectedUrl = outboundProxyUrl(scope)
const allowed = new Set([
'HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy',
'NO_PROXY', 'no_proxy', 'ALL_PROXY', 'all_proxy',
])
if (Object.keys(value).some(key => !allowed.has(key))) {
throw new Error('outbound proxy environment response contains unsupported fields')
}
const proxyKeys = ['HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy']
const proxied = proxyKeys.some(key => value[key] !== undefined)
for (const key of proxyKeys) {
if (proxied ? value[key] !== expectedUrl : value[key] !== undefined) {
throw new Error(`outbound proxy environment ${key} is invalid`)
}
}
if (typeof value.NO_PROXY !== 'string' || value.no_proxy !== value.NO_PROXY) {
throw new Error('outbound proxy environment NO_PROXY is invalid')
}
const hasAllProxy = value.ALL_PROXY !== undefined || value.all_proxy !== undefined
if (hasAllProxy && (value.ALL_PROXY !== expectedUrl || value.all_proxy !== expectedUrl)) {
throw new Error('outbound proxy environment ALL_PROXY is invalid')
}
return Object.freeze(Object.fromEntries(Object.entries(value)))
}
export function clearOutboundProxyEnvironment(value = {}) {
return Object.freeze({
...Object.fromEntries(OUTBOUND_PROXY_ENVIRONMENT_KEYS.map(key => [key, undefined])),
...value,
})
}
+155
View File
@@ -0,0 +1,155 @@
import { randomUUID } from 'node:crypto'
import { chmod, chown, lstat, mkdir, rename, rm, symlink } from 'node:fs/promises'
import { dirname, join, resolve } from 'node:path'
const LEGACY_NAMES = Object.freeze([
'bootstrap', 'downloads', 'dsh', 'environments', 'run', 'runtime', 'snapshots', 'system-plugins', 'trust',
])
async function exists(path) {
return lstat(path).then(() => true, error => error?.code === 'ENOENT' ? false : Promise.reject(error))
}
export class PlatformPaths {
constructor(dataRoot = '/data/platform', runRoot = '/run/dsh-platform') {
this.dataRoot = resolve(dataRoot)
this.runRoot = resolve(runRoot)
this.stateRoot = join(this.dataRoot, 'state')
this.storeRoot = join(this.dataRoot, 'store')
this.cacheRoot = join(this.dataRoot, 'cache')
this.logsRoot = join(this.dataRoot, 'logs')
this.trustStateRoot = join(this.stateRoot, 'trust')
this.developmentTrustStateRoot = join(this.trustStateRoot, 'development')
this.bootstrapStateRoot = join(this.stateRoot, 'bootstrap')
this.deploymentStateRoot = join(this.stateRoot, 'deployments')
this.deploymentStatusPath = join(this.deploymentStateRoot, 'status.json')
this.systemSkillStatePath = join(this.deploymentStateRoot, 'skills.json')
this.updaterStateRoot = join(this.stateRoot, 'updater')
this.proxyStateRoot = join(this.stateRoot, 'proxy')
this.accessStateRoot = join(this.stateRoot, 'access')
this.proxyTestStatePath = join(this.proxyStateRoot, 'test-tasks.json')
this.managementStateRoot = join(this.stateRoot, 'management')
this.userPluginStatePath = join(this.managementStateRoot, 'user-plugins.json')
this.userPluginJournalPath = join(this.managementStateRoot, 'user-plugin-transaction.json')
this.proxyProviderInventoryPath = join(this.managementStateRoot, 'proxy-provider-inventory.json')
this.fileTasksRoot = join(this.managementStateRoot, 'file-tasks')
this.maintenanceLeasePath = join(this.managementStateRoot, 'maintenance-mutation.lock')
this.objectsRoot = join(this.storeRoot, 'objects')
this.bootstrapStoreRoot = join(this.storeRoot, 'bootstrap')
this.environmentsRoot = join(this.storeRoot, 'environments')
this.systemSkillCatalogsRoot = join(this.environmentsRoot, 'versions', '.system-skill-catalogs')
this.pristineRoot = join(this.storeRoot, 'pristine')
this.runtimesRoot = join(this.storeRoot, 'runtimes')
this.systemPluginsRoot = join(this.storeRoot, 'system-plugins')
this.snapshotsRoot = join(this.storeRoot, 'snapshots')
this.userPluginSnapshotsRoot = join(this.snapshotsRoot, 'user-plugins')
this.downloadsRoot = join(this.cacheRoot, 'downloads')
this.npmCacheRoot = join(this.cacheRoot, 'npm')
this.viewsRoot = join(this.runRoot, 'views')
this.deploymentViewsRoot = join(this.runRoot, 'deployments')
this.systemPluginViewsRoot = join(this.runRoot, 'system-plugin-views')
this.systemSkillsView = join(this.viewsRoot, 'skills')
this.deploymentView = join(this.runRoot, 'deployment')
this.trustSocket = join(this.runRoot, 'stage0-trust.sock')
this.bootstrapSocket = join(this.runRoot, 'bootstrap.sock')
this.dshLifecycleSocket = join(this.runRoot, 'dsh-lifecycle.sock')
this.managementSocket = join(this.runRoot, 'management.sock')
this.managementCliSocket = join(this.runRoot, 'management-cli.sock')
this.maintenanceSocket = join(this.runRoot, 'maintenance.sock')
this.userSkillSocket = join(this.runRoot, 'user-skills.sock')
this.snapshotSocket = join(this.runRoot, 'snapshot.sock')
this.gatewayAccessSocket = join(this.runRoot, 'gateway-access.sock')
this.accessRunRoot = join(this.runRoot, 'access')
this.accessSocket = join(this.accessRunRoot, 'access.sock')
this.accessRecoverySocket = join(this.accessRunRoot, 'recovery.sock')
this.accessLaunchSocket = join(this.runRoot, 'access-launch.sock')
this.proxyControlSocket = join(this.runRoot, 'outbound-proxy.sock')
this.proxyRoutingStatePath = join(this.runRoot, 'outbound-proxy-routing.json')
this.proxyLaunchSocket = join(this.runRoot, 'proxy-launch.sock')
this.recoverySocket = join(this.runRoot, 'recovery.sock')
}
}
export function trustStateRootForAuthority(paths, authority) {
return authority === 'development' ? paths.developmentTrustStateRoot : paths.trustStateRoot
}
export async function rejectLegacyLayout(paths) {
const found = []
for (const name of LEGACY_NAMES) {
if (await exists(join(paths.dataRoot, name))) found.push(name)
}
if (found.length > 0) {
throw new Error(
`legacy DSH platform volume layout detected (${found.join(', ')}); stop the container and clear only ${paths.dataRoot}, then restart. Do not delete /data/dsh.`,
)
}
}
export async function preparePersistentLayout(paths) {
await mkdir(paths.dataRoot, { recursive: true })
await rejectLegacyLayout(paths)
const directories = [
paths.trustStateRoot, paths.bootstrapStateRoot, paths.deploymentStateRoot, paths.updaterStateRoot,
paths.managementStateRoot, paths.fileTasksRoot,
paths.objectsRoot, paths.bootstrapStoreRoot, paths.environmentsRoot, paths.pristineRoot,
paths.runtimesRoot, paths.systemPluginsRoot, paths.snapshotsRoot, paths.userPluginSnapshotsRoot,
paths.downloadsRoot, paths.npmCacheRoot, paths.logsRoot,
]
await Promise.all(directories.map(path => mkdir(path, { recursive: true })))
return Object.freeze([...directories])
}
export async function resetRuntimeLayout(paths) {
if (paths.runRoot === '/' || paths.runRoot === paths.dataRoot) throw new Error('platform run root is unsafe')
await rm(paths.runRoot, { recursive: true, force: true })
await mkdir(paths.viewsRoot, { recursive: true })
await mkdir(paths.deploymentViewsRoot, { recursive: true })
await mkdir(paths.systemPluginViewsRoot, { recursive: true })
await mkdir(paths.systemSkillsView, { recursive: true })
for (const name of ['environment', 'runtime']) {
await symlink(join('..', 'deployment', name), join(paths.viewsRoot, name), 'dir')
}
await symlink(join('..', 'system-plugin-views', 'current'), join(paths.viewsRoot, 'system-plugins'), 'dir')
await symlink(join('..', 'deployment', 'system-plugins'), join(paths.systemPluginViewsRoot, 'current'), 'dir')
if (process.getuid?.() === 0) {
await chown(paths.runRoot, 0, 1000)
// Isolated service identities must traverse to their dedicated subtrees and
// immutable Bootstrap entrypoints without gaining the node group's socket access.
await chmod(paths.runRoot, 0o1771)
await chown(paths.viewsRoot, 0, 0)
await chmod(paths.viewsRoot, 0o755)
await chown(paths.deploymentViewsRoot, 1000, 1000)
await chmod(paths.deploymentViewsRoot, 0o755)
await chown(paths.systemPluginViewsRoot, 1000, 1000)
await chmod(paths.systemPluginViewsRoot, 0o755)
await chown(paths.systemSkillsView, 1000, 1000)
await chmod(paths.systemSkillsView, 0o755)
}
}
export async function replaceRuntimeView(paths, name, target) {
if (name !== 'bootstrap') {
throw new Error(`runtime view ${name} is invalid`)
}
const path = join(paths.viewsRoot, name)
const temporary = join(dirname(path), `.${name}.${randomUUID()}.tmp`)
await symlink(resolve(target), temporary, 'dir')
await rename(temporary, path)
return path
}
export async function replaceDeploymentView(paths, target) {
const temporary = join(paths.runRoot, `.deployment.${randomUUID()}.tmp`)
await symlink(resolve(target), temporary, 'dir')
await rename(temporary, paths.deploymentView)
return paths.deploymentView
}
export async function replaceSystemPluginView(paths, target = join(paths.deploymentView, 'system-plugins')) {
const path = join(paths.systemPluginViewsRoot, 'current')
const temporary = join(paths.systemPluginViewsRoot, `.current.${randomUUID()}.tmp`)
await symlink(resolve(target), temporary, 'dir')
await rename(temporary, path)
return path
}
@@ -0,0 +1,92 @@
import { exactKeys, parseJsonDocument, TrustError } from './validation.mjs'
const SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/
function parseSemver(value, label) {
if (typeof value !== 'string') throw new TrustError(`${label} must be a semantic version`)
const match = SEMVER.exec(value)
if (match === null) throw new TrustError(`${label} must be a semantic version`)
const prerelease = match[4] === undefined ? [] : match[4].split('.')
for (const identifier of prerelease) {
if (/^\d+$/.test(identifier) && identifier.length > 1 && identifier.startsWith('0')) {
throw new TrustError(`${label} has an invalid numeric prerelease identifier`)
}
}
return {
core: match.slice(1, 4),
prerelease,
}
}
function compareNumeric(left, right) {
if (left.length !== right.length) return left.length < right.length ? -1 : 1
if (left === right) return 0
return left < right ? -1 : 1
}
function compareIdentifier(left, right) {
const leftNumeric = /^\d+$/.test(left)
const rightNumeric = /^\d+$/.test(right)
if (leftNumeric && rightNumeric) return compareNumeric(left, right)
if (leftNumeric !== rightNumeric) return leftNumeric ? -1 : 1
if (left === right) return 0
return left < right ? -1 : 1
}
export function compareSemanticVersions(leftValue, rightValue, leftLabel = 'left version', rightLabel = 'right version') {
const left = parseSemver(leftValue, leftLabel)
const right = parseSemver(rightValue, rightLabel)
for (let index = 0; index < left.core.length; index += 1) {
const compared = compareNumeric(left.core[index], right.core[index])
if (compared !== 0) return compared
}
if (left.prerelease.length === 0 || right.prerelease.length === 0) {
return left.prerelease.length === right.prerelease.length ? 0 : left.prerelease.length === 0 ? 1 : -1
}
const length = Math.max(left.prerelease.length, right.prerelease.length)
for (let index = 0; index < length; index += 1) {
if (left.prerelease[index] === undefined) return -1
if (right.prerelease[index] === undefined) return 1
const compared = compareIdentifier(left.prerelease[index], right.prerelease[index])
if (compared !== 0) return Math.sign(compared)
}
return 0
}
export function compareDshVersions(leftValue, rightValue) {
return compareSemanticVersions(leftValue, rightValue, 'left DSH version', 'right DSH version')
}
export function parseSupportedTarget(bytes) {
const object = parseJsonDocument(bytes, 'supported target')
exactKeys(object, ['environment', 'latestSupportedDsh', 'schema'], 'supported target')
if (object.schema !== 1) throw new TrustError('supported target schema must be 1')
parseSemver(object.latestSupportedDsh, 'latestSupportedDsh')
if (typeof object.environment !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(object.environment)) {
throw new TrustError('supported target environment is invalid')
}
return Object.freeze({
schema: 1,
latestSupportedDsh: object.latestSupportedDsh,
environment: object.environment,
})
}
export function validateSupportedTarget(targetBytes, environmentDefinitionBytes) {
const target = parseSupportedTarget(targetBytes)
const definition = parseJsonDocument(environmentDefinitionBytes, 'environment definition')
if (definition.version !== target.environment) {
throw new TrustError(`supported Environment ${JSON.stringify(target.environment)} does not match definition ${JSON.stringify(definition.version)}`)
}
return target
}
export function advanceSupportedDsh(target, upstreamVersion) {
const comparison = compareDshVersions(upstreamVersion, target.latestSupportedDsh)
if (comparison < 0) throw new TrustError('upstream DSH version would roll back Latest Supported')
if (comparison === 0) return Object.freeze({ changed: false, target })
return Object.freeze({
changed: true,
target: Object.freeze({ ...target, latestSupportedDsh: upstreamVersion }),
})
}
@@ -0,0 +1,45 @@
import { createHash } from 'node:crypto'
import { lstat, readFile, readdir, readlink } from 'node:fs/promises'
import { join } from 'node:path'
import { canonicalJson } from './canonical-json.mjs'
import { TrustError } from './validation.mjs'
async function entries(root, relative = '', includeMode = true) {
const names = await readdir(join(root, relative))
const result = []
for (const name of names.sort()) {
const path = relative === '' ? name : `${relative}/${name}`
const absolute = join(root, path)
const details = await lstat(absolute)
const mode = details.mode & 0o777
if (details.isDirectory()) {
result.push({ path, type: 'directory', ...(includeMode ? { mode } : {}) })
result.push(...await entries(root, path, includeMode))
} else if (details.isFile()) {
result.push({
path,
type: 'file',
...(includeMode ? { mode } : {}),
sha256: createHash('sha256').update(await readFile(absolute)).digest('hex'),
size: details.size,
})
} else if (details.isSymbolicLink()) {
const target = await readlink(absolute)
if (target.includes('\0')) throw new TrustError(`tree symlink ${path} is invalid`)
result.push({ path, type: 'symlink', ...(includeMode ? { mode } : {}), target })
} else {
throw new TrustError(`tree entry ${path} has an unsupported type`)
}
}
return result
}
export async function hashTree(root) {
const manifest = { schema: 1, entries: await entries(root) }
return createHash('sha256').update(canonicalJson(manifest)).digest('hex')
}
export async function hashTreeContent(root) {
const manifest = { schema: 1, entries: await entries(root, '', false) }
return createHash('sha256').update(canonicalJson(manifest)).digest('hex')
}
@@ -0,0 +1,49 @@
export class TrustError extends Error {
constructor(message, code = 'TRUST_INVALID') {
super(message)
this.name = 'TrustError'
this.code = code
}
}
export function plainObject(value, label) {
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
throw new TrustError(`${label} must be an object`)
}
return value
}
export function exactKeys(value, expected, label) {
const actual = Object.keys(value).sort()
const wanted = [...expected].sort()
if (actual.length !== wanted.length || actual.some((key, index) => key !== wanted[index])) {
throw new TrustError(`${label} fields must be exactly: ${wanted.join(', ')}`)
}
}
export function positiveSafeInteger(value, label) {
if (!Number.isSafeInteger(value) || value < 1) {
throw new TrustError(`${label} must be a positive safe integer`)
}
return value
}
export function isoTimestamp(value, label) {
if (typeof value !== 'string') throw new TrustError(`${label} must be an ISO timestamp`)
const parsed = new Date(value)
if (!Number.isFinite(parsed.valueOf()) || parsed.toISOString() !== value) {
throw new TrustError(`${label} must be a canonical ISO timestamp`)
}
return value
}
export function parseJsonDocument(bytes, label) {
if (!Buffer.isBuffer(bytes)) throw new TrustError(`${label} must be bytes`)
let value
try {
value = JSON.parse(bytes.toString('utf8'))
} catch {
throw new TrustError(`${label} must contain valid JSON`)
}
return plainObject(value, label)
}
+11
View File
@@ -0,0 +1,11 @@
{
"name": "dsh-docker-platform",
"private": true,
"type": "module",
"scripts": {
"test": "node --test"
},
"engines": {
"node": ">=24"
}
}
@@ -0,0 +1,2 @@
This public trust fixture is for local builds and tests only.
Release workflows must replace it with an offline Recovery-signed seed and reject this marker.
@@ -0,0 +1 @@
{"current":{"algorithm":"Ed25519","keyId":"67d0eaa2e221472b07bb7e090f13fbc33662f00b1b95b77650aa98fcbfc3917c","publicKey":"MCowBQYDK2VwAyEAloaqTWysQwhw98hQc+bhcd7zjvngnhLw9fjaC8X28Ho="},"generation":1,"issuedAt":"2026-08-19T00:00:00.000Z","next":{"algorithm":"Ed25519","keyId":"1fe144dde06c2525dcf73b50363d051d821d9ddc80f6c2954a0a9a9a23d07cf5","publicKey":"MCowBQYDK2VwAyEAz4DKA6dm2sx/zmQA96MJjJ3xWEIsYZRVLw+ebFc/kAY="},"revokedKeyIds":[],"schema":1}
@@ -0,0 +1 @@
{"algorithm":"Ed25519","keyId":"e40cdc209514de354c2c1976bf3476ef21753058c73ba9bf373a20f201860fd6","schema":1,"signature":"WXErXfkHnhprgnywUB4ukhizyqqBBBAoxQE9WDN4Q452lz1nm867eBrUsKpMfrh7DE0OU7Z+FN4g1leCEawiBg=="}
@@ -0,0 +1 @@
MCowBQYDK2VwAyEA2mTS4kmUv3mIxqkJkQc949uZ7mOLtPT2g7iWU7mstRM=
@@ -0,0 +1,372 @@
#!/usr/bin/env node
import { chmod, chown, mkdir, readFile } from 'node:fs/promises'
import { randomBytes } from 'node:crypto'
import { join } from 'node:path'
import { TrustLedger } from './lib/ledger.mjs'
import { VerifiedObjectStore } from './lib/artifacts.mjs'
import { BootstrapManager } from './lib/slots.mjs'
import { BootstrapSupervisor } from './lib/supervisor.mjs'
import { createTrustServer, listenUnix } from './lib/trust-server.mjs'
import { provisionPlatformSeed } from './lib/seed.mjs'
import {
PlatformPaths,
preparePersistentLayout,
replaceRuntimeView,
resetRuntimeLayout,
trustStateRootForAuthority,
} from '../lib/paths.mjs'
import { parseImageInventory, recordsFromImageInventory } from '../lib/deployment-contracts.mjs'
import { readDeploymentStatus } from '../lib/deployment-status.mjs'
import { createRecoveryServer, listenRecovery } from './lib/recovery-server.mjs'
import { JsonlLogManager } from '../../control-plane/modules/log-manager/index.mjs'
import { LocalApiClient } from '../../control-plane/modules/updater/lib/client.mjs'
import { consumeInternalCapability } from '../lib/access-capability.mjs'
import { collectAccessEvidence } from '../lib/access-evidence.mjs'
import { createMaintenanceServer, listenMaintenance } from './lib/maintenance-server.mjs'
import { prepareUserSkillRoots, UserSkillManager } from '../../control-plane/services/management/user-skills.mjs'
import { PersistentStateSnapshots } from '../../control-plane/modules/updater/lib/snapshots.mjs'
import { UserPluginSnapshots } from '../../control-plane/modules/plugin-manager/user-snapshots.mjs'
import { createSnapshotServer, listenSnapshots } from './lib/snapshot-server.mjs'
import { createUserSkillServer, listenUserSkills } from './lib/user-skill-server.mjs'
import { prepareUserWritableRoots } from './lib/user-roots.mjs'
import {
ProxyLaunchBroker,
createProxyLaunchServer,
listenProxyLaunch,
} from './lib/proxy-launch-server.mjs'
import {
AccessLaunchBroker,
createAccessLaunchServer,
listenAccessLaunch,
} from './lib/access-launch-server.mjs'
import {
clearOutboundProxyEnvironment,
outboundProxyEnvironment,
outboundProxyScopeEnabled,
parseOutboundProxyEnvironment,
} from '../lib/outbound-proxy.mjs'
const dataRoot = process.env.DSH_PLATFORM_DATA ?? '/data/platform'
const runRoot = process.env.DSH_PLATFORM_RUN ?? '/run/dsh-platform'
const paths = new PlatformPaths(dataRoot, runRoot)
const seedRoot = process.env.DSH_PLATFORM_SEED ?? '/opt/dsh-platform/seed'
const dshHome = process.env.DSH_HOME ?? '/data/dsh'
const defaultWorkspace = process.env.DSH_DEFAULT_WORKSPACE ?? '/workspace'
const agentsHome = process.env.DSH_AGENTS_HOME ?? '/home/node/.agents'
const inventory = parseImageInventory(await readFile(join(seedRoot, 'inventory.json')))
const proxyUid = 991
const proxyGid = 991
const accessUid = 992
const accessGid = 992
const platformGid = 1000
const imageRecords = recordsFromImageInventory(inventory)
const recoveryPublicKey = (await readFile(join(seedRoot, 'trust', 'recovery-root.spki.base64'), 'utf8')).trim()
const trustStateRoot = trustStateRootForAuthority(paths, inventory.authority)
const accessEvidence = await collectAccessEvidence({
dshHome,
paths,
legacyAuthenticationConfigured: Boolean(
process.env.DSH_PROXY_USERNAME || process.env.DSH_PROXY_PASSWORD || process.env.DSH_PLATFORM_PASSWORD,
),
})
await preparePersistentLayout(paths)
const logs = new JsonlLogManager({
root: paths.logsRoot,
maxBytes: Number(process.env.DSH_LOG_MAX_BYTES ?? 104857600),
retentionDays: Number(process.env.DSH_LOG_RETENTION_DAYS ?? 14),
output: { stdout: process.stdout, stderr: process.stderr },
fileMode: 0o660,
fileGid: process.getgid?.() === 0 ? 1000 : undefined,
})
const rateLimitedAccessMessages = new Set([
'access.authentication.failed',
'access.fresh-authentication.failed',
'access.totp.failed',
])
const auditedAccessMessages = new Set([
'access.authentication-reset-key.generated',
'access.authentication-reset.completed',
'access.authentication-reset.failed',
'access.authentication-retry.cleared',
'access.recovery-account.changed',
'access.recovery-operation.failed',
])
const reportAccess = async (message, fields = {}) => {
if (rateLimitedAccessMessages.has(message)) {
await logs.diagnosticRateLimited(
`${message}:${fields.kind === 'management' ? 'management' : 'main'}`,
'access-manager', message, fields,
)
} else {
await logs.diagnostic('access-manager', message, fields)
}
if (auditedAccessMessages.has(message)) await logs.audit(message, fields)
}
await logs.prepare()
logs.on('error', error => { void logs.diagnostic('log-manager', 'capture.failed', { error }) })
const startup = async (phase, operation) => {
try {
return await operation()
} catch (error) {
await logs.diagnostic('stage0', 'stage0.startup.failed', { error, phase })
throw error
}
}
await logs.diagnostic('stage0', 'stage0.starting', {
imageBuildId: inventory.imageBuildId,
targetSequence: inventory.targetSequence,
})
await startup('runtime-layout', () => resetRuntimeLayout(paths))
await startup('user-writable-roots', () => prepareUserWritableRoots({
dshHome,
defaultWorkspace,
uid: process.getuid?.() === 0 ? 1000 : undefined,
gid: process.getgid?.() === 0 ? 1000 : undefined,
}))
await startup('user-skill-roots', () => prepareUserSkillRoots({
dshHome,
agentsHome,
uid: process.getuid?.() === 0 ? 1000 : undefined,
gid: process.getgid?.() === 0 ? 1000 : undefined,
}))
const ledger = new TrustLedger(trustStateRoot, recoveryPublicKey)
const objects = new VerifiedObjectStore({
objectRoot: paths.objectsRoot,
receiptRoot: join(trustStateRoot, 'receipts'),
untrustedRoot: paths.downloadsRoot,
ledger,
})
await startup('seed-provision', () => provisionPlatformSeed(seedRoot, paths))
const slots = new BootstrapManager({
stateRoot: paths.bootstrapStateRoot,
storeRoot: paths.bootstrapStoreRoot,
seedRoot,
inventory,
})
await startup('bootstrap-reconcile', () => slots.reconcileImage(imageRecords.bootstrap))
const currentBootstrap = await startup('bootstrap-resolve', () => slots.current())
await startup('bootstrap-view', () => replaceRuntimeView(paths, 'bootstrap', currentBootstrap.path))
await startup('proxy-state-root', async () => {
await mkdir(paths.proxyStateRoot, { recursive: true })
if (process.getuid?.() === 0) await chown(paths.proxyStateRoot, proxyUid, proxyGid)
await chmod(paths.proxyStateRoot, process.getuid?.() === 0 ? 0o700 : 0o700)
})
await startup('access-state-root', async () => {
await mkdir(paths.accessStateRoot, { recursive: true })
if (process.getuid?.() === 0) await chown(paths.accessStateRoot, accessUid, accessGid)
await chmod(paths.accessStateRoot, 0o700)
})
await startup('access-run-root', async () => {
await mkdir(paths.accessRunRoot, { recursive: true })
if (process.getuid?.() === 0) await chown(paths.accessRunRoot, accessUid, platformGid)
await chmod(paths.accessRunRoot, process.getuid?.() === 0 ? 0o710 : 0o700)
})
const accessLaunchToken = randomBytes(32).toString('base64url')
const accessBroker = new AccessLaunchBroker({
token: accessLaunchToken,
dataRoot,
runRoot,
script: join(paths.viewsRoot, 'bootstrap', 'control-plane', 'services', 'access-manager', 'index.mjs'),
accessSocket: paths.accessSocket,
recoverySocket: paths.accessRecoverySocket,
uid: accessUid,
gid: accessGid,
platformGid,
capture: (child, source, declaration) => logs.capture(child, source, declaration),
report: reportAccess,
})
const accessLaunchServer = createAccessLaunchServer({
broker: accessBroker,
token: accessLaunchToken,
report: (message, fields) => logs.diagnostic('stage0', message, fields),
})
await startup('access-launch-api', () => listenAccessLaunch(accessLaunchServer, paths.accessLaunchSocket, platformGid))
await logs.diagnostic('stage0', 'access-launch-api.ready')
const proxyLaunchToken = randomBytes(32).toString('base64url')
const proxyBroker = new ProxyLaunchBroker({
token: proxyLaunchToken,
dataRoot,
runRoot,
script: join(paths.viewsRoot, 'bootstrap', 'control-plane', 'services', 'outbound-proxy', 'index.mjs'),
controlSocket: paths.proxyControlSocket,
uid: proxyUid,
gid: proxyGid,
platformGid,
capture: (child, source, declaration) => logs.capture(child, source, declaration),
report: (message, fields) => logs.diagnostic('outbound-proxy', message, fields),
})
const proxyLaunchServer = createProxyLaunchServer({
broker: proxyBroker,
token: proxyLaunchToken,
report: (message, fields) => logs.diagnostic('stage0', message, fields),
})
await startup('proxy-launch-api', () => listenProxyLaunch(proxyLaunchServer, paths.proxyLaunchSocket, platformGid))
await logs.diagnostic('stage0', 'proxy-launch-api.ready')
const seedKeyring = await readFile(join(seedRoot, 'trust', 'keyring.json'))
const seedSignature = JSON.parse(await readFile(join(seedRoot, 'trust', 'keyring.sig.json'), 'utf8'))
await startup('keyring', () => ledger.acceptKeyring(seedKeyring, seedSignature))
const supervisor = new BootstrapSupervisor({
slots,
dataRoot,
runRoot,
paths,
uid: process.getuid?.() === 0 ? 1000 : undefined,
gid: process.getgid?.() === 0 ? 1000 : undefined,
user: process.getuid?.() === 0 ? 'node' : undefined,
home: process.getuid?.() === 0 ? '/home/node' : undefined,
proxyLaunchToken,
accessLaunchToken,
accessEvidence,
entrypoint: 'platform/bootstrap/index.mjs',
seedRoot,
report: (message, fields) => logs.diagnostic('stage0', message, fields),
})
const trustServer = createTrustServer({
ledger,
objects,
stageBootstrap: async (receipt, version) => {
const packageObject = await objects.bootstrapPackage(receipt, version)
const staged = await slots.stageArchive(packageObject.path, {
version,
targetSequence: packageObject.receipt.targetSequence,
})
await logs.diagnostic('stage0', 'bootstrap.staged', {
bootstrapVersion: version,
recordId: staged.record.id,
targetSequence: staged.record.targetSequence,
recordChanged: staged.recordChanged,
restartRequired: staged.restartRequired,
})
if (staged.restartRequired) setImmediate(() => {
void supervisor.restart().catch(error => logs.diagnostic('stage0', 'bootstrap.activation.failed', {
error,
bootstrapVersion: version,
}))
})
return Object.freeze({
recordId: staged.record.id,
targetSequence: staged.record.targetSequence,
recordChanged: staged.recordChanged,
restartRequired: staged.restartRequired,
})
},
collectBootstrap: () => slots.collectGarbage(),
report: (message, fields) => logs.diagnostic('stage0-trust', message, fields),
})
await startup('trust-api', () => listenUnix(trustServer, paths.trustSocket, {
mode: process.getuid?.() === 0 ? 0o660 : 0o600,
uid: process.getuid?.() === 0 ? 0 : undefined,
gid: process.getgid?.() === 0 ? 1000 : undefined,
}))
await logs.diagnostic('stage0', 'trust-api.ready')
const management = new LocalApiClient(paths.managementSocket)
const outboundProxy = new LocalApiClient(paths.proxyControlSocket)
const access = new LocalApiClient(paths.accessSocket)
const maintenance = await createMaintenanceServer({
paths,
dshHome,
defaultWorkspace,
terminalEnvironment: async () => {
try {
const result = await outboundProxy.request('GET', '/v1/environment?scope=managementTerminal')
return clearOutboundProxyEnvironment(parseOutboundProxyEnvironment(result.environment, 'managementTerminal'))
} catch (error) {
await logs.diagnostic('stage0', 'outbound-proxy.terminal-environment.fallback', {
error,
level: 'warning',
})
try {
const state = JSON.parse(await readFile(paths.proxyRoutingStatePath, 'utf8'))
return outboundProxyEnvironment('managementTerminal', {
enabled: outboundProxyScopeEnabled(state, 'managementTerminal'),
})
} catch {
return outboundProxyEnvironment('managementTerminal')
}
}
},
authorize: async request => {
const token = request.headers['x-dsh-internal-capability']
if (typeof token !== 'string') return false
return consumeInternalCapability(access, {
token, audience: 'maintenance', method: request.method ?? 'GET', target: request.url ?? '/',
})
},
platformBusy: async () => {
try {
const status = await management.request('GET', '/_dsh_platform/api/v1/status')
return !['idle', 'success', 'failed'].includes(status.update?.status)
|| ['starting', 'stopping', 'restarting', 'recovering'].includes(status.dshLifecycle?.state)
|| status.runtimeReset?.status === 'resetting'
|| status.systemPluginOperation?.status === 'running'
|| status.userPluginOperation?.status === 'running'
} catch { return true }
},
report: (message, fields) => logs.diagnostic('maintenance', message, fields),
fileReport: (message, fields) => logs.diagnostic('file-manager', message, fields),
terminalReport: (message, fields) => logs.diagnostic('terminal', message, fields),
audit: (message, fields) => logs.diagnostic('audit', message, { stream: 'audit', ...fields }),
})
await startup('maintenance-api', () => listenMaintenance(maintenance.server, paths.maintenanceSocket))
await logs.diagnostic('stage0', 'maintenance-api.ready', { privileged: true })
const snapshotServer = createSnapshotServer({
dshSnapshots: new PersistentStateSnapshots({ root: paths.snapshotsRoot, sourceRoot: dshHome }),
userPluginSnapshots: new UserPluginSnapshots({
root: paths.userPluginSnapshotsRoot,
profileRoot: join(dshHome, 'profiles', 'web'),
}),
report: (message, fields) => logs.diagnostic('snapshot-manager', message, fields),
})
await startup('snapshot-api', () => listenSnapshots(snapshotServer, paths.snapshotSocket))
await logs.diagnostic('stage0', 'snapshot-api.ready', { privileged: true })
const userSkillServer = createUserSkillServer({
manager: new UserSkillManager({ dshHome, agentsHome }),
report: (message, fields) => logs.diagnostic('user-skill-manager', message, fields),
})
await startup('user-skill-api', () => listenUserSkills(userSkillServer, paths.userSkillSocket))
await logs.diagnostic('stage0', 'user-skill-api.ready', { privileged: true })
await startup('bootstrap', () => supervisor.startWithRollback())
const recoveryServer = createRecoveryServer({
inventory,
deployments: () => readDeploymentStatus(paths.deploymentStatusPath),
supervisor,
report: (message, fields) => logs.diagnostic('stage0-recovery', message, fields),
})
await startup('recovery-api', () => listenRecovery(recoveryServer, paths.recoverySocket))
await logs.diagnostic('stage0', 'stage0.ready', {
bootstrapRecordId: (await slots.state()).current,
recoveryApi: true,
trustApi: true,
})
let resolveSignal
const signal = new Promise(resolve => { resolveSignal = resolve })
const onSignal = value => resolveSignal({ type: 'signal', signal: value })
process.once('SIGINT', () => onSignal('SIGINT'))
process.once('SIGTERM', () => onSignal('SIGTERM'))
const outcome = await Promise.race([
signal,
supervisor.fatal.then(error => ({ type: 'exit', error })),
])
if (outcome.type === 'exit') await logs.diagnostic('stage0', 'stage0.fatal', { error: outcome.error })
else await logs.diagnostic('stage0', 'stage0.stopping', { signal: outcome.signal })
trustServer.close()
recoveryServer.close()
await maintenance.terminal.shutdown()
await new Promise(resolve => maintenance.server.close(resolve))
await new Promise(resolve => snapshotServer.close(resolve))
await new Promise(resolve => userSkillServer.close(resolve))
try {
await supervisor.stop()
} catch (error) {
await logs.diagnostic('stage0', 'stage0.stop.failed', { error })
throw error
}
await proxyBroker.stop()
await new Promise(resolve => proxyLaunchServer.close(resolve))
await accessBroker.stop()
await new Promise(resolve => accessLaunchServer.close(resolve))
await logs.diagnostic('stage0', 'stage0.stopped')
if (outcome.type === 'exit') {
throw outcome.error
}
@@ -0,0 +1,223 @@
import { spawn } from 'node:child_process'
import { timingSafeEqual } from 'node:crypto'
import { once } from 'node:events'
import { createServer } from 'node:http'
import { chmod, chown, mkdir, rm } from 'node:fs/promises'
import { dirname } from 'node:path'
const MAX_BODY_BYTES = 1024
function sameToken(left, right) {
const a = Buffer.from(left ?? '')
const b = Buffer.from(right ?? '')
return a.byteLength === b.byteLength && a.byteLength > 0 && timingSafeEqual(a, b)
}
function send(response, status, value) {
response.writeHead(status, { 'content-type': 'application/json; charset=utf-8' })
response.end(`${JSON.stringify(value)}\n`)
}
async function body(request) {
const chunks = []
let size = 0
for await (const chunk of request) {
size += chunk.byteLength
if (size > MAX_BODY_BYTES) throw new Error('access launch request body is too large')
chunks.push(chunk)
}
return size === 0 ? {} : JSON.parse(Buffer.concat(chunks).toString('utf8'))
}
export function accessLaunchCommand({ node, script, uid, gid }) {
if (!Number.isInteger(uid) || uid < 1 || !Number.isInteger(gid) || gid < 1) {
throw new Error('Access Manager UID and GID must be positive integers')
}
return {
executable: '/usr/bin/setpriv',
args: [
`--reuid=${String(uid)}`,
`--regid=${String(gid)}`,
'--clear-groups',
'--', node, script,
],
}
}
export function accessLaunchEnvironment({ environment = process.env, dataRoot, runRoot, classificationToken }) {
return {
HOME: '/nonexistent',
USER: 'dsh-access',
LOGNAME: 'dsh-access',
PATH: environment.PATH ?? '/usr/local/bin:/usr/bin:/bin',
DSH_PLATFORM_DATA: dataRoot,
DSH_PLATFORM_RUN: runRoot,
DSH_ACCESS_CLASSIFICATION_TOKEN: classificationToken,
}
}
export class AccessLaunchBroker {
constructor({
token,
dataRoot,
runRoot,
script,
accessSocket,
recoverySocket,
uid,
gid,
platformGid,
node = process.execPath,
spawnImpl = spawn,
capture = () => {},
report = async () => {},
readyTimeoutMs = 10_000,
}) {
this.token = token
this.dataRoot = dataRoot
this.runRoot = runRoot
this.script = script
this.accessSocket = accessSocket
this.recoverySocket = recoverySocket
this.uid = uid
this.gid = gid
this.platformGid = platformGid
this.node = node
this.spawnImpl = spawnImpl
this.capture = capture
this.report = report
this.readyTimeoutMs = readyTimeoutMs
this.child = undefined
this.ready = false
}
status() {
const running = this.child !== undefined
&& this.child.exitCode === null
&& this.child.signalCode === null
return {
running,
componentReady: running && this.ready,
pid: running ? this.child.pid ?? null : null,
}
}
async start() {
if (this.status().running) return this.status()
const command = accessLaunchCommand({ node: this.node, script: this.script, uid: this.uid, gid: this.gid })
const child = this.spawnImpl(command.executable, command.args, {
env: accessLaunchEnvironment({
dataRoot: this.dataRoot, runRoot: this.runRoot, classificationToken: this.token,
}),
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
})
this.child = child
this.ready = false
this.capture(child, 'access-manager', { stdout: true, stderr: true })
const ready = new Promise((resolve, reject) => {
child.once('error', reject)
child.once('exit', (code, signal) => reject(new Error(
`Access Manager exited before readiness (code=${String(code)}, signal=${String(signal)})`,
)))
child.on('message', message => {
if (message?.type === 'ready' && message.componentReady === true) resolve()
if (message?.type === 'diagnostic' && typeof message.message === 'string') {
void this.report(message.message, message.fields ?? {})
}
})
})
let timer
try {
await Promise.race([
ready,
new Promise((_, reject) => {
timer = setTimeout(() => reject(new Error('Access Manager readiness timed out')), this.readyTimeoutMs)
timer.unref?.()
}),
])
if (process.getuid?.() === 0) {
await chown(this.accessSocket, this.uid, this.platformGid)
await chmod(this.accessSocket, 0o660)
await chown(this.recoverySocket, this.uid, this.gid)
await chmod(this.recoverySocket, 0o600)
}
this.ready = true
await this.report('access-manager.launch.completed', { pid: child.pid ?? null })
child.once('exit', (code, signal) => {
if (this.child !== child) return
this.child = undefined
this.ready = false
void this.report('access-manager.exited', {
level: code === 0 || signal === 'SIGTERM' ? 'info' : 'error', code, signal,
})
})
return this.status()
} catch (error) {
if (child.exitCode === null && child.signalCode === null) {
const exited = once(child, 'exit')
child.kill('SIGTERM')
await exited.catch(() => {})
}
if (this.child === child) this.child = undefined
this.ready = false
await this.report('access-manager.launch.failed', { error })
throw error
} finally {
clearTimeout(timer)
}
}
async stop() {
const child = this.child
if (child === undefined || child.exitCode !== null || child.signalCode !== null) {
this.child = undefined
this.ready = false
return this.status()
}
const exited = once(child, 'exit')
child.kill('SIGTERM')
const graceful = await Promise.race([
exited.then(() => true),
new Promise(resolve => setTimeout(() => resolve(false), 5_000)),
])
if (!graceful && child.exitCode === null && child.signalCode === null) {
child.kill('SIGKILL')
await once(child, 'exit')
}
if (this.child === child) this.child = undefined
this.ready = false
await this.report('access-manager.stop.completed')
return this.status()
}
}
export function createAccessLaunchServer({ broker, token, report = async () => {} }) {
return createServer((request, response) => {
void (async () => {
const pathname = new URL(request.url ?? '/', 'http://access-launch.internal').pathname
if (request.method === 'GET' && pathname === '/v1/status') return send(response, 200, broker.status())
if (request.method !== 'POST' || !['/v1/start', '/v1/stop'].includes(pathname)) {
return send(response, 404, { error: 'not found' })
}
const value = await body(request)
if (!sameToken(value.token, token)) return send(response, 403, { error: 'access launch authorization failed' })
send(response, 200, pathname === '/v1/start' ? await broker.start() : await broker.stop())
})().catch(async error => {
await report('access-launch.request.failed', {
error, method: request.method ?? null, pathname: request.url ?? null,
})
send(response, 500, { error: error instanceof Error ? error.message : 'access launch failed' })
})
})
}
export async function listenAccessLaunch(server, socketPath, platformGid) {
await mkdir(dirname(socketPath), { recursive: true })
await rm(socketPath, { force: true })
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(socketPath, resolve)
})
if (process.getuid?.() === 0) await chown(socketPath, 0, platformGid)
await chmod(socketPath, process.getuid?.() === 0 ? 0o660 : 0o600)
}
@@ -0,0 +1,625 @@
import { constants } from 'node:fs'
import { link, mkdir, open, readdir, readFile, rm } from 'node:fs/promises'
import { createHash, randomBytes, randomUUID } from 'node:crypto'
import { join, resolve } from 'node:path'
import { pipeline } from 'node:stream/promises'
import { Transform } from 'node:stream'
import { durableReplace } from '../../lib/atomic.mjs'
import {
MANIFEST_MEDIA_TYPE,
parseArtifactList,
parseReleaseManifest,
SIGNATURE_MEDIA_TYPE,
} from '../../lib/artifact-contracts.mjs'
import { exactKeys, isoTimestamp, plainObject, positiveSafeInteger, TrustError } from '../../lib/validation.mjs'
import { compareDshVersions } from '../../lib/supported-target.mjs'
import { OFFICIAL_DSH_METADATA_LIMIT, OFFICIAL_DSH_TARBALL_LIMIT } from '../../lib/official-dsh-contracts.mjs'
import { candidateFromPackument, verifyOfficialDshCandidate } from './official-dsh.mjs'
import { verifyDetached } from './signature.mjs'
export {
MANIFEST_MEDIA_TYPE,
parseArtifactDescriptor,
parseArtifactList,
parseReleaseManifest,
SIGNATURE_MEDIA_TYPE,
} from '../../lib/artifact-contracts.mjs'
function nonNegativeSafeInteger(value, label) {
if (!Number.isSafeInteger(value) || value < 0) {
throw new TrustError(`${label} must be a non-negative safe integer`)
}
return value
}
function descriptorsFromTarget(target) {
return parseArtifactList(target.document.artifacts, 'release target artifacts')
}
function descriptorById(artifacts, artifactId) {
const descriptor = artifacts.find(artifact => artifact.id === artifactId)
if (descriptor === undefined) throw new TrustError(`artifact ${JSON.stringify(artifactId)} is not authorized`)
return descriptor
}
async function atomicJson(path, value) {
await durableReplace(path, `${JSON.stringify(value)}\n`)
}
async function hashFile(path) {
const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW)
const hash = createHash('sha256')
let size = 0
try {
if (!(await handle.stat()).isFile()) throw new TrustError('trusted object must be a regular file')
for await (const chunk of handle.createReadStream({ autoClose: false })) {
hash.update(chunk)
size += chunk.byteLength
}
} finally {
await handle.close()
}
return { sha256: hash.digest('hex'), size }
}
function receiptToken() {
return randomBytes(32).toString('base64url')
}
function validateReceipt(value) {
const receipt = plainObject(value, 'receipt')
const required = [
'artifactId', 'authoritySignature', 'importedAt', 'keyringGeneration', 'mediaType',
'objectSha256', 'parentReceipt', 'parentSha256', 'signerKeyId', 'size', 'status',
'targetSequence', 'token',
]
const authorityType = receipt.authorityType ?? 'stable'
const receiptFields = receipt.authorityType === undefined
? required
: [...required, 'authorityType', ...(authorityType === 'official-dsh' ? ['authorityVersion'] : [])]
exactKeys(receipt, receiptFields, 'receipt')
if (!['stable', 'official-dsh'].includes(authorityType)) throw new TrustError('receipt authority type is invalid')
if (authorityType === 'official-dsh') {
compareDshVersions(receipt.authorityVersion, receipt.authorityVersion)
}
if (typeof receipt.token !== 'string' || !/^[A-Za-z0-9_-]{43}$/.test(receipt.token)) {
throw new TrustError('receipt token is invalid')
}
if (!['staged', 'active', 'previous', 'retired', 'revoked'].includes(receipt.status)) {
throw new TrustError('receipt status is invalid')
}
if (typeof receipt.artifactId !== 'string' || !/^[a-z0-9][a-z0-9._-]{0,127}$/.test(receipt.artifactId)) {
throw new TrustError('receipt artifact ID is invalid')
}
if (typeof receipt.mediaType !== 'string' || !/^[\x21-\x7e]{1,127}$/.test(receipt.mediaType)) {
throw new TrustError('receipt media type is invalid')
}
for (const [name, digest] of [
['object SHA-256', receipt.objectSha256],
['parent SHA-256', receipt.parentSha256],
['signer key ID', receipt.signerKeyId],
]) {
const valid = name === 'signer key ID' && authorityType === 'official-dsh'
? typeof digest === 'string' && /^SHA256:[A-Za-z0-9+/]{43}$/.test(digest)
: typeof digest === 'string' && /^[a-f0-9]{64}$/.test(digest)
if (!valid) {
throw new TrustError(`receipt ${name} is invalid`)
}
}
nonNegativeSafeInteger(receipt.size, 'receipt size')
positiveSafeInteger(receipt.keyringGeneration, 'receipt keyring generation')
positiveSafeInteger(receipt.targetSequence, 'receipt target sequence')
isoTimestamp(receipt.importedAt, 'receipt import time')
if (
receipt.parentReceipt !== null
&& (typeof receipt.parentReceipt !== 'string' || !/^[A-Za-z0-9_-]{43}$/.test(receipt.parentReceipt))
) throw new TrustError('receipt parent token is invalid')
if (
receipt.authoritySignature !== null
&& (typeof receipt.authoritySignature !== 'object' || Array.isArray(receipt.authoritySignature))
) throw new TrustError('receipt authority signature is invalid')
return Object.assign(receipt, { authorityType })
}
export class VerifiedObjectStore {
constructor({
root, objectRoot, receiptRoot, untrustedRoot, ledger, now = () => new Date(),
}) {
if (root === undefined && (objectRoot === undefined || receiptRoot === undefined)) {
throw new TypeError('VerifiedObjectStore requires root or explicit objectRoot and receiptRoot')
}
this.objectRoot = resolve(objectRoot ?? join(root, 'objects'))
this.receiptRoot = resolve(receiptRoot ?? join(root, 'receipts'))
this.untrustedRoot = resolve(untrustedRoot)
this.ledger = ledger
this.now = now
this.queue = Promise.resolve()
}
exclusive(operation) {
const result = this.queue.then(operation, operation)
this.queue = result.then(() => undefined, () => undefined)
return result
}
objectPath(sha256) {
return join(this.objectRoot, sha256)
}
receiptPath(token) {
return join(this.receiptRoot, `${token}.json`)
}
resolveUntrustedSource(sourcePath) {
if (typeof sourcePath !== 'string' || sourcePath === '') {
throw new TrustError('artifact source path is invalid')
}
const source = resolve(sourcePath)
if (source !== this.untrustedRoot && !source.startsWith(`${this.untrustedRoot}/`)) {
throw new TrustError('artifact source must be inside the untrusted download directory')
}
return source
}
async readReceipt(token) {
if (typeof token !== 'string' || !/^[A-Za-z0-9_-]{43}$/.test(token)) {
throw new TrustError('receipt token is invalid')
}
let value
try {
value = JSON.parse(await readFile(this.receiptPath(token), 'utf8'))
} catch (error) {
if (error?.code === 'ENOENT') throw new TrustError('receipt does not exist')
throw error
}
return validateReceipt(value)
}
async authorityFromTarget(artifactId) {
const target = await this.ledger.currentTarget()
if (target === undefined) throw new TrustError('no accepted release target exists')
return {
descriptor: descriptorById(descriptorsFromTarget(target.value), artifactId),
keyringGeneration: target.value.keyringGeneration,
parentReceipt: null,
parentSha256: createHash('sha256').update(target.bytes).digest('hex'),
signerKeyId: target.value.keyId,
targetSequence: target.value.targetSequence,
authorityType: 'stable',
}
}
async authorityFromManifest(parentToken, artifactId) {
const parent = await this.readReceipt(parentToken)
if (!['staged', 'active', 'previous'].includes(parent.status)) {
throw new TrustError('parent manifest receipt is not usable')
}
if (parent.authoritySignature === null) throw new TrustError('parent receipt is not a verified manifest')
const bytes = await readFile(this.objectPath(parent.objectSha256))
const manifest = parseReleaseManifest(bytes)
const keyring = (await this.ledger.keyringGeneration(manifest.keyringGeneration)).value
const signerKeyId = verifyDetached(bytes, parent.authoritySignature, keyring.current.publicKey, 'release manifest')
if (
manifest.targetSequence !== parent.targetSequence
|| manifest.keyringGeneration !== parent.keyringGeneration
|| signerKeyId !== parent.signerKeyId
) throw new TrustError('parent manifest receipt is inconsistent')
return {
descriptor: descriptorById(manifest.artifacts, artifactId),
keyringGeneration: manifest.keyringGeneration,
parentReceipt: parentToken,
parentSha256: parent.objectSha256,
signerKeyId,
targetSequence: manifest.targetSequence,
authorityType: 'stable',
}
}
importFromTarget(artifactId, sourcePath) {
return this.exclusive(async () => this.importAuthorized(
await this.authorityFromTarget(artifactId),
sourcePath,
))
}
importFromManifest(parentToken, artifactId, sourcePath) {
return this.exclusive(async () => this.importAuthorized(
await this.authorityFromManifest(parentToken, artifactId),
sourcePath,
))
}
async importAuthorized(authority, sourcePath) {
const currentKeyring = (await this.ledger.currentKeyring())?.value
const currentTarget = (await this.ledger.currentTarget())?.value
if (
currentKeyring === undefined
|| currentTarget === undefined
|| authority.keyringGeneration !== currentKeyring.generation
|| authority.signerKeyId !== currentKeyring.current.keyId
|| authority.targetSequence !== currentTarget.targetSequence
) throw new TrustError('artifact authority is no longer current', 'TRUST_REVOKED')
const source = this.resolveUntrustedSource(sourcePath)
await mkdir(this.objectRoot, { recursive: true })
await mkdir(this.receiptRoot, { recursive: true })
const destination = this.objectPath(authority.descriptor.sha256)
const temporary = `${destination}.${randomUUID()}.tmp`
const sourceHandle = await open(source, constants.O_RDONLY | constants.O_NOFOLLOW)
let destinationHandle
try {
if (!(await sourceHandle.stat()).isFile()) throw new TrustError('artifact source must be a regular file')
destinationHandle = await open(temporary, 'wx', 0o444)
const hash = createHash('sha256')
let size = 0
const meter = new Transform({
transform(chunk, _encoding, callback) {
hash.update(chunk)
size += chunk.byteLength
callback(null, chunk)
},
})
await pipeline(sourceHandle.createReadStream({ autoClose: false }), meter, destinationHandle.createWriteStream())
destinationHandle = undefined
const sha256 = hash.digest('hex')
if (size !== authority.descriptor.size || sha256 !== authority.descriptor.sha256) {
throw new TrustError('artifact content does not match its signed descriptor', 'TRUST_ARTIFACT_MISMATCH')
}
try {
await link(temporary, destination)
await rm(temporary)
} catch (error) {
if (error?.code !== 'EEXIST') throw error
const existing = await hashFile(destination)
if (existing.size !== size || existing.sha256 !== sha256) {
throw new TrustError('trusted object store contains conflicting content')
}
await rm(temporary, { force: true })
}
const receipt = {
token: receiptToken(),
artifactId: authority.descriptor.id,
mediaType: authority.descriptor.mediaType,
objectSha256: sha256,
size,
parentReceipt: authority.parentReceipt,
parentSha256: authority.parentSha256,
signerKeyId: authority.signerKeyId,
keyringGeneration: authority.keyringGeneration,
targetSequence: authority.targetSequence,
authorityType: authority.authorityType,
status: 'staged',
authoritySignature: null,
importedAt: this.now().toISOString(),
}
await atomicJson(this.receiptPath(receipt.token), receipt)
return Object.freeze({ ...receipt, path: destination })
} finally {
await sourceHandle.close().catch(() => {})
await destinationHandle?.close().catch(() => {})
await rm(temporary, { force: true }).catch(() => {})
}
}
ensureOfficialDsh(requestedVersion, metadataPath, tarballPath) {
return this.exclusive(async () => {
compareDshVersions(requestedVersion, requestedVersion)
const target = (await this.ledger.currentTarget())?.value
if (target === undefined) {
throw new TrustError('official DSH import requires an accepted registry policy')
}
if (compareDshVersions(requestedVersion, target.desired.dsh.version) < 0) {
throw new TrustError('official DSH version is older than the current supported target', 'TRUST_ROLLBACK')
}
const metadataSource = this.resolveUntrustedSource(metadataPath)
const tarballSource = this.resolveUntrustedSource(tarballPath)
const metadataHandle = await open(metadataSource, constants.O_RDONLY | constants.O_NOFOLLOW)
let metadata
try {
const stat = await metadataHandle.stat()
if (!stat.isFile()) throw new TrustError('official DSH metadata source must be a regular file')
if (stat.size > OFFICIAL_DSH_METADATA_LIMIT) throw new TrustError('official DSH metadata exceeds the download limit')
metadata = await metadataHandle.readFile()
if (metadata.byteLength > OFFICIAL_DSH_METADATA_LIMIT) {
throw new TrustError('official DSH metadata exceeds the download limit')
}
} finally {
await metadataHandle.close()
}
let packument
try { packument = JSON.parse(metadata.toString('utf8')) } catch { throw new TrustError('official DSH metadata is not valid JSON') }
const candidate = verifyOfficialDshCandidate(
candidateFromPackument(packument, requestedVersion),
requestedVersion,
target.officialDshPolicy,
this.now(),
)
if (requestedVersion === target.desired.dsh.version && candidate.dist.integrity !== target.desired.dsh.integrity) {
throw new TrustError('official DSH metadata does not match the supported target integrity', 'TRUST_ARTIFACT_MISMATCH')
}
await mkdir(this.objectRoot, { recursive: true })
await mkdir(this.receiptRoot, { recursive: true })
const temporary = join(this.objectRoot, `.${randomUUID()}.tmp`)
const sourceHandle = await open(tarballSource, constants.O_RDONLY | constants.O_NOFOLLOW)
let destinationHandle
try {
if (!(await sourceHandle.stat()).isFile()) throw new TrustError('official DSH tarball source must be a regular file')
destinationHandle = await open(temporary, 'wx', 0o444)
const sha256 = createHash('sha256')
const sha512 = createHash('sha512')
let size = 0
const meter = new Transform({
transform(chunk, _encoding, callback) {
size += chunk.byteLength
if (size > OFFICIAL_DSH_TARBALL_LIMIT) {
callback(new TrustError('official DSH tarball exceeds the download limit'))
return
}
sha256.update(chunk)
sha512.update(chunk)
callback(null, chunk)
},
})
await pipeline(sourceHandle.createReadStream({ autoClose: false }), meter, destinationHandle.createWriteStream())
destinationHandle = undefined
const objectSha256 = sha256.digest('hex')
const integrity = `sha512-${sha512.digest('base64')}`
if (integrity !== candidate.dist.integrity) {
throw new TrustError('official DSH tarball does not match signed npm integrity', 'TRUST_ARTIFACT_MISMATCH')
}
const destination = this.objectPath(objectSha256)
try {
await link(temporary, destination)
await rm(temporary)
} catch (error) {
if (error?.code !== 'EEXIST') throw error
const existing = await hashFile(destination)
if (existing.size !== size || existing.sha256 !== objectSha256) {
throw new TrustError('trusted object store contains conflicting content')
}
await rm(temporary, { force: true })
}
const accepted = await this.ledger.acceptOfficialDsh(candidate, objectSha256, size)
const verifiedCandidate = accepted.value
const authority = `${candidate.name}@${candidate.version}:${candidate.dist.integrity}`
const receipt = {
token: receiptToken(),
artifactId: 'official-dsh-tarball',
mediaType: 'application/vnd.npm.package+gzip',
objectSha256,
size,
parentReceipt: null,
parentSha256: createHash('sha256').update(authority).digest('hex'),
signerKeyId: verifiedCandidate.signerKeyId,
keyringGeneration: accepted.keyringGeneration,
targetSequence: accepted.targetSequence,
authorityType: 'official-dsh',
authorityVersion: candidate.version,
status: 'staged',
authoritySignature: null,
importedAt: this.now().toISOString(),
}
await atomicJson(this.receiptPath(receipt.token), receipt)
return Object.freeze({ ...receipt, path: destination })
} finally {
await sourceHandle.close().catch(() => {})
await destinationHandle?.close().catch(() => {})
await rm(temporary, { force: true }).catch(() => {})
}
})
}
acceptManifest(token, signatureToken) {
return this.exclusive(async () => {
const receipt = await this.readReceipt(token)
const signatureReceipt = await this.readReceipt(signatureToken)
if (receipt.mediaType !== MANIFEST_MEDIA_TYPE) throw new TrustError('receipt is not a manifest artifact')
if (receipt.status !== 'staged') throw new TrustError('only a staged manifest can become an authority')
if (signatureReceipt.mediaType !== SIGNATURE_MEDIA_TYPE) throw new TrustError('signature receipt has the wrong media type')
if (signatureReceipt.status !== 'staged' || signatureReceipt.parentReceipt !== null) {
throw new TrustError('manifest signature must be a staged target Artifact')
}
if (
signatureReceipt.keyringGeneration !== receipt.keyringGeneration
|| signatureReceipt.targetSequence !== receipt.targetSequence
|| signatureReceipt.signerKeyId !== receipt.signerKeyId
) throw new TrustError('manifest and signature receipts have different authorities')
const bytes = await readFile(this.objectPath(receipt.objectSha256))
const signatureBytes = await readFile(this.objectPath(signatureReceipt.objectSha256))
let signature
try {
signature = JSON.parse(signatureBytes.toString('utf8'))
} catch {
throw new TrustError('manifest signature Artifact must contain valid JSON')
}
const manifest = parseReleaseManifest(bytes)
const currentTarget = await this.ledger.currentTarget()
if (
currentTarget === undefined
|| manifest.keyringGeneration !== currentTarget.value.keyringGeneration
|| manifest.targetSequence !== currentTarget.value.targetSequence
) throw new TrustError('manifest does not belong to the current release target')
const keyring = (await this.ledger.currentKeyring()).value
const signerKeyId = verifyDetached(bytes, signature, keyring.current.publicKey, 'release manifest')
if (signerKeyId !== receipt.signerKeyId) throw new TrustError('manifest signer differs from its parent target')
const updated = { ...receipt, authoritySignature: signature }
await atomicJson(this.receiptPath(token), updated)
return Object.freeze(updated)
})
}
async receiptFiles() {
try {
return (await readdir(this.receiptRoot)).filter(name => name.endsWith('.json'))
} catch (error) {
if (error?.code === 'ENOENT') return []
throw error
}
}
async allReceipts() {
return Promise.all((await this.receiptFiles()).map(async name => (
validateReceipt(JSON.parse(await readFile(join(this.receiptRoot, name), 'utf8')))
)))
}
async activeReceipts() {
return Object.freeze((await this.allReceipts())
.filter(receipt => receipt.status === 'active')
.map(receipt => Object.freeze({ token: receipt.token, authorityType: receipt.authorityType })))
}
reconcileRevocations(keyring) {
return this.exclusive(async () => {
const receipts = await this.allReceipts()
const revoked = new Set(keyring.revokedKeyIds)
const currentTarget = (await this.ledger.currentTarget())?.value
const currentOfficialDsh = await this.ledger.currentOfficialDsh().catch(() => undefined)
const unusable = new Set(receipts.filter(receipt => (
receipt.status === 'revoked' || receipt.status === 'retired'
)).map(receipt => receipt.token))
let changed = true
while (changed) {
changed = false
for (const receipt of receipts) {
if (
receipt.status === 'staged'
&& (
revoked.has(receipt.signerKeyId)
|| (receipt.authorityType === 'stable' && (
currentTarget === undefined
|| receipt.keyringGeneration !== currentTarget.keyringGeneration
|| receipt.targetSequence !== currentTarget.targetSequence
))
|| (receipt.authorityType === 'official-dsh' && (
currentOfficialDsh === undefined
|| receipt.authorityVersion !== currentOfficialDsh.value.version
|| receipt.objectSha256 !== currentOfficialDsh.objectSha256
|| receipt.signerKeyId !== currentOfficialDsh.value.signerKeyId
))
|| (receipt.parentReceipt !== null && unusable.has(receipt.parentReceipt))
)
) {
receipt.status = 'revoked'
unusable.add(receipt.token)
changed = true
}
}
}
for (const receipt of receipts) await atomicJson(this.receiptPath(receipt.token), receipt)
return receipts.filter(receipt => receipt.status === 'revoked').map(receipt => receipt.token)
})
}
activate(tokens) {
return this.exclusive(async () => {
if (!Array.isArray(tokens)) throw new TrustError('activation receipts must be an array')
const receipts = await this.allReceipts()
const byToken = new Map(receipts.map(receipt => [receipt.token, receipt]))
const selected = new Set()
let currentKeyring
let currentTarget
let currentOfficialDsh
if (tokens.length > 0) {
currentKeyring = (await this.ledger.currentKeyring())?.value
if (currentKeyring === undefined) throw new TrustError('activation requires a current keyring')
currentTarget = (await this.ledger.currentTarget())?.value
if (currentTarget === undefined) throw new TrustError('activation requires a current release target')
currentOfficialDsh = await this.ledger.currentOfficialDsh().catch(() => undefined)
}
const add = (token) => {
const receipt = byToken.get(token)
if (receipt === undefined) throw new TrustError('activation references an unknown receipt')
if (!['staged', 'active', 'previous'].includes(receipt.status)) {
throw new TrustError('activation references an unusable receipt')
}
const officialAuthorityCurrent = currentOfficialDsh !== undefined
&& receipt.authorityVersion === currentOfficialDsh.value.version
&& receipt.objectSha256 === currentOfficialDsh.objectSha256
&& receipt.signerKeyId === currentOfficialDsh.value.signerKeyId
const stableAuthorityCurrent = receipt.keyringGeneration === currentKeyring.generation
&& receipt.signerKeyId === currentKeyring.current.keyId
&& receipt.targetSequence === currentTarget.targetSequence
const authorityCurrent = receipt.authorityType === 'official-dsh'
? officialAuthorityCurrent
: stableAuthorityCurrent
if (
receipt.status === 'staged'
&& !authorityCurrent
) throw new TrustError('activation references a revoked receipt', 'TRUST_REVOKED')
if (selected.has(token)) return
selected.add(token)
if (receipt.parentReceipt !== null) add(receipt.parentReceipt)
}
for (const token of tokens) add(token)
for (const receipt of receipts) {
if (selected.has(receipt.token)) receipt.status = 'active'
else if (receipt.status === 'active') receipt.status = 'previous'
else if (receipt.status === 'previous') receipt.status = 'retired'
await atomicJson(this.receiptPath(receipt.token), receipt)
}
return Object.freeze([...selected])
})
}
rollback() {
return this.exclusive(async () => {
const receipts = await this.allReceipts()
if (!receipts.some(receipt => receipt.status === 'previous')) {
throw new TrustError('no previous activation exists')
}
for (const receipt of receipts) {
if (receipt.status === 'active') receipt.status = 'previous'
else if (receipt.status === 'previous') receipt.status = 'active'
await atomicJson(this.receiptPath(receipt.token), receipt)
}
})
}
bootstrapPackage(token, version) {
return this.exclusive(async () => {
const receipt = await this.readReceipt(token)
if (receipt.status !== 'staged' || receipt.parentReceipt === null) {
throw new TrustError('Bootstrap package must be a staged manifest child')
}
const parent = await this.readReceipt(receipt.parentReceipt)
if (parent.authoritySignature === null) throw new TrustError('Bootstrap package parent is not verified')
const manifest = parseReleaseManifest(await readFile(this.objectPath(parent.objectSha256)))
if (manifest.document.manifestType !== 'bootstrap' || manifest.document.version !== version) {
throw new TrustError('Bootstrap package does not belong to the requested version')
}
if (manifest.artifacts.length !== 1 || manifest.artifacts[0].id !== receipt.artifactId) {
throw new TrustError('Bootstrap manifest must authorize exactly one package')
}
const target = await this.ledger.currentTarget()
if (target?.value.document.desired?.bootstrap?.manifestArtifactId !== parent.artifactId) {
throw new TrustError('Bootstrap manifest is not the current desired Bootstrap')
}
return Object.freeze({ path: this.objectPath(receipt.objectSha256), receipt, manifest })
})
}
collectGarbage() {
return this.exclusive(async () => {
const receipts = await this.allReceipts()
const retained = receipts.filter(receipt => !['retired', 'revoked'].includes(receipt.status))
const retainedObjects = new Set(retained.map(receipt => receipt.objectSha256))
for (const receipt of receipts) {
if (!retained.includes(receipt)) await rm(this.receiptPath(receipt.token), { force: true })
}
let objects = []
try {
objects = await readdir(this.objectRoot)
} catch (error) {
if (error?.code !== 'ENOENT') throw error
}
const removed = []
for (const sha256 of objects) {
if (!retainedObjects.has(sha256)) {
await rm(this.objectPath(sha256), { force: true })
removed.push(sha256)
}
}
return Object.freeze(removed)
})
}
}
@@ -0,0 +1,76 @@
import { exactKeys, isoTimestamp, parseJsonDocument, plainObject, positiveSafeInteger, TrustError } from '../../lib/validation.mjs'
import { releaseKeyId, verifyDetached } from './signature.mjs'
export const KEYRING_SCHEMA = 1
function parseReleaseKey(value, label) {
const object = plainObject(value, label)
exactKeys(object, ['algorithm', 'keyId', 'publicKey'], label)
if (object.algorithm !== 'Ed25519') throw new TrustError(`${label}.algorithm must be Ed25519`)
if (typeof object.publicKey !== 'string') throw new TrustError(`${label}.publicKey must be base64`)
const keyId = releaseKeyId(object.publicKey)
if (object.keyId !== keyId) throw new TrustError(`${label}.keyId does not match its public key`)
return Object.freeze({ algorithm: object.algorithm, keyId, publicKey: object.publicKey })
}
export function parseKeyring(bytes) {
const object = parseJsonDocument(bytes, 'keyring')
exactKeys(object, ['current', 'generation', 'issuedAt', 'next', 'revokedKeyIds', 'schema'], 'keyring')
if (object.schema !== KEYRING_SCHEMA) throw new TrustError('keyring.schema must be 1')
const generation = positiveSafeInteger(object.generation, 'keyring.generation')
const issuedAt = isoTimestamp(object.issuedAt, 'keyring.issuedAt')
const current = parseReleaseKey(object.current, 'keyring.current')
const next = parseReleaseKey(object.next, 'keyring.next')
if (current.keyId === next.keyId) throw new TrustError('keyring current and next keys must differ')
if (!Array.isArray(object.revokedKeyIds)) throw new TrustError('keyring.revokedKeyIds must be an array')
const revokedKeyIds = object.revokedKeyIds.map((keyId, index) => {
if (typeof keyId !== 'string' || !/^[a-f0-9]{64}$/.test(keyId)) {
throw new TrustError(`keyring.revokedKeyIds[${String(index)}] must be a SHA-256 hex digest`)
}
return keyId
})
if (new Set(revokedKeyIds).size !== revokedKeyIds.length) {
throw new TrustError('keyring.revokedKeyIds must not contain duplicates')
}
if ([...revokedKeyIds].sort().some((keyId, index) => keyId !== revokedKeyIds[index])) {
throw new TrustError('keyring.revokedKeyIds must be sorted')
}
if (revokedKeyIds.includes(current.keyId) || revokedKeyIds.includes(next.keyId)) {
throw new TrustError('keyring active keys must not be revoked')
}
return Object.freeze({
schema: object.schema,
generation,
issuedAt,
current,
next,
revokedKeyIds: Object.freeze(revokedKeyIds),
})
}
export function verifyRecoveryKeyring(bytes, signature, recoveryPublicKey) {
verifyDetached(bytes, signature, recoveryPublicKey, 'keyring')
return parseKeyring(bytes)
}
export function validateKeyringTransition(previous, next) {
if (next.generation <= previous.generation) {
throw new TrustError('keyring generation must increase', 'TRUST_ROLLBACK')
}
const nextRevoked = new Set(next.revokedKeyIds)
for (const keyId of previous.revokedKeyIds) {
if (!nextRevoked.has(keyId)) throw new TrustError('keyring cannot remove a revoked key')
}
for (const keyId of [previous.current.keyId, previous.next.keyId]) {
const remainsActive = keyId === next.current.keyId || keyId === next.next.keyId
if (!remainsActive && !nextRevoked.has(keyId)) {
throw new TrustError('keyring must revoke every removed release key')
}
}
for (const keyId of [next.current.keyId, next.next.keyId]) {
if (previous.revokedKeyIds.includes(keyId)) {
throw new TrustError('keyring cannot reactivate a revoked key')
}
}
return next
}
@@ -0,0 +1,244 @@
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'
import { durableCreate, durableReplace } from '../../lib/atomic.mjs'
import { compareDshVersions } from '../../lib/supported-target.mjs'
import { TrustError } from '../../lib/validation.mjs'
import { validateKeyringTransition, verifyRecoveryKeyring } from './keyring.mjs'
import { parseReleaseTarget, validateTargetTransition, verifyReleaseTarget } from './target.mjs'
import { parseOfficialDshCandidate, verifyOfficialDshCandidate } from './official-dsh.mjs'
async function readOptional(path) {
try {
return await readFile(path)
} catch (error) {
if (error?.code === 'ENOENT') return undefined
throw error
}
}
function signedRecord(document, signature) {
return Buffer.from(`${JSON.stringify({ document: document.toString('base64'), signature })}\n`)
}
function parseSignedRecord(bytes, label) {
let value
try {
value = JSON.parse(bytes.toString('utf8'))
} catch {
throw new TrustError(`${label} record must contain valid JSON`)
}
if (
value === null
|| typeof value !== 'object'
|| Array.isArray(value)
|| typeof value.document !== 'string'
|| value.signature === null
|| typeof value.signature !== 'object'
|| Array.isArray(value.signature)
|| Object.keys(value).sort().join(',') !== 'document,signature'
) {
throw new TrustError(`${label} record is invalid`)
}
const document = Buffer.from(value.document, 'base64')
if (document.toString('base64') !== value.document) {
throw new TrustError(`${label} record document must be canonical base64`)
}
return { document, signature: value.signature }
}
function parseOfficialDshRecord(bytes) {
let record
try { record = JSON.parse(bytes.toString('utf8')) } catch { throw new TrustError('official DSH record must contain valid JSON') }
if (
record === null || typeof record !== 'object' || Array.isArray(record)
|| Object.keys(record).sort().join(',') !== 'candidate,objectSha256,size'
|| typeof record.objectSha256 !== 'string' || !/^[a-f0-9]{64}$/.test(record.objectSha256)
|| !Number.isSafeInteger(record.size) || record.size < 0
) throw new TrustError('official DSH record is invalid')
return { candidate: parseOfficialDshCandidate(record.candidate), objectSha256: record.objectSha256, size: record.size }
}
export class TrustLedger {
constructor(root, recoveryPublicKey) {
this.root = root
this.recoveryPublicKey = recoveryPublicKey
this.queue = Promise.resolve()
}
exclusive(operation) {
const result = this.queue.then(operation, operation)
this.queue = result.then(() => undefined, () => undefined)
return result
}
keyringPath(name) {
return join(this.root, 'keyring', name)
}
targetPath(name) {
return join(this.root, 'target', name)
}
officialDshPath(name) {
return join(this.root, 'official-dsh', name)
}
async currentKeyring() {
const recordBytes = await readOptional(this.keyringPath('current.record.json'))
if (recordBytes === undefined) return undefined
return this.parseKeyringRecord(recordBytes)
}
parseKeyringRecord(recordBytes) {
const { document, signature } = parseSignedRecord(recordBytes, 'keyring')
return {
bytes: document,
signature,
value: verifyRecoveryKeyring(document, signature, this.recoveryPublicKey),
}
}
async keyringGeneration(generation) {
const recordBytes = await readOptional(this.keyringPath(join('generations', `${String(generation)}.record.json`)))
if (recordBytes === undefined) throw new TrustError(`trusted keyring generation ${String(generation)} is missing`)
const record = this.parseKeyringRecord(recordBytes)
if (record.value.generation !== generation) throw new TrustError('keyring generation record is inconsistent')
return record
}
acceptKeyring(bytes, signature) {
return this.exclusive(() => this.acceptKeyringUnlocked(bytes, signature))
}
async acceptKeyringUnlocked(bytes, signature) {
const next = verifyRecoveryKeyring(bytes, signature, this.recoveryPublicKey)
const current = await this.currentKeyring()
if (current !== undefined) {
if (next.generation === current.value.generation) {
if (!current.bytes.equals(bytes)) {
throw new TrustError('keyring generation cannot identify different content')
}
return current.value
}
validateKeyringTransition(current.value, next)
}
const record = signedRecord(bytes, signature)
const generationPath = this.keyringPath(join('generations', `${String(next.generation)}.record.json`))
const existingGeneration = await readOptional(generationPath)
if (existingGeneration === undefined) {
await durableCreate(generationPath, record)
} else if (!this.parseKeyringRecord(existingGeneration).bytes.equals(bytes)) {
throw new TrustError('keyring generation history conflicts with the proposed content')
}
await durableReplace(this.keyringPath('current.record.json'), record)
return next
}
async currentTarget(keyring = undefined) {
const recordBytes = await readOptional(this.targetPath('current.record.json'))
if (recordBytes === undefined) return undefined
const { document, signature } = parseSignedRecord(recordBytes, 'release target')
const parsed = parseReleaseTarget(document)
const trustedKeyring = keyring?.generation === parsed.keyringGeneration
? keyring
: (await this.keyringGeneration(parsed.keyringGeneration)).value
return { bytes: document, signature, value: verifyReleaseTarget(document, signature, trustedKeyring) }
}
acceptTarget(bytes, signature) {
return this.exclusive(() => this.acceptTargetUnlocked(bytes, signature))
}
async acceptTargetUnlocked(bytes, signature) {
const keyring = (await this.currentKeyring())?.value
if (keyring === undefined) throw new TrustError('a keyring must be accepted before a release target')
const next = verifyReleaseTarget(bytes, signature, keyring)
const current = await this.currentTarget(keyring)
if (current !== undefined) validateTargetTransition(current.bytes, current.value, bytes, next)
if (current?.bytes.equals(bytes)) return current.value
await durableReplace(this.targetPath('current.record.json'), signedRecord(bytes, signature))
return next
}
async currentOfficialDsh(target = undefined) {
const recordBytes = await readOptional(this.officialDshPath('current.record.json'))
if (recordBytes === undefined) return undefined
const record = parseOfficialDshRecord(recordBytes)
const currentTarget = target ?? (await this.currentTarget())?.value
if (currentTarget === undefined) {
throw new TrustError('official DSH requires an accepted registry policy')
}
const keyring = (await this.currentKeyring())?.value
if (
keyring === undefined
|| currentTarget.keyringGeneration !== keyring.generation
|| currentTarget.keyId !== keyring.current.keyId
) throw new TrustError('official DSH policy is no longer authorized', 'TRUST_REVOKED')
return Object.freeze({
...record,
value: verifyOfficialDshCandidate(
record.candidate,
record.candidate.version,
currentTarget.officialDshPolicy,
),
})
}
acceptOfficialDsh(candidate, objectSha256, size) {
return this.exclusive(() => this.acceptOfficialDshUnlocked(candidate, objectSha256, size))
}
async acceptOfficialDshUnlocked(candidate, objectSha256, size) {
if (typeof objectSha256 !== 'string' || !/^[a-f0-9]{64}$/.test(objectSha256)) {
throw new TrustError('official DSH object SHA-256 is invalid')
}
if (!Number.isSafeInteger(size) || size < 0) throw new TrustError('official DSH object size is invalid')
const target = (await this.currentTarget())?.value
if (target === undefined) {
throw new TrustError('official DSH requires an accepted registry policy')
}
const keyring = (await this.currentKeyring())?.value
if (
keyring === undefined
|| target.keyringGeneration !== keyring.generation
|| target.keyId !== keyring.current.keyId
) throw new TrustError('official DSH policy is no longer authorized', 'TRUST_REVOKED')
const candidateDocument = parseOfficialDshCandidate({
schema: candidate.schema,
name: candidate.name,
version: candidate.version,
dist: candidate.dist,
})
const next = verifyOfficialDshCandidate(candidateDocument, candidateDocument.version, target.officialDshPolicy)
if (compareDshVersions(next.version, target.desired.dsh.version) < 0) {
throw new TrustError('official DSH version is older than the current supported target', 'TRUST_ROLLBACK')
}
if (next.version === target.desired.dsh.version && next.dist.integrity !== target.desired.dsh.integrity) {
throw new TrustError('official DSH does not match the supported target integrity', 'TRUST_ARTIFACT_MISMATCH')
}
const previousBytes = await readOptional(this.officialDshPath('current.record.json'))
if (previousBytes !== undefined) {
const previous = parseOfficialDshRecord(previousBytes)
const compared = compareDshVersions(next.version, previous.candidate.version)
if (compared < 0) throw new TrustError('official DSH version cannot decrease', 'TRUST_ROLLBACK')
if (
compared === 0
&& (
next.dist.integrity !== previous.candidate.dist.integrity
|| objectSha256 !== previous.objectSha256
|| size !== previous.size
)
) throw new TrustError('official DSH version cannot identify different content')
}
const record = { candidate: candidateDocument, objectSha256, size }
await durableReplace(this.officialDshPath('current.record.json'), `${JSON.stringify(record)}\n`)
return Object.freeze({
candidate: next,
objectSha256,
size,
value: next,
keyringGeneration: target.keyringGeneration,
targetSequence: target.targetSequence,
})
}
}
@@ -0,0 +1,270 @@
import { createServer } from 'node:http'
import { chmod, chown, mkdir, open, rm } from 'node:fs/promises'
import { dirname } from 'node:path'
import { AtomicFileEditor } from '../../../control-plane/modules/file-manager/editor.mjs'
import { createManagedPathMatcher, fileError, FileInventory, fileManagerLocations } from '../../../control-plane/modules/file-manager/index.mjs'
import { FileTaskManager } from '../../../control-plane/modules/file-manager/tasks.mjs'
import { FileTransferManager } from '../../../control-plane/modules/file-manager/transfers.mjs'
import { TerminalSessionManager } from '../../../control-plane/services/management/terminal/sessions.mjs'
const API_PREFIX = '/_dsh_platform/api/v1/'
const MAX_BODY_BYTES = 16 * 1024
const MAX_TEXT_BODY_BYTES = 2 * 1024 * 1024 + 8192
const TERMINAL_SESSION_ROUTE = /^terminal\/sessions\/([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/
const TERMINAL_STREAM_ROUTE = /^terminal\/sessions\/([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\/stream$/
const FILE_TASK_ROUTE = /^files\/tasks\/([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/
async function jsonBody(request, maxBytes = MAX_BODY_BYTES) {
const chunks = []
let size = 0
for await (const chunk of request) {
size += chunk.byteLength
if (size > maxBytes) {
const error = new Error('request body is too large')
error.statusCode = 413
throw error
}
chunks.push(chunk)
}
return size === 0 ? {} : JSON.parse(Buffer.concat(chunks).toString('utf8'))
}
function send(response, status, value) {
if (response.headersSent) return response.destroy()
response.writeHead(status, { 'content-type': 'application/json; charset=utf-8' })
response.end(`${JSON.stringify(value)}\n`)
}
async function acquireLease(path) {
try {
const handle = await open(path, 'wx', 0o600)
await handle.writeFile(`${JSON.stringify({ pid: process.pid, startedAt: new Date().toISOString() })}\n`)
await handle.sync()
await handle.close()
} catch (error) {
if (error?.code === 'EEXIST') {
const conflict = new Error('a privileged file operation is already running')
conflict.statusCode = 409
throw conflict
}
throw error
}
}
export async function createMaintenanceServer({
paths,
dshHome = '/data/dsh',
defaultWorkspace = '/workspace',
platformBusy = async () => false,
authorize = async () => false,
report = async () => {},
fileReport = report,
terminalReport = report,
terminalEnvironment = async () => ({}),
audit = async () => {},
} = {}) {
if (process.getuid?.() !== 0) throw new Error('Maintenance Broker must run as root')
const isManaged = createManagedPathMatcher(paths.dataRoot)
const inventory = new FileInventory({ isManaged })
const transfers = new FileTransferManager({ isManaged })
const editor = new AtomicFileEditor({ isManaged })
const locations = Object.freeze({
...fileManagerLocations({ platformData: paths.dataRoot, dshHome, defaultWorkspace }),
privileged: true,
})
const terminal = new TerminalSessionManager({
cwd: '/root',
dshHome,
report: (message, fields) => terminalReport(message, fields),
})
let server
const tasks = new FileTaskManager({
root: paths.fileTasksRoot,
inventory,
isManaged,
protectedRoots: ['/', '/data', '/data/dsh', '/data/platform', '/workspace', paths.dataRoot, dshHome, defaultWorkspace, paths.deploymentView],
platformBusy: () => false,
onState: state => server?.emit('maintenance-state', state),
report: (message, fields) => fileReport(message, fields),
})
await rm(paths.maintenanceLeasePath, { force: true })
await tasks.initialize()
const startTask = async body => {
const managed = tasks.wouldManage(body)
if (managed && await platformBusy()) {
const error = new Error('a platform operation is already running')
error.statusCode = 409
throw error
}
if (managed) await acquireLease(paths.maintenanceLeasePath)
try {
const task = tasks.start(body)
if (managed) {
const internal = tasks.tasks.get(task.taskId)
void internal.completion.finally(() => rm(paths.maintenanceLeasePath, { force: true }))
}
void tasks.completion(task.taskId).then(async result => {
const outcome = result.status === 'success' ? 'completed' : result.status
await audit(`files.${result.operation}.${outcome}`, {
taskId: result.taskId,
path: result.path ?? result.destination ?? result.sources?.[0]?.path ?? null,
processedEntries: result.processedEntries ?? result.entries ?? null,
processedBytes: result.processedBytes ?? result.bytes ?? null,
...(result.error === null || result.error === undefined ? {} : { error: result.error }),
managed: result.managed ?? false,
privileged: true,
})
}).catch(error => report('maintenance.file-task.audit.failed', { error, taskId: task.taskId }))
return task
} catch (error) {
if (managed) await rm(paths.maintenanceLeasePath, { force: true })
throw error
}
}
const managedWrite = async (path, operation) => {
const managed = isManaged(path)
if (!managed) return operation()
if (await platformBusy()) {
const error = new Error('a platform operation is already running')
error.statusCode = 409
throw error
}
await acquireLease(paths.maintenanceLeasePath)
try { return await operation() } finally { await rm(paths.maintenanceLeasePath, { force: true }) }
}
server = createServer((request, response) => {
void (async () => {
if (!await authorize(request)) return send(response, 401, { error: 'maintenance authentication required' })
const url = new URL(request.url ?? '/', 'http://maintenance.internal')
if (!url.pathname.startsWith(API_PREFIX)) return send(response, 404, { error: 'not found' })
const route = url.pathname.slice(API_PREFIX.length)
if (request.method === 'POST' && route === 'terminal/sessions') {
const [body, environment] = await Promise.all([jsonBody(request), terminalEnvironment()])
const result = terminal.create(body, environment)
await audit('terminal.root-session.created', { sessionId: result.sessionId })
send(response, 201, { ...result, privileged: true })
} else if (request.method === 'GET' && TERMINAL_SESSION_ROUTE.test(route)) {
send(response, 200, { ...terminal.status(TERMINAL_SESSION_ROUTE.exec(route)[1]), privileged: true })
} else if (request.method === 'DELETE' && TERMINAL_SESSION_ROUTE.test(route)) {
send(response, 200, terminal.close(TERMINAL_SESSION_ROUTE.exec(route)[1]))
} else if (request.method === 'GET' && route === 'files/config') {
send(response, 200, locations)
} else if (request.method === 'GET' && route === 'files/list') {
const result = await inventory.list(url.searchParams.get('path'), {
cursor: url.searchParams.get('cursor'), limit: url.searchParams.get('limit'),
sort: url.searchParams.get('sort') ?? 'name', order: url.searchParams.get('order') ?? 'asc',
})
send(response, 200, result)
} else if (request.method === 'GET' && route === 'files/stat') {
try {
send(response, 200, await inventory.stat(url.searchParams.get('path')))
} catch (error) {
if (url.searchParams.get('optional') !== 'true' || error?.statusCode !== 404) throw error
send(response, 200, null)
}
} else if (request.method === 'GET' && route === 'files/content') {
send(response, 200, await inventory.content(url.searchParams.get('path')))
} else if (request.method === 'PUT' && route === 'files/content') {
const body = await jsonBody(request, MAX_TEXT_BODY_BYTES)
const result = await managedWrite(body.path, () => editor.write(body.path, body.content, body.revision, { create: body.create === true }))
await audit('files.content.saved', { path: result.path, size: result.size, managed: result.managed, privileged: true })
send(response, body.create === true ? 201 : 200, result)
} else if (request.method === 'GET' && route === 'files/download') {
const path = url.searchParams.get('path')
const transfer = tasks.startTransfer({ operation: 'download', path }, async controls => {
const download = await transfers.openDownload(path, {
revision: url.searchParams.get('revision') ?? undefined,
range: typeof request.headers.range === 'string' ? request.headers.range : undefined,
signal: controls.signal,
})
controls.progress({ processedBytes: 0, totalBytes: Number(download.headers['content-length']) })
await transfers.sendDownload(response, download, {
signal: controls.signal,
onProgress: (processedBytes, totalBytes) => controls.progress({ processedBytes, totalBytes }),
})
return download
})
await audit('files.download.started', { path, taskId: transfer.task.taskId, privileged: true })
await transfer.result
} else if (request.method === 'POST' && route === 'files/upload') {
const length = request.headers['content-length']
const contentLength = length === undefined ? undefined : Number(length)
const path = url.searchParams.get('path')
const managed = isManaged(path)
if (managed && await platformBusy()) {
const error = new Error('a platform operation is already running')
error.statusCode = 409
throw error
}
if (managed) await acquireLease(paths.maintenanceLeasePath)
const transfer = tasks.startTransfer({ operation: 'upload', path, totalBytes: contentLength ?? null, managed }, controls => transfers.upload(request, path, {
conflict: url.searchParams.get('conflict') ?? 'reject', contentLength, signal: controls.signal,
onProgress: (processedBytes, totalBytes) => controls.progress({ processedBytes, totalBytes }),
}))
if (managed) void tasks.completion(transfer.task.taskId).finally(() => rm(paths.maintenanceLeasePath, { force: true }))
await audit('files.upload.started', { path, taskId: transfer.task.taskId, managed, privileged: true })
const result = await transfer.result
await audit('files.upload.completed', { path: result.path, size: result.size, privileged: true })
send(response, 201, result)
} else if (request.method === 'POST' && route === 'files/tasks') {
const body = await jsonBody(request)
try {
const task = await startTask(body)
await audit(`files.${task.operation}.started`, { taskId: task.taskId, path: task.destination, managed: task.managed, privileged: true })
send(response, 202, task)
} catch (error) {
const operation = ['mkdir', 'touch'].includes(body?.operation) ? body.operation : 'task'
await audit(`files.${operation}.rejected`, { error, path: body?.destination ?? null, privileged: true })
throw error
}
} else if (request.method === 'GET' && route === 'files/tasks') {
send(response, 200, { tasks: tasks.list() })
} else if (request.method === 'GET' && FILE_TASK_ROUTE.test(route)) {
send(response, 200, tasks.get(FILE_TASK_ROUTE.exec(route)[1], {
cursor: url.searchParams.get('cursor'), limit: url.searchParams.get('limit'),
}))
} else if (request.method === 'DELETE' && FILE_TASK_ROUTE.test(route)) {
send(response, 202, tasks.cancel(FILE_TASK_ROUTE.exec(route)[1]))
} else send(response, 404, { error: 'not found' })
})().catch(async error => {
const failure = Number.isInteger(error?.statusCode)
? error
: fileError(error, 'maintenance request failed')
await report('maintenance.request.failed', {
error: failure, method: request.method ?? null, pathname: request.url ?? null,
level: failure.statusCode === 409 ? 'warning' : 'error',
})
send(response, failure.statusCode, { error: failure.message, code: failure.code ?? 'FILE_ERROR' })
})
})
server.on('upgrade', (request, socket, head) => {
void (async () => {
if (!await authorize(request)) {
socket.end('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\nContent-Length: 0\r\n\r\n')
return
}
const pathname = new URL(request.url ?? '/', 'http://maintenance.internal').pathname
const match = pathname.startsWith(API_PREFIX) ? TERMINAL_STREAM_ROUTE.exec(pathname.slice(API_PREFIX.length)) : null
if (match === null) throw new Error('not found')
terminal.upgrade(request, socket, head, match[1])
})().catch(error => {
void report('maintenance.terminal-upgrade.failed', { error })
if (!socket.destroyed) socket.end('HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Length: 0\r\n\r\n')
})
})
return Object.freeze({ server, terminal, tasks, locations })
}
export async function listenMaintenance(server, socketPath) {
await mkdir(dirname(socketPath), { recursive: true })
await rm(socketPath, { force: true })
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(socketPath, resolve)
})
await chown(socketPath, 0, 1000)
await chmod(socketPath, 0o660)
}
@@ -0,0 +1,89 @@
import { createPublicKey, verify } from 'node:crypto'
import { compareDshVersions } from '../../lib/supported-target.mjs'
import { exactKeys, plainObject, TrustError } from '../../lib/validation.mjs'
import {
OFFICIAL_DSH_PACKAGE,
OFFICIAL_NPM_REGISTRY,
officialDshTarballUrl,
} from '../../lib/official-dsh-contracts.mjs'
function registrySignature(value, label) {
const object = plainObject(value, label)
exactKeys(object, ['keyid', 'sig'], label)
if (typeof object.keyid !== 'string' || typeof object.sig !== 'string') {
throw new TrustError(`${label} is invalid`)
}
return Object.freeze({ keyid: object.keyid, sig: object.sig })
}
export function parseOfficialDshCandidate(value) {
const object = plainObject(value, 'official DSH candidate')
exactKeys(object, ['dist', 'name', 'schema', 'version'], 'official DSH candidate')
if (object.schema !== 1) throw new TrustError('official DSH candidate schema must be 1')
if (object.name !== OFFICIAL_DSH_PACKAGE) throw new TrustError('official DSH candidate package name is invalid')
compareDshVersions(object.version, object.version)
const dist = plainObject(object.dist, 'official DSH candidate dist')
exactKeys(dist, ['integrity', 'signatures', 'tarball'], 'official DSH candidate dist')
if (typeof dist.integrity !== 'string' || !/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(dist.integrity)) {
throw new TrustError('official DSH candidate integrity must use SHA-512')
}
if (!Array.isArray(dist.signatures) || dist.signatures.length === 0) {
throw new TrustError('official DSH candidate must contain registry signatures')
}
return Object.freeze({
schema: 1,
name: object.name,
version: object.version,
dist: Object.freeze({
integrity: dist.integrity,
tarball: dist.tarball,
signatures: Object.freeze(dist.signatures.map((entry, index) => (
registrySignature(entry, `official DSH signatures[${String(index)}]`)
))),
}),
})
}
export function candidateFromPackument(value, requestedVersion) {
compareDshVersions(requestedVersion, requestedVersion)
const packument = plainObject(value, 'official DSH packument')
const versions = plainObject(packument.versions, 'official DSH packument versions')
const selected = plainObject(versions[requestedVersion], `official DSH ${requestedVersion}`)
return parseOfficialDshCandidate({
schema: 1,
name: selected.name,
version: selected.version,
dist: {
integrity: selected.dist?.integrity,
tarball: selected.dist?.tarball,
signatures: selected.dist?.signatures,
},
})
}
export function verifyOfficialDshCandidate(value, requestedVersion, policy, now = new Date()) {
const candidate = parseOfficialDshCandidate(value)
if (policy.registry !== OFFICIAL_NPM_REGISTRY) throw new TrustError('official DSH policy registry is invalid')
if (candidate.version !== requestedVersion) throw new TrustError('official DSH metadata returned a different version')
if (candidate.name !== policy.packageName) throw new TrustError('official DSH candidate is outside the trusted policy')
const expectedTarball = officialDshTarballUrl(candidate.version).href
if (candidate.dist.tarball !== expectedTarball) throw new TrustError('official DSH tarball URL is not canonical')
const message = `${candidate.name}@${candidate.version}:${candidate.dist.integrity}`
for (const candidateSignature of candidate.dist.signatures) {
const key = policy.keys.find(entry => entry.keyId === candidateSignature.keyid)
if (key === undefined || (key.expires !== null && new Date(key.expires) <= now)) continue
let publicKey
try {
publicKey = createPublicKey({ key: Buffer.from(key.publicKey, 'base64'), format: 'der', type: 'spki' })
} catch {
throw new TrustError('official DSH policy contains an invalid registry public key')
}
if (publicKey.asymmetricKeyType !== 'ec' || publicKey.asymmetricKeyDetails?.namedCurve !== 'prime256v1') {
throw new TrustError('official DSH registry key must use P-256')
}
if (verify('sha256', Buffer.from(message), publicKey, Buffer.from(candidateSignature.sig, 'base64'))) {
return Object.freeze({ ...candidate, signerKeyId: key.keyId })
}
}
throw new TrustError('official DSH candidate has no valid delegated registry signature', 'TRUST_UNKNOWN_KEY')
}
@@ -0,0 +1,232 @@
import { spawn } from 'node:child_process'
import { timingSafeEqual } from 'node:crypto'
import { once } from 'node:events'
import { createServer } from 'node:http'
import { chmod, chown, mkdir, rm } from 'node:fs/promises'
import { dirname } from 'node:path'
const MAX_BODY_BYTES = 1024
function sameToken(left, right) {
const a = Buffer.from(left ?? '')
const b = Buffer.from(right ?? '')
return a.byteLength === b.byteLength && a.byteLength > 0 && timingSafeEqual(a, b)
}
function send(response, status, value) {
response.writeHead(status, { 'content-type': 'application/json; charset=utf-8' })
response.end(`${JSON.stringify(value)}\n`)
}
async function body(request) {
const chunks = []
let size = 0
for await (const chunk of request) {
size += chunk.byteLength
if (size > MAX_BODY_BYTES) throw new Error('proxy launch request body is too large')
chunks.push(chunk)
}
return size === 0 ? {} : JSON.parse(Buffer.concat(chunks).toString('utf8'))
}
export function proxyLaunchCommand({ node, script, uid, gid, platformGid }) {
if (!Number.isInteger(uid) || uid < 1 || !Number.isInteger(gid) || gid < 1) {
throw new Error('Proxy Manager UID and GID must be positive integers')
}
if (!Number.isInteger(platformGid) || platformGid < 1) {
throw new Error('Proxy Manager platform GID must be a positive integer')
}
return {
executable: '/usr/bin/setpriv',
args: [
`--reuid=${String(uid)}`,
`--regid=${String(gid)}`,
`--groups=${String(platformGid)}`,
'--', node, script,
],
}
}
export function proxyLaunchEnvironment({ environment = process.env, dataRoot, runRoot }) {
return {
HOME: '/nonexistent',
USER: 'dsh-proxy',
LOGNAME: 'dsh-proxy',
PATH: environment.PATH ?? '/usr/local/bin:/usr/bin:/bin',
DSH_PLATFORM_DATA: dataRoot,
DSH_PLATFORM_RUN: runRoot,
...(environment.DSH_PROXY_MASTER_KEY === undefined
? {}
: { DSH_PROXY_MASTER_KEY: environment.DSH_PROXY_MASTER_KEY }),
}
}
export class ProxyLaunchBroker {
constructor({
token,
dataRoot,
runRoot,
script,
controlSocket,
uid,
gid,
platformGid,
node = process.execPath,
spawnImpl = spawn,
capture = () => {},
report = async () => {},
readyTimeoutMs = 10_000,
}) {
this.token = token
this.dataRoot = dataRoot
this.runRoot = runRoot
this.script = script
this.controlSocket = controlSocket
this.uid = uid
this.gid = gid
this.platformGid = platformGid
this.node = node
this.spawnImpl = spawnImpl
this.capture = capture
this.report = report
this.readyTimeoutMs = readyTimeoutMs
this.child = undefined
this.ready = undefined
}
status() {
const running = this.child !== undefined
&& this.child.exitCode === null
&& this.child.signalCode === null
return {
running,
componentReady: running && this.ready?.componentReady === true,
pid: running ? this.child.pid ?? null : null,
revision: running ? this.ready?.revision ?? null : null,
ports: running ? this.ready?.ports ?? [] : [],
}
}
async start() {
if (this.status().running) return this.status()
const command = proxyLaunchCommand({
node: this.node,
script: this.script,
uid: this.uid,
gid: this.gid,
platformGid: this.platformGid,
})
const child = this.spawnImpl(command.executable, command.args, {
env: proxyLaunchEnvironment({ dataRoot: this.dataRoot, runRoot: this.runRoot }),
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
})
this.child = child
this.ready = undefined
this.capture(child, 'outbound-proxy', { stdout: true, stderr: true })
const ready = new Promise((resolve, reject) => {
child.once('error', reject)
child.once('exit', (code, signal) => reject(new Error(
`Proxy Manager exited before readiness (code=${String(code)}, signal=${String(signal)})`,
)))
child.on('message', message => {
if (message?.type === 'ready' && message.componentReady === true) resolve(message)
})
})
let timer
try {
const message = await Promise.race([
ready,
new Promise((_, reject) => {
timer = setTimeout(() => reject(new Error('Proxy Manager readiness timed out')), this.readyTimeoutMs)
timer.unref?.()
}),
])
this.ready = Object.freeze({
componentReady: true,
revision: message.revision ?? null,
ports: Array.isArray(message.ports) ? [...message.ports] : [],
})
if (process.getuid?.() === 0 && this.platformGid !== undefined) {
await chown(this.controlSocket, this.uid, this.platformGid)
await chmod(this.controlSocket, 0o660)
}
await this.report('outbound-proxy.launch.completed', { pid: child.pid ?? null })
child.once('exit', (code, signal) => {
if (this.child !== child) return
this.child = undefined
this.ready = undefined
void this.report('outbound-proxy.exited', {
level: code === 0 || signal === 'SIGTERM' ? 'info' : 'error', code, signal,
})
})
return this.status()
} catch (error) {
if (child.exitCode === null && child.signalCode === null) {
const exited = once(child, 'exit')
child.kill('SIGTERM')
await exited.catch(() => {})
}
if (this.child === child) this.child = undefined
this.ready = undefined
await this.report('outbound-proxy.launch.failed', { error })
throw error
} finally {
clearTimeout(timer)
}
}
async stop() {
const child = this.child
if (child === undefined || child.exitCode !== null || child.signalCode !== null) {
this.child = undefined
this.ready = undefined
return this.status()
}
const exited = once(child, 'exit')
child.kill('SIGTERM')
const graceful = await Promise.race([
exited.then(() => true),
new Promise(resolve => setTimeout(() => resolve(false), 5_000)),
])
if (!graceful && child.exitCode === null && child.signalCode === null) {
child.kill('SIGKILL')
await once(child, 'exit')
}
if (this.child === child) this.child = undefined
this.ready = undefined
await this.report('outbound-proxy.stop.completed')
return this.status()
}
}
export function createProxyLaunchServer({ broker, token, report = async () => {} }) {
return createServer((request, response) => {
void (async () => {
const pathname = new URL(request.url ?? '/', 'http://proxy-launch.internal').pathname
if (request.method === 'GET' && pathname === '/v1/status') return send(response, 200, broker.status())
if (request.method !== 'POST' || !['/v1/start', '/v1/stop'].includes(pathname)) {
return send(response, 404, { error: 'not found' })
}
const value = await body(request)
if (!sameToken(value.token, token)) return send(response, 403, { error: 'proxy launch authorization failed' })
const result = pathname === '/v1/start' ? await broker.start() : await broker.stop()
send(response, 200, result)
})().catch(async error => {
await report('proxy-launch.request.failed', {
error, method: request.method ?? null, pathname: request.url ?? null,
})
send(response, 500, { error: error instanceof Error ? error.message : 'proxy launch failed' })
})
})
}
export async function listenProxyLaunch(server, socketPath, platformGid) {
await mkdir(dirname(socketPath), { recursive: true })
await rm(socketPath, { force: true })
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(socketPath, resolve)
})
if (process.getuid?.() === 0) await chown(socketPath, 0, platformGid)
await chmod(socketPath, process.getuid?.() === 0 ? 0o660 : 0o600)
}
@@ -0,0 +1,58 @@
import { createServer } from 'node:http'
import { chmod, mkdir, rm } from 'node:fs/promises'
import { dirname } from 'node:path'
async function body(request) {
const chunks = []
let size = 0
for await (const chunk of request) {
size += chunk.byteLength
if (size > 16 * 1024) throw new Error('recovery request body is too large')
chunks.push(chunk)
}
return size === 0 ? {} : JSON.parse(Buffer.concat(chunks).toString('utf8'))
}
function send(response, status, value) {
response.writeHead(status, { 'content-type': 'application/json; charset=utf-8' })
response.end(`${JSON.stringify(value)}\n`)
}
export function createRecoveryServer({ inventory, deployments, supervisor, report = async () => {} }) {
const record = (message, fields) => Promise.resolve().then(() => report(message, fields)).catch(() => {})
return createServer(async (request, response) => {
try {
if (request.method === 'GET' && request.url === '/v1/status') {
send(response, 200, {
imageBaseline: {
imageBuildId: inventory.imageBuildId,
targetSequence: inventory.targetSequence,
dsh: inventory.deployment.dshVersion,
environment: inventory.deployment.environmentVersion,
},
deployment: await deployments().catch(() => null),
})
} else if (request.method === 'POST' && request.url === '/v1/recover-image-baseline') {
const value = await body(request)
if (value.confirm !== inventory.imageBuildId) throw new Error('image baseline recovery confirmation is invalid')
await record('image-baseline.requested', { imageBuildId: inventory.imageBuildId })
const slots = await supervisor.recoverImageBaseline()
await record('image-baseline.recovered', { imageBuildId: inventory.imageBuildId })
send(response, 200, { status: 'recovered', slots })
} else send(response, 404, { error: 'not found' })
} catch (error) {
await record('recovery.request.failed', { error, method: request.method ?? null, pathname: request.url ?? null })
send(response, 400, { error: error instanceof Error ? error.message : 'recovery failed' })
}
})
}
export async function listenRecovery(server, socketPath) {
await mkdir(dirname(socketPath), { recursive: true })
await rm(socketPath, { force: true })
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(socketPath, resolve)
})
await chmod(socketPath, 0o600)
}
@@ -0,0 +1,86 @@
import { lchown, lstat, mkdir, readFile, readlink, rename, stat, symlink } from 'node:fs/promises'
import { basename, dirname, join, resolve } from 'node:path'
import { randomUUID } from 'node:crypto'
import { PlatformPaths, preparePersistentLayout } from '../../lib/paths.mjs'
async function exists(path) {
return lstat(path).then(() => true, error => error?.code === 'ENOENT' ? false : Promise.reject(error))
}
function validSeedId(value, label) {
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value)) {
throw new Error(`${label} seed ID is invalid`)
}
return value
}
async function seedLink(source, destination) {
const target = resolve(source)
const details = await lstat(target)
if (!details.isDirectory() || details.isSymbolicLink()) throw new Error(`seed source is not a directory: ${target}`)
await mkdir(dirname(destination), { recursive: true })
if (await exists(destination)) {
const destinationDetails = await lstat(destination)
if (!destinationDetails.isSymbolicLink() || await resolves(destination)) return undefined
}
await replaceLink(destination, target)
return destination
}
async function seedSlot(source, root, version) {
const seeded = await seedLink(source, join(root, 'versions', version))
await mkdir(root, { recursive: true })
const current = join(root, 'current')
if (!await exists(current) || !await resolves(current)) await replaceLink(current, join('versions', version))
return seeded
}
async function resolves(path) {
return stat(path).then(() => true, error => error?.code === 'ENOENT' ? false : Promise.reject(error))
}
async function replaceLink(path, target) {
const temporary = `${path}.${randomUUID()}.tmp`
await symlink(target, temporary, 'dir')
await rename(temporary, path)
}
export async function provisionPlatformSeed(seedRoot, dataRoot) {
const paths = dataRoot instanceof PlatformPaths ? dataRoot : new PlatformPaths(dataRoot)
await preparePersistentLayout(paths)
const environmentSeedRoot = await exists(join(seedRoot, 'environments'))
? join(seedRoot, 'environments')
: join(seedRoot, 'environment')
const runtimeSeedRoot = await exists(join(seedRoot, 'runtimes'))
? join(seedRoot, 'runtimes')
: join(seedRoot, 'runtime')
const environmentVersion = validSeedId(
(await readFile(join(environmentSeedRoot, 'VERSION'), 'utf8')).trim(),
'Environment',
)
const runtimeVersion = validSeedId(
(await readFile(join(runtimeSeedRoot, 'VERSION'), 'utf8')).trim(),
'Runtime',
)
const seededLinks = (await Promise.all([
seedSlot(join(environmentSeedRoot, environmentVersion), paths.environmentsRoot, environmentVersion),
seedSlot(join(runtimeSeedRoot, runtimeVersion), paths.runtimesRoot, runtimeVersion),
seedLink(join(seedRoot, 'pristine', runtimeVersion), join(paths.pristineRoot, runtimeVersion)),
seedSlot(join(seedRoot, 'system-plugins', environmentVersion), paths.systemPluginsRoot, environmentVersion),
])).filter(Boolean)
if (process.getuid?.() === 0) {
const writableRoots = [
paths.deploymentStateRoot, paths.updaterStateRoot, paths.managementStateRoot, paths.fileTasksRoot,
paths.environmentsRoot, join(paths.environmentsRoot, 'versions'),
paths.pristineRoot, paths.runtimesRoot, join(paths.runtimesRoot, 'versions'), paths.systemPluginsRoot,
join(paths.systemPluginsRoot, 'versions'), paths.snapshotsRoot, paths.userPluginSnapshotsRoot,
paths.cacheRoot, paths.downloadsRoot, paths.npmCacheRoot, paths.logsRoot,
]
await Promise.all(writableRoots.map(path => lchown(path, 1000, 1000)))
}
return Object.freeze({ environmentVersion, runtimeVersion, seededLinks: Object.freeze(seededLinks) })
}
export async function currentSlot(root) {
return basename(await readlink(join(root, 'current')))
}
@@ -0,0 +1,65 @@
import { createHash, createPublicKey, verify } from 'node:crypto'
import { exactKeys, plainObject, TrustError } from '../../lib/validation.mjs'
export const SIGNATURE_SCHEMA = 1
export const SIGNATURE_ALGORITHM = 'Ed25519'
function canonicalBase64(value, label) {
if (typeof value !== 'string' || value === '') throw new TrustError(`${label} must be base64`)
const bytes = Buffer.from(value, 'base64')
if (bytes.length === 0 || bytes.toString('base64') !== value) {
throw new TrustError(`${label} must be canonical base64`)
}
return bytes
}
export function publicKeyFromBase64(value, label = 'public key') {
const der = canonicalBase64(value, label)
let key
try {
key = createPublicKey({ key: der, format: 'der', type: 'spki' })
} catch {
throw new TrustError(`${label} must be an Ed25519 SPKI public key`)
}
if (key.asymmetricKeyType !== 'ed25519') {
throw new TrustError(`${label} must be an Ed25519 SPKI public key`)
}
return { der, key }
}
export function releaseKeyId(publicKey) {
const { der } = publicKeyFromBase64(publicKey)
return createHash('sha256').update(der).digest('hex')
}
export function parseSignature(value, label = 'signature') {
const object = plainObject(value, label)
exactKeys(object, ['algorithm', 'keyId', 'schema', 'signature'], label)
if (object.schema !== SIGNATURE_SCHEMA) throw new TrustError(`${label}.schema must be 1`)
if (object.algorithm !== SIGNATURE_ALGORITHM) {
throw new TrustError(`${label}.algorithm must be Ed25519`)
}
if (typeof object.keyId !== 'string' || !/^[a-f0-9]{64}$/.test(object.keyId)) {
throw new TrustError(`${label}.keyId must be a SHA-256 hex digest`)
}
return Object.freeze({
schema: object.schema,
algorithm: object.algorithm,
keyId: object.keyId,
signature: canonicalBase64(object.signature, `${label}.signature`),
})
}
export function verifyDetached(document, signatureValue, publicKey, label = 'document') {
if (!Buffer.isBuffer(document)) throw new TrustError(`${label} must be bytes`)
const signature = parseSignature(signatureValue)
const parsedKey = publicKeyFromBase64(publicKey, `${label} signing key`)
const expectedKeyId = createHash('sha256').update(parsedKey.der).digest('hex')
if (signature.keyId !== expectedKeyId) {
throw new TrustError(`${label} signature key is not trusted`, 'TRUST_UNKNOWN_KEY')
}
if (!verify(null, document, parsedKey.key, signature.signature)) {
throw new TrustError(`${label} signature is invalid`, 'TRUST_BAD_SIGNATURE')
}
return signature.keyId
}
@@ -0,0 +1,242 @@
import { lstat, mkdir, readFile, readdir, rename, rm } from 'node:fs/promises'
import { join, resolve } from 'node:path'
import { randomUUID } from 'node:crypto'
import { spawn } from 'node:child_process'
import { canonicalJson } from '../../lib/canonical-json.mjs'
import { deriveRecordId, parseBootstrapRecord, parseSlots } from '../../lib/deployment-contracts.mjs'
import { durableCreate, durableReplace } from '../../lib/atomic.mjs'
import { hashTree } from '../../lib/tree-hash.mjs'
import { TrustError } from '../../lib/validation.mjs'
async function readOptional(path) {
try { return await readFile(path) } catch (error) {
if (error?.code === 'ENOENT') return undefined
throw error
}
}
function tar(args, capture = false) {
return new Promise((resolveTar, reject) => {
const child = spawn('tar', args, { stdio: ['ignore', capture ? 'pipe' : 'ignore', 'pipe'] })
const stdout = []
const stderr = []
child.stdout?.on('data', chunk => stdout.push(chunk))
child.stderr.on('data', chunk => stderr.push(chunk))
child.once('error', reject)
child.once('exit', code => code === 0
? resolveTar(Buffer.concat(stdout).toString('utf8'))
: reject(new Error(`Bootstrap archive failed: ${Buffer.concat(stderr).toString('utf8')}`)))
})
}
function recordDocument(content) {
return parseBootstrapRecord({ ...content, id: deriveRecordId('bootstrap-record', content) })
}
export class BootstrapManager {
constructor({ stateRoot, storeRoot, seedRoot, inventory }) {
this.stateRoot = resolve(stateRoot)
this.storeRoot = resolve(storeRoot)
this.seedRoot = resolve(seedRoot)
this.inventory = inventory
this.recordsRoot = join(this.stateRoot, 'records')
this.slotsPath = join(this.stateRoot, 'slots.json')
this.queue = Promise.resolve()
}
exclusive(operation) {
const result = this.queue.then(operation, operation)
this.queue = result.then(() => undefined, () => undefined)
return result
}
recordPath(id) {
return join(this.recordsRoot, `${id}.json`)
}
async writeRecord(value) {
const record = parseBootstrapRecord(value)
const bytes = canonicalJson(record)
const path = this.recordPath(record.id)
const existing = await readOptional(path)
if (existing === undefined) await durableCreate(path, bytes)
else if (!existing.equals(bytes)) throw new TrustError('Bootstrap Record ID identifies conflicting content')
return record
}
async record(id) {
const bytes = await readOptional(this.recordPath(id))
if (bytes === undefined) throw new TrustError(`Bootstrap Record ${id} does not exist`)
return parseBootstrapRecord(bytes)
}
async state() {
const bytes = await readOptional(this.slotsPath)
if (bytes === undefined) return Object.freeze({ generation: 0, current: null, previous: null })
return parseSlots(bytes, 'bootstrap-record', 'Bootstrap slots')
}
async commit(current, previous) {
const state = await this.state()
if (current === previous) previous = null
const slots = parseSlots({
schema: 1,
generation: state.generation + 1,
current,
previous,
}, 'bootstrap-record', 'Bootstrap slots')
await durableReplace(this.slotsPath, canonicalJson(slots))
return slots
}
async reconcileImage(record) {
return this.exclusive(async () => {
const image = await this.writeRecord(record)
const state = await this.state()
if (state.current === null) return this.commit(image.id, null)
const current = await this.record(state.current)
if (
this.inventory.authority === 'development'
&& image.targetSequence === 0
&& image.artifact.storage === 'image'
&& current.id !== image.id
) return this.commit(image.id, null)
if (current.targetSequence > image.targetSequence) {
if (
current.artifact.storage === 'image'
&& current.artifact.imageBuildId !== this.inventory.imageBuildId
) return this.commit(image.id, state.current)
return state
}
if (current.targetSequence < image.targetSequence) return this.commit(image.id, state.current)
if (current.version !== image.version || current.artifact.sha256 !== image.artifact.sha256) {
throw new TrustError('Bootstrap image conflicts with current content at the same targetSequence')
}
if (current.id === image.id) return state
return this.commit(image.id, state.current)
})
}
async promote(recordId) {
return this.exclusive(async () => {
await this.record(recordId)
const state = await this.state()
if (state.current === recordId) return state
return this.commit(recordId, state.current)
})
}
async rollback() {
return this.exclusive(async () => {
const state = await this.state()
if (state.previous === null) throw new TrustError('no previous Bootstrap exists')
await this.record(state.previous)
return this.commit(state.previous, state.current)
})
}
async resolveRecord(recordId) {
const record = await this.record(recordId)
const reference = record.artifact
let path
if (reference.storage === 'image') {
if (reference.imageBuildId !== this.inventory.imageBuildId) {
throw new TrustError('Bootstrap Image Reference belongs to a different image')
}
if (reference.id !== this.inventory.bootstrap.id || reference.sha256 !== this.inventory.bootstrap.sha256) {
throw new TrustError('Bootstrap Image Reference is absent from inventory')
}
path = join(this.seedRoot, 'bootstrap', reference.id)
} else {
path = join(this.storeRoot, reference.id)
}
const details = await lstat(path)
if (!details.isDirectory() || details.isSymbolicLink()) throw new TrustError('resolved Bootstrap must be an immutable directory')
if (await hashTree(path) !== reference.sha256) throw new TrustError('resolved Bootstrap content hash differs from its Record')
return Object.freeze({ record, path })
}
async current() {
const state = await this.state()
if (state.current === null) throw new TrustError('no current Bootstrap exists')
return this.resolveRecord(state.current)
}
async installArchive(archive, { version, targetSequence }) {
const temporary = join(this.storeRoot, `.${randomUUID()}.tmp`)
await mkdir(temporary, { recursive: true })
try {
const allowed = new Set(['platform', 'control-plane'])
const entries = (await tar(['-tzf', archive], true)).split('\n').filter(Boolean)
if (entries.length === 0 || entries.some(name => (
name.startsWith('/') || name.split('/').includes('..') || !allowed.has(name.split('/')[0])
))) throw new TrustError('Bootstrap archive contains an unsafe path')
await tar(['-xzf', archive, '--no-same-owner', '--no-same-permissions', '-C', temporary])
await lstat(join(temporary, 'platform', 'bootstrap', 'index.mjs'))
const sha256 = await hashTree(temporary)
const artifactId = `bootstrap-${sha256}`
const destination = join(this.storeRoot, artifactId)
try {
await rename(temporary, destination)
} catch (error) {
if (error?.code !== 'EEXIST' && error?.code !== 'ENOTEMPTY') throw error
if (await hashTree(destination) !== sha256) throw new TrustError('Bootstrap Store contains conflicting content')
}
return this.writeRecord(recordDocument({
schema: 1,
version,
bootstrapApi: 1,
targetSequence,
artifact: { storage: 'store', kind: 'bootstrap', id: artifactId, sha256 },
}))
} finally {
await rm(temporary, { recursive: true, force: true })
}
}
async stageArchive(archive, { version, targetSequence }) {
const record = await this.installArchive(archive, { version, targetSequence })
return this.exclusive(async () => {
const state = await this.state()
if (state.current === null) throw new TrustError('no current Bootstrap exists')
const current = await this.record(state.current)
const recordChanged = current.id !== record.id
const restartRequired = current.version !== record.version
|| current.artifact.sha256 !== record.artifact.sha256
if (recordChanged) await this.commit(record.id, state.current)
return Object.freeze({ record, recordChanged, restartRequired })
})
}
async collectGarbage() {
const state = await this.state()
const retainedRecords = new Set([state.current, state.previous].filter(Boolean))
const retainedAssets = new Set()
let names
try { names = await readdir(this.recordsRoot) } catch (error) {
if (error?.code === 'ENOENT') return Object.freeze({ assets: [], records: [] })
throw error
}
const records = []
for (const name of names.filter(name => name.endsWith('.json')).sort()) {
const record = parseBootstrapRecord(await readFile(join(this.recordsRoot, name)))
records.push(record)
if (retainedRecords.has(record.id) && record.artifact.storage === 'store') retainedAssets.add(record.artifact.id)
}
const removedAssets = []
for (const name of await readdir(this.storeRoot).catch(error => error?.code === 'ENOENT' ? [] : Promise.reject(error))) {
if (name.startsWith('.') || retainedAssets.has(name)) continue
const path = join(this.storeRoot, name)
const details = await lstat(path)
if (!details.isDirectory() || details.isSymbolicLink()) continue
await rm(path, { recursive: true, force: true })
removedAssets.push(name)
}
const removedRecords = []
for (const record of records) if (!retainedRecords.has(record.id)) {
await rm(this.recordPath(record.id), { force: true })
removedRecords.push(record.id)
}
return Object.freeze({ assets: removedAssets.sort(), records: removedRecords.sort() })
}
}
@@ -0,0 +1,73 @@
import { createServer } from 'node:http'
import { chmod, chown, mkdir, rm } from 'node:fs/promises'
import { dirname } from 'node:path'
const ROUTE = /^\/v1\/(dsh|user-plugin)\/(create|inspect|restore|remove)$/
const MAX_BODY_BYTES = 4096
async function jsonBody(request) {
const chunks = []
let size = 0
for await (const chunk of request) {
size += chunk.byteLength
if (size > MAX_BODY_BYTES) throw new Error('snapshot request body is too large')
chunks.push(chunk)
}
return size === 0 ? {} : JSON.parse(Buffer.concat(chunks).toString('utf8'))
}
function publicSnapshot(value) {
if (value === undefined || value === null || typeof value !== 'object') return value
const { archive: _archive, path: _path, ...snapshot } = value
return snapshot
}
function send(response, status, value) {
response.writeHead(status, { 'content-type': 'application/json; charset=utf-8' })
response.end(`${JSON.stringify(value)}\n`)
}
export function createSnapshotServer({ dshSnapshots, userPluginSnapshots, report = async () => {} }) {
return createServer((request, response) => {
void (async () => {
if (request.method !== 'POST') return send(response, 404, { error: 'not found' })
const match = ROUTE.exec(new URL(request.url ?? '/', 'http://snapshot.internal').pathname)
if (match === null) return send(response, 404, { error: 'not found' })
const [, scope, operation] = match
const body = await jsonBody(request)
let progress = null
const onProgress = value => { progress = value }
let result
if (scope === 'dsh') {
if (operation === 'create') result = await dshSnapshots.create({ ...body, onProgress })
else if (operation === 'inspect') result = await dshSnapshots.inspect(body.id)
else if (operation === 'restore') result = await dshSnapshots.restore(body.id, { onProgress })
else result = await dshSnapshots.remove(body.id)
} else {
if (operation === 'create') result = await userPluginSnapshots.create(body.id)
else if (operation === 'inspect') result = await userPluginSnapshots.inspect(body.id)
else if (operation === 'restore') result = await userPluginSnapshots.restore(body.id)
else result = await userPluginSnapshots.remove(body.id)
}
send(response, 200, { result: publicSnapshot(result), progress })
})().catch(async error => {
await report('snapshot.request.failed', {
error,
method: request.method ?? null,
pathname: request.url ?? null,
})
send(response, 500, { error: error.message })
})
})
}
export async function listenSnapshots(server, socketPath) {
await mkdir(dirname(socketPath), { recursive: true })
await rm(socketPath, { force: true })
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(socketPath, resolve)
})
if (process.getuid?.() === 0) await chown(socketPath, 0, 1000)
await chmod(socketPath, process.getuid?.() === 0 ? 0o660 : 0o600)
}
@@ -0,0 +1,339 @@
import { spawn } from 'node:child_process'
import { once } from 'node:events'
import { join } from 'node:path'
import { randomUUID } from 'node:crypto'
import { replaceRuntimeView } from '../../lib/paths.mjs'
function timeout(milliseconds, message) {
return new Promise((_, reject) => {
const timer = setTimeout(() => reject(new Error(message)), milliseconds)
timer.unref?.()
})
}
export function bootstrapLaunchCommand({ node, script, uid, gid }) {
if (uid === undefined && gid === undefined) return { executable: node, args: [script] }
if (!Number.isInteger(uid) || uid < 0 || !Number.isInteger(gid) || gid < 0) {
throw new Error('Bootstrap UID and GID must be non-negative integers')
}
return {
executable: '/usr/bin/setpriv',
args: [
`--reuid=${String(uid)}`,
`--regid=${String(gid)}`,
'--keep-groups',
'--',
node,
script,
],
}
}
export function bootstrapLaunchEnvironment({
environment = process.env,
dataRoot,
runRoot,
seedRoot,
bootstrapVersion,
user,
home,
proxyLaunchToken,
accessLaunchToken,
accessEvidence,
}) {
const inherited = { ...environment }
delete inherited.DSH_PROXY_MASTER_KEY
delete inherited.DSH_PROXY_LAUNCH_TOKEN
delete inherited.DSH_ACCESS_LAUNCH_TOKEN
delete inherited.DSH_PROXY_USERNAME
delete inherited.DSH_PROXY_PASSWORD
delete inherited.DSH_PLATFORM_PASSWORD
return {
...inherited,
...(home === undefined ? {} : {
HOME: home,
XDG_CACHE_HOME: join(home, '.cache'),
XDG_CONFIG_HOME: join(home, '.config'),
XDG_DATA_HOME: join(home, '.local', 'share'),
}),
...(user === undefined ? {} : { USER: user, LOGNAME: user }),
DSH_PLATFORM_DATA: dataRoot,
DSH_PLATFORM_RUN: runRoot,
...(seedRoot === undefined ? {} : { DSH_PLATFORM_SEED: seedRoot }),
DSH_BOOTSTRAP_VERSION: bootstrapVersion,
...(proxyLaunchToken === undefined ? {} : { DSH_PROXY_LAUNCH_TOKEN: proxyLaunchToken }),
...(accessLaunchToken === undefined ? {} : { DSH_ACCESS_LAUNCH_TOKEN: accessLaunchToken }),
DSH_LEGACY_INSTALLATION_EVIDENCE: JSON.stringify(accessEvidence),
}
}
export async function terminateChild(child, graceMs = 5_000) {
if (child.exitCode !== null || child.signalCode !== null) return
const exited = once(child, 'exit')
child.kill('SIGTERM')
const graceful = await Promise.race([
exited.then(() => true),
new Promise(resolve => setTimeout(() => resolve(false), graceMs)),
])
if (!graceful && child.exitCode === null && child.signalCode === null) {
child.kill('SIGKILL')
await once(child, 'exit')
}
}
export class BootstrapSupervisor {
constructor({
slots,
dataRoot,
runRoot = '/run/dsh-platform',
paths,
node = process.execPath,
entrypoint = 'index.mjs',
seedRoot,
readyTimeoutMs = 30_000,
spawnImpl = spawn,
report = async () => {},
uid,
gid,
user,
home,
proxyLaunchToken,
accessLaunchToken,
accessEvidence,
}) {
this.slots = slots
this.dataRoot = dataRoot
this.runRoot = runRoot
this.paths = paths
this.node = node
this.entrypoint = entrypoint
this.seedRoot = seedRoot
this.readyTimeoutMs = readyTimeoutMs
this.spawnImpl = spawnImpl
this.report = report
this.uid = uid
this.gid = gid
this.user = user
this.home = home
this.proxyLaunchToken = proxyLaunchToken
this.accessLaunchToken = accessLaunchToken
this.accessEvidence = accessEvidence
this.child = undefined
this.requests = new Map()
this.transition = Promise.resolve()
this.fatal = new Promise(resolveFatal => { this.resolveFatal = resolveFatal })
}
emit(message, fields = {}) {
void Promise.resolve().then(() => this.report(message, fields)).catch(() => {})
}
rejectRequests(error) {
for (const pending of this.requests.values()) pending.reject(error)
this.requests.clear()
}
enqueueTransition(operation) {
const pending = this.transition.then(operation, operation)
this.transition = pending.catch(() => {})
return pending
}
async launch(recordId) {
const startedAt = Date.now()
const resolved = await this.slots.resolveRecord(recordId)
this.emit('bootstrap.launch.started', {
recordId,
bootstrapVersion: resolved.record.version,
targetSequence: resolved.record.targetSequence,
})
if (this.paths !== undefined) await replaceRuntimeView(this.paths, 'bootstrap', resolved.path)
const command = bootstrapLaunchCommand({
node: this.node,
script: join(resolved.path, this.entrypoint),
uid: this.uid,
gid: this.gid,
})
const child = this.spawnImpl(command.executable, command.args, {
env: bootstrapLaunchEnvironment({
dataRoot: this.dataRoot,
runRoot: this.runRoot,
seedRoot: this.seedRoot,
bootstrapVersion: resolved.record.version,
user: this.user,
home: this.home,
proxyLaunchToken: this.proxyLaunchToken,
accessLaunchToken: this.accessLaunchToken,
accessEvidence: this.accessEvidence,
}),
stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
})
this.child = child
const ready = new Promise((resolve, reject) => {
child.once('error', reject)
child.once('exit', (code, signal) => reject(new Error(
`Bootstrap exited before readiness (code=${String(code)}, signal=${String(signal)})`,
)))
child.on('message', message => {
if (message?.type === 'ready' && message.bootstrapApi === 1) resolve()
if (message?.type === 'recovery-result' && typeof message.requestId === 'string') {
const pending = this.requests.get(message.requestId)
if (pending !== undefined) {
this.requests.delete(message.requestId)
if (message.error === undefined) pending.resolve(message.slots)
else pending.reject(new Error(message.error))
}
}
})
})
try {
await Promise.race([ready, timeout(this.readyTimeoutMs, 'Bootstrap readiness timed out')])
this.emit('bootstrap.ready', {
recordId,
bootstrapVersion: resolved.record.version,
pid: child.pid ?? null,
elapsedMs: Date.now() - startedAt,
})
child.once('exit', (code, signal) => {
if (this.child === child) {
const error = new Error(`Bootstrap exited unexpectedly (code=${String(code)}, signal=${String(signal)})`)
this.emit('bootstrap.exited', {
error,
recordId,
pid: child.pid ?? null,
code,
signal,
elapsedMs: Date.now() - startedAt,
})
this.rejectRequests(error)
this.resolveFatal(error)
}
})
return child
} catch (error) {
await terminateChild(child)
if (this.child === child) this.child = undefined
this.emit('bootstrap.launch.failed', {
error,
recordId,
bootstrapVersion: resolved.record.version,
pid: child.pid ?? null,
elapsedMs: Date.now() - startedAt,
})
throw error
}
}
startWithRollback() {
return this.enqueueTransition(() => this.startWithRollbackNow())
}
async startWithRollbackNow() {
const state = await this.slots.state()
if (state.current === null) throw new Error('no current Bootstrap is installed')
try {
return await this.launch(state.current)
} catch (candidateError) {
if (state.previous === null) {
this.emit('bootstrap.start.failed', { error: candidateError, failedRecordId: state.current })
throw candidateError
}
this.emit('bootstrap.rollback.started', {
level: 'warning',
error: candidateError,
failedRecordId: state.current,
targetRecordId: state.previous,
})
await this.slots.rollback()
try {
const child = await this.launch(state.previous)
this.emit('bootstrap.rollback.completed', {
level: 'warning',
failedRecordId: state.current,
recordId: state.previous,
pid: child.pid ?? null,
})
return child
} catch (rollbackError) {
const error = new AggregateError([candidateError, rollbackError], 'Bootstrap candidate and rollback both failed')
this.emit('bootstrap.rollback.failed', {
error,
failedRecordId: state.current,
targetRecordId: state.previous,
})
throw error
}
}
}
restart() {
return this.enqueueTransition(() => this.restartNow())
}
async restartNow() {
this.emit('bootstrap.restart.started', { pid: this.child?.pid ?? null })
const child = this.child
this.child = undefined
this.rejectRequests(new Error('Bootstrap restarted during recovery'))
if (child !== undefined) await terminateChild(child)
try {
const restarted = await this.startWithRollbackNow()
this.emit('bootstrap.restart.completed', { pid: restarted.pid ?? null })
return restarted
} catch (error) {
this.emit('bootstrap.restart.failed', { error })
throw error
}
}
stop() {
return this.enqueueTransition(() => this.stopNow())
}
async stopNow() {
const child = this.child
this.emit('bootstrap.stop.started', { pid: child?.pid ?? null })
this.child = undefined
this.rejectRequests(new Error('Bootstrap stopped during recovery'))
if (child !== undefined) await terminateChild(child)
this.emit('bootstrap.stop.completed', {
pid: child?.pid ?? null,
code: child?.exitCode ?? null,
signal: child?.signalCode ?? null,
})
}
recoverImageBaseline(timeoutMs = 60_000) {
if (this.child === undefined || this.child.connected !== true) throw new Error('Bootstrap is unavailable for recovery')
const requestId = randomUUID()
this.emit('bootstrap.recovery.started', { requestId })
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
this.requests.delete(requestId)
const error = new Error('image baseline recovery timed out')
this.emit('bootstrap.recovery.failed', { error, requestId })
reject(error)
}, timeoutMs)
this.requests.set(requestId, {
resolve: value => {
clearTimeout(timer)
this.emit('bootstrap.recovery.completed', { requestId })
resolve(value)
},
reject: error => {
clearTimeout(timer)
this.emit('bootstrap.recovery.failed', { error, requestId })
reject(error)
},
})
this.child.send({ type: 'recover-image-baseline', requestId }, error => {
if (error !== null && error !== undefined) {
clearTimeout(timer)
this.requests.delete(requestId)
this.emit('bootstrap.recovery.failed', { error, requestId })
reject(error)
}
})
})
}
}
@@ -0,0 +1,26 @@
import { TrustError } from '../../lib/validation.mjs'
import { parseStable } from '../../lib/contracts.mjs'
import { verifyDetached } from './signature.mjs'
export function parseReleaseTarget(bytes) {
return parseStable(bytes)
}
export function verifyReleaseTarget(bytes, signature, keyring) {
const parsed = parseReleaseTarget(bytes)
if (parsed.keyringGeneration !== keyring.generation) {
throw new TrustError('release target requires a different keyring generation')
}
const keyId = verifyDetached(bytes, signature, keyring.current.publicKey, 'release target')
return Object.freeze({ ...parsed, keyId })
}
export function validateTargetTransition(previousBytes, previous, nextBytes, next) {
if (next.targetSequence < previous.targetSequence) {
throw new TrustError('release target sequence cannot decrease', 'TRUST_ROLLBACK')
}
if (next.targetSequence === previous.targetSequence && !previousBytes.equals(nextBytes)) {
throw new TrustError('release target sequence cannot identify different content')
}
return next
}
@@ -0,0 +1,121 @@
import { createServer } from 'node:http'
import { chmod, chown, mkdir, rm } from 'node:fs/promises'
import { dirname } from 'node:path'
const MAX_BODY = 64 * 1024
async function jsonBody(request) {
const chunks = []
let bytes = 0
for await (const chunk of request) {
bytes += chunk.byteLength
if (bytes > MAX_BODY) throw new Error('request body is too large')
chunks.push(chunk)
}
return JSON.parse(Buffer.concat(chunks).toString('utf8'))
}
function send(response, status, value) {
response.writeHead(status, { 'content-type': 'application/json; charset=utf-8' })
response.end(`${JSON.stringify(value)}\n`)
}
export function createTrustServer({ ledger, objects, stageBootstrap, collectBootstrap, report = async () => {} }) {
const record = (message, fields) => Promise.resolve().then(() => report(message, fields)).catch(() => {})
return createServer(async (request, response) => {
let pathname = 'invalid-url'
try {
const url = new URL(request.url ?? '/', 'http://stage0.internal')
pathname = url.pathname
if (request.method === 'GET' && url.pathname === '/v1/status') {
const keyring = await ledger.currentKeyring()
const target = await ledger.currentTarget()
send(response, 200, {
keyringGeneration: keyring?.value.generation ?? null,
targetSequence: target?.value.targetSequence ?? null,
officialDshVersion: (await ledger.currentOfficialDsh().catch(() => undefined))?.value.version ?? null,
})
return
}
if (request.method === 'GET' && url.pathname === '/v1/receipts/active') {
send(response, 200, { receipts: await objects.activeReceipts() })
return
}
if (request.method !== 'POST') {
send(response, 404, { error: 'not found' })
return
}
const body = await jsonBody(request)
if (url.pathname === '/v1/keyring') {
const value = await ledger.acceptKeyring(Buffer.from(body.document, 'base64'), body.signature)
await objects.reconcileRevocations(value)
await record('keyring.accepted', { generation: value.generation })
send(response, 200, { generation: value.generation })
} else if (url.pathname === '/v1/target') {
const value = await ledger.acceptTarget(Buffer.from(body.document, 'base64'), body.signature)
await objects.reconcileRevocations((await ledger.currentKeyring()).value)
await record('target.accepted', { targetSequence: value.targetSequence })
send(response, 200, { targetSequence: value.targetSequence })
} else if (url.pathname === '/v1/artifacts/import') {
const receipt = body.parentReceipt === null || body.parentReceipt === undefined
? await objects.importFromTarget(body.artifactId, body.sourcePath)
: await objects.importFromManifest(body.parentReceipt, body.artifactId, body.sourcePath)
await record('artifact.imported', { artifactId: body.artifactId, objectSha256: receipt.objectSha256 })
send(response, 200, receipt)
} else if (url.pathname === '/v1/dsh/ensure') {
if (
body === null || typeof body !== 'object' || Array.isArray(body)
|| Object.keys(body).length !== 3
|| !Object.hasOwn(body, 'version')
|| !Object.hasOwn(body, 'metadataPath')
|| !Object.hasOwn(body, 'tarballPath')
) {
throw new Error('official DSH import requires fixed untrusted sources')
}
const receipt = await objects.ensureOfficialDsh(body.version, body.metadataPath, body.tarballPath)
await record('official-dsh.imported', { dshVersion: body.version, objectSha256: receipt.objectSha256 })
send(response, 200, receipt)
} else if (url.pathname === '/v1/manifests/accept') {
const receipt = await objects.acceptManifest(body.receipt, body.signatureReceipt)
await record('manifest.accepted', { objectSha256: receipt.objectSha256 })
send(response, 200, receipt)
} else if (url.pathname === '/v1/activate') {
const receipts = await objects.activate(body.receipts)
await record('receipts.activated', { receiptCount: receipts.length })
send(response, 200, { receipts })
} else if (url.pathname === '/v1/objects/collect') {
const removed = await objects.collectGarbage()
await record('objects.collected', { removedCount: removed.length })
send(response, 200, { removed })
} else if (url.pathname === '/v1/bootstrap/stage' && stageBootstrap !== undefined) {
const result = await stageBootstrap(body.receipt, body.version)
send(response, 202, {
status: result?.restartRequired === false ? 'staged' : 'switching',
version: body.version,
...(result ?? {}),
})
} else if (url.pathname === '/v1/bootstrap/collect' && collectBootstrap !== undefined) {
send(response, 200, await collectBootstrap())
} else {
send(response, 404, { error: 'not found' })
}
} catch (error) {
await record('trust.request.failed', { error, method: request.method ?? null, pathname })
send(response, 400, {
error: error instanceof Error ? error.message : 'invalid request',
...(typeof error?.code === 'string' ? { code: error.code } : {}),
})
}
})
}
export async function listenUnix(server, socketPath, { mode = 0o600, uid, gid } = {}) {
await mkdir(dirname(socketPath), { recursive: true })
await rm(socketPath, { force: true })
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(socketPath, resolve)
})
if (uid !== undefined || gid !== undefined) await chown(socketPath, uid ?? -1, gid ?? -1)
await chmod(socketPath, mode)
}
@@ -0,0 +1,23 @@
import { lchown, lstat, mkdir } from 'node:fs/promises'
import { isAbsolute, resolve } from 'node:path'
export async function prepareUserWritableRoot(path, { uid, gid, label = 'User-writable root' } = {}) {
if (typeof path !== 'string' || !isAbsolute(path)) throw new Error(`${label} must be an absolute path`)
const root = resolve(path)
if (root === '/') throw new Error(`${label} cannot be the filesystem root`)
await mkdir(root, { recursive: true })
const metadata = await lstat(root)
if (!metadata.isDirectory() || metadata.isSymbolicLink()) throw new Error(`${label} must be a directory`)
if (uid !== undefined || gid !== undefined) await lchown(root, uid ?? -1, gid ?? -1)
return root
}
export async function prepareUserWritableRoots({ dshHome, defaultWorkspace, uid, gid } = {}) {
const roots = []
for (const [label, path] of [['DSH_HOME', dshHome], ['DSH_DEFAULT_WORKSPACE', defaultWorkspace]]) {
const root = resolve(path)
if (roots.includes(root)) continue
roots.push(await prepareUserWritableRoot(path, { uid, gid, label }))
}
return Object.freeze(roots)
}
@@ -0,0 +1,67 @@
import { createServer } from 'node:http'
import { chmod, chown, mkdir, rm } from 'node:fs/promises'
import { dirname } from 'node:path'
const ACTION_ROUTE = '/v1/action'
const MAX_BODY_BYTES = 4096
async function jsonBody(request) {
const chunks = []
let size = 0
for await (const chunk of request) {
size += chunk.byteLength
if (size > MAX_BODY_BYTES) {
const error = new Error('User Skill request body is too large')
error.statusCode = 413
throw error
}
chunks.push(chunk)
}
return size === 0 ? {} : JSON.parse(Buffer.concat(chunks).toString('utf8'))
}
function send(response, status, value) {
response.writeHead(status, { 'content-type': 'application/json; charset=utf-8' })
response.end(`${JSON.stringify(value)}\n`)
}
function actionBody(value) {
if (value === null || typeof value !== 'object' || Array.isArray(value)
|| Object.keys(value).sort().join(',') !== 'action,entryId,revision') {
const error = new Error('User Skill request is invalid')
error.statusCode = 400
throw error
}
return value
}
export function createUserSkillServer({ manager, report = async () => {} }) {
if (manager === undefined || typeof manager.configure !== 'function') throw new Error('User Skill manager is required')
return createServer((request, response) => {
void (async () => {
const pathname = new URL(request.url ?? '/', 'http://user-skills.internal').pathname
if (request.method !== 'POST' || pathname !== ACTION_ROUTE) return send(response, 404, { error: 'not found' })
send(response, 200, await manager.configure(actionBody(await jsonBody(request))))
})().catch(async error => {
await report('user-skill.privileged-action.failed', {
error,
method: request.method ?? null,
pathname: request.url ?? null,
})
send(response, Number.isInteger(error?.statusCode) ? error.statusCode : 500, {
error: error instanceof Error ? error.message : String(error),
})
})
})
}
export async function listenUserSkills(server, socketPath) {
await mkdir(dirname(socketPath), { recursive: true })
await rm(socketPath, { force: true })
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(socketPath, resolve)
})
if (process.getuid?.() === 0) await chown(socketPath, 0, 1000)
await chmod(socketPath, process.getuid?.() === 0 ? 0o660 : 0o600)
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,353 @@
import assert from 'node:assert/strict'
import { createHash } from 'node:crypto'
import { mkdir, mkdtemp, readFile, stat, symlink, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { MANIFEST_MEDIA_TYPE, SIGNATURE_MEDIA_TYPE, VerifiedObjectStore } from '../stage0/lib/artifacts.mjs'
import { TrustLedger } from '../stage0/lib/ledger.mjs'
import { document, officialDshPolicy, keyPair, keyring, registryCandidate, registryKeyPair, signature } from './helpers.mjs'
function descriptor(id, content, mediaType = 'application/octet-stream') {
return {
id,
mediaType,
sha256: createHash('sha256').update(content).digest('hex'),
size: content.byteLength,
url: `https://github.com/example/releases/download/v1/${id}`,
}
}
function releaseTarget(generation, sequence, artifacts, policy = officialDshPolicy()) {
const selected = artifacts[0]
const signatureArtifact = descriptor('stable-signature', Buffer.from('signature'), 'application/vnd.dsh-platform.signature.v1+json')
const targetArtifacts = artifacts.some(artifact => artifact.id === signatureArtifact.id)
? artifacts
: [...artifacts, signatureArtifact]
return {
schema: 1,
updateApi: 1,
keyringGeneration: generation,
targetSequence: sequence,
issuedAt: '2026-08-19T00:00:00.000Z',
artifacts: targetArtifacts,
desired: {
bootstrap: { version: '1.0.0', manifestArtifactId: selected.id, signatureArtifactId: signatureArtifact.id },
environment: { version: '1.0.0', manifestArtifactId: selected.id, signatureArtifactId: signatureArtifact.id },
dsh: {
version: '0.1.0-rc.7',
integrity: `sha512-${Buffer.alloc(64).toString('base64')}`,
},
},
officialDshPolicy: policy,
}
}
function manifest(generation, sequence, artifacts) {
return {
schema: 1,
manifestType: 'environment',
version: '1.0.0',
keyringGeneration: generation,
targetSequence: sequence,
issuedAt: '2026-08-19T00:00:00.000Z',
artifacts,
bootstrapApi: 1,
components: [],
patches: [],
systemPlugins: [],
}
}
async function fixture(targetArtifacts, policy = officialDshPolicy()) {
const directory = await mkdtemp(join(tmpdir(), 'dsh-object-store-'))
const recovery = keyPair()
const current = keyPair()
const next = keyPair()
const ledger = new TrustLedger(join(directory, 'trust'), recovery.publicKey)
const ringBytes = document(keyring(1, current, next))
await ledger.acceptKeyring(ringBytes, signature(ringBytes, recovery))
const artifacts = typeof targetArtifacts === 'function' ? targetArtifacts(current) : targetArtifacts
const targetBytes = document(releaseTarget(1, 1, artifacts, policy))
await ledger.acceptTarget(targetBytes, signature(targetBytes, current))
const untrustedRoot = join(directory, 'downloads', 'untrusted')
await mkdir(untrustedRoot, { recursive: true })
return {
current,
directory,
ledger,
next,
recovery,
store: new VerifiedObjectStore({ root: join(directory, 'trust'), untrustedRoot, ledger }),
untrustedRoot,
}
}
test('empty activation succeeds before release trust is initialized', async () => {
const directory = await mkdtemp(join(tmpdir(), 'dsh-empty-activation-'))
const recovery = keyPair()
const ledger = new TrustLedger(join(directory, 'trust'), recovery.publicKey)
const untrustedRoot = join(directory, 'downloads', 'untrusted')
await mkdir(untrustedRoot, { recursive: true })
const store = new VerifiedObjectStore({ root: join(directory, 'trust'), untrustedRoot, ledger })
assert.deepEqual(await store.activate([]), [])
})
async function importSignature(store, untrustedRoot, signatureBytes) {
const path = join(untrustedRoot, 'manifest.sig.json')
await writeFile(path, signatureBytes)
return store.importFromTarget('stable-signature', path)
}
async function officialSources(untrustedRoot, candidate, content, suffix = '') {
const metadataPath = join(untrustedRoot, `official-dsh${suffix}.json`)
const tarballPath = join(untrustedRoot, `official-dsh${suffix}.tgz`)
await writeFile(metadataPath, JSON.stringify({ versions: { [candidate.version]: candidate } }))
await writeFile(tarballPath, content)
return { metadataPath, tarballPath }
}
test('imports only target-authorized bytes from the untrusted directory', async () => {
const content = Buffer.from('verified artifact')
const expected = descriptor('gateway', content)
const { store, untrustedRoot, directory } = await fixture([expected])
const source = join(untrustedRoot, 'gateway.bin')
await writeFile(source, content)
const receipt = await store.importFromTarget('gateway', source)
assert.equal(receipt.objectSha256, expected.sha256)
assert.deepEqual(await readFile(receipt.path), content)
assert.equal((await stat(receipt.path)).mode & 0o777, 0o444)
await assert.rejects(store.importFromTarget('missing', source), /not authorized/)
await assert.rejects(store.importFromTarget('gateway', join(directory, 'outside.bin')), /untrusted/)
})
test('treats receipts written before authority typing as Stable receipts', async () => {
const content = Buffer.from('legacy stable artifact')
const { store, untrustedRoot } = await fixture([descriptor('legacy', content)])
const source = join(untrustedRoot, 'legacy')
await writeFile(source, content)
const receipt = await store.importFromTarget('legacy', source)
const persisted = JSON.parse(await readFile(store.receiptPath(receipt.token), 'utf8'))
delete persisted.authorityType
await writeFile(store.receiptPath(receipt.token), `${JSON.stringify(persisted)}\n`)
assert.equal((await store.readReceipt(receipt.token)).authorityType, 'stable')
})
test('verifies updater-downloaded official DSH metadata and tarball without networking', async () => {
const content = Buffer.from('official tarball')
const registry = registryKeyPair()
const candidate = registryCandidate(registry, '0.1.0-rc.8', content)
const policy = officialDshPolicy(registry)
const { current, ledger, untrustedRoot, directory } = await fixture(
[descriptor('stable-only', Buffer.alloc(0))],
policy,
)
const store = new VerifiedObjectStore({ root: join(directory, 'trust'), untrustedRoot, ledger })
const advanced = document(releaseTarget(1, 2, [descriptor('stable-only', Buffer.alloc(0))], policy))
await ledger.acceptTarget(advanced, signature(advanced, current))
const sources = await officialSources(untrustedRoot, candidate, content)
const receipt = await store.ensureOfficialDsh(candidate.version, sources.metadataPath, sources.tarballPath)
assert.equal(receipt.authorityType, 'official-dsh')
assert.equal(receipt.authorityVersion, candidate.version)
assert.equal(receipt.targetSequence, 2)
assert.deepEqual(await readFile(receipt.path), content)
await store.activate([receipt.token])
assert.equal((await store.readReceipt(receipt.token)).status, 'active')
})
test('official DSH import accepts only regular files inside the untrusted directory', async () => {
const content = Buffer.from('official tarball')
const registry = registryKeyPair()
const candidate = registryCandidate(registry, '0.1.0-rc.8', content)
const { store, untrustedRoot, directory } = await fixture(
[descriptor('stable-only', Buffer.alloc(0))],
officialDshPolicy(registry),
)
const outside = await officialSources(directory, candidate, content)
await assert.rejects(store.ensureOfficialDsh(candidate.version, outside.metadataPath, outside.tarballPath), /untrusted/)
const sources = await officialSources(untrustedRoot, candidate, content)
const metadataLink = join(untrustedRoot, 'metadata-link.json')
await symlink(sources.metadataPath, metadataLink)
await assert.rejects(store.ensureOfficialDsh(candidate.version, metadataLink, sources.tarballPath), /ELOOP|symbolic/i)
})
test('official DSH import rejects malformed metadata and mismatched tarball bytes', async () => {
const content = Buffer.from('official tarball')
const registry = registryKeyPair()
const candidate = registryCandidate(registry, '0.1.0-rc.8', content)
const { store, untrustedRoot } = await fixture(
[descriptor('stable-only', Buffer.alloc(0))],
officialDshPolicy(registry),
)
const malformed = join(untrustedRoot, 'malformed.json')
const tarball = join(untrustedRoot, 'official.tgz')
await writeFile(malformed, '{')
await writeFile(tarball, content)
await assert.rejects(store.ensureOfficialDsh(candidate.version, malformed, tarball), /valid JSON/)
const sources = await officialSources(untrustedRoot, candidate, Buffer.from('different bytes'), '-mismatch')
await assert.rejects(
store.ensureOfficialDsh(candidate.version, sources.metadataPath, sources.tarballPath),
{ code: 'TRUST_ARTIFACT_MISMATCH' },
)
})
test('revokes staged official DSH receipts after Registry delegation changes but retains active objects', async () => {
const content = Buffer.from('official tarball')
const registry = registryKeyPair()
const replacement = registryKeyPair()
const candidate = registryCandidate(registry, '0.1.0-rc.8', content)
const { current, directory, ledger, untrustedRoot } = await fixture(
[descriptor('stable-only', Buffer.alloc(0))],
officialDshPolicy(registry),
)
const store = new VerifiedObjectStore({ root: join(directory, 'trust'), untrustedRoot, ledger })
const first = await officialSources(untrustedRoot, candidate, content, '-active')
const active = await store.ensureOfficialDsh(candidate.version, first.metadataPath, first.tarballPath)
await store.activate([active.token])
const second = await officialSources(untrustedRoot, candidate, content, '-staged')
const staged = await store.ensureOfficialDsh(candidate.version, second.metadataPath, second.tarballPath)
const advanced = document(releaseTarget(
1,
2,
[descriptor('stable-only', Buffer.alloc(0))],
officialDshPolicy(replacement),
))
await ledger.acceptTarget(advanced, signature(advanced, current))
await store.reconcileRevocations((await ledger.currentKeyring()).value)
assert.equal((await store.readReceipt(active.token)).status, 'active')
assert.equal((await store.readReceipt(staged.token)).status, 'revoked')
})
test('rejects mismatched content and symbolic-link sources', async () => {
const expected = descriptor('gateway', Buffer.from('expected'))
const { store, untrustedRoot } = await fixture([expected])
const bad = join(untrustedRoot, 'bad.bin')
await writeFile(bad, 'different')
await assert.rejects(store.importFromTarget('gateway', bad), { code: 'TRUST_ARTIFACT_MISMATCH' })
const real = join(untrustedRoot, 'real.bin')
const link = join(untrustedRoot, 'link.bin')
await writeFile(real, 'expected')
await symlink(real, link)
await assert.rejects(store.importFromTarget('gateway', link), /ELOOP|symbolic/i)
})
test('verifies a signed manifest before authorizing its child artifacts', async () => {
const component = Buffer.from('component')
const manifestBytes = document(manifest(1, 1, [descriptor('component', component)]))
const manifestDescriptor = descriptor('environment-manifest', manifestBytes, MANIFEST_MEDIA_TYPE)
let signatureBytes
const { store, untrustedRoot } = await fixture(current => {
signatureBytes = document(signature(manifestBytes, current))
return [manifestDescriptor, descriptor('stable-signature', signatureBytes, SIGNATURE_MEDIA_TYPE)]
})
const manifestPath = join(untrustedRoot, 'manifest.json')
await writeFile(manifestPath, manifestBytes)
const imported = await store.importFromTarget('environment-manifest', manifestPath)
await assert.rejects(store.importFromManifest(imported.token, 'component', manifestPath), /verified manifest/)
const signatureReceipt = await importSignature(store, untrustedRoot, signatureBytes)
await store.acceptManifest(imported.token, signatureReceipt.token)
const componentPath = join(untrustedRoot, 'component.bin')
await writeFile(componentPath, component)
const child = await store.importFromManifest(imported.token, 'component', componentPath)
assert.equal(child.parentReceipt, imported.token)
})
test('revokes unactivated receipts after key rotation but retains active and previous objects', async () => {
const one = Buffer.from('one')
const two = Buffer.from('two')
const third = Buffer.from('three')
const descriptors = [descriptor('one', one), descriptor('two', two), descriptor('three', third)]
const { current, next, recovery, ledger, store, untrustedRoot } = await fixture(descriptors)
for (const [name, content] of [['one', one], ['two', two], ['three', third]]) {
await writeFile(join(untrustedRoot, name), content)
}
const first = await store.importFromTarget('one', join(untrustedRoot, 'one'))
const second = await store.importFromTarget('two', join(untrustedRoot, 'two'))
const unactivated = await store.importFromTarget('three', join(untrustedRoot, 'three'))
await store.activate([first.token])
await store.activate([second.token])
const future = keyPair()
const rotated = document(keyring(2, next, future, [current.keyId]))
const rotatedValue = await ledger.acceptKeyring(rotated, signature(rotated, recovery))
await assert.rejects(store.importFromTarget('three', join(untrustedRoot, 'three')), { code: 'TRUST_REVOKED' })
await assert.rejects(store.activate([unactivated.token]), { code: 'TRUST_REVOKED' })
const revoked = await store.reconcileRevocations(rotatedValue)
assert.deepEqual(revoked, [unactivated.token])
assert.equal((await store.readReceipt(first.token)).status, 'previous')
assert.equal((await store.readReceipt(second.token)).status, 'active')
await store.collectGarbage()
await assert.rejects(store.readReceipt(unactivated.token), /does not exist/)
assert.deepEqual(await readFile(store.objectPath(descriptors[0].sha256)), one)
assert.deepEqual(await readFile(store.objectPath(descriptors[1].sha256)), two)
})
test('activation includes manifest ancestors and rollback swaps current and previous', async () => {
const component = Buffer.from('component')
const other = Buffer.from('other')
const manifestBytes = document(manifest(1, 1, [descriptor('component', component)]))
const manifestDescriptor = descriptor('environment-manifest', manifestBytes, MANIFEST_MEDIA_TYPE)
let signatureBytes
const { store, untrustedRoot } = await fixture(current => {
signatureBytes = document(signature(manifestBytes, current))
return [
manifestDescriptor,
descriptor('stable-signature', signatureBytes, SIGNATURE_MEDIA_TYPE),
descriptor('other', other),
]
})
await writeFile(join(untrustedRoot, 'manifest'), manifestBytes)
await writeFile(join(untrustedRoot, 'component'), component)
await writeFile(join(untrustedRoot, 'other'), other)
const parent = await store.importFromTarget('environment-manifest', join(untrustedRoot, 'manifest'))
const signatureReceipt = await importSignature(store, untrustedRoot, signatureBytes)
await store.acceptManifest(parent.token, signatureReceipt.token)
const child = await store.importFromManifest(parent.token, 'component', join(untrustedRoot, 'component'))
const selected = await store.activate([child.token])
assert.deepEqual(new Set(selected), new Set([parent.token, child.token]))
assert.equal((await store.readReceipt(parent.token)).status, 'active')
const nextReceipt = await store.importFromTarget('other', join(untrustedRoot, 'other'))
await store.activate([nextReceipt.token])
assert.equal((await store.readReceipt(parent.token)).status, 'previous')
await store.rollback()
assert.equal((await store.readReceipt(parent.token)).status, 'active')
assert.equal((await store.readReceipt(nextReceipt.token)).status, 'previous')
})
test('rejects staged receipts after the current Release Key advances targetSequence', async () => {
const content = Buffer.from('old target artifact')
const expected = descriptor('artifact', content)
const { current, ledger, store, untrustedRoot } = await fixture([expected])
const source = join(untrustedRoot, 'artifact')
await writeFile(source, content)
const receipt = await store.importFromTarget('artifact', source)
const advanced = document(releaseTarget(1, 2, [expected]))
await ledger.acceptTarget(advanced, signature(advanced, current))
await assert.rejects(store.activate([receipt.token]), { code: 'TRUST_REVOKED' })
})
test('rejects child imports through a manifest from an older targetSequence', async () => {
const component = Buffer.from('component')
const componentDescriptor = descriptor('component', component)
const manifestBytes = document(manifest(1, 1, [componentDescriptor]))
const manifestDescriptor = descriptor('environment-manifest', manifestBytes, MANIFEST_MEDIA_TYPE)
let signatureBytes
const { current, ledger, store, untrustedRoot } = await fixture(release => {
signatureBytes = document(signature(manifestBytes, release))
return [manifestDescriptor, descriptor('stable-signature', signatureBytes, SIGNATURE_MEDIA_TYPE)]
})
await writeFile(join(untrustedRoot, 'manifest'), manifestBytes)
await writeFile(join(untrustedRoot, 'component'), component)
const parent = await store.importFromTarget('environment-manifest', join(untrustedRoot, 'manifest'))
const signatureReceipt = await importSignature(store, untrustedRoot, signatureBytes)
await store.acceptManifest(parent.token, signatureReceipt.token)
const advanced = document(releaseTarget(1, 2, [manifestDescriptor]))
await ledger.acceptTarget(advanced, signature(advanced, current))
await assert.rejects(
store.importFromManifest(parent.token, 'component', join(untrustedRoot, 'component')),
{ code: 'TRUST_REVOKED' },
)
})
@@ -0,0 +1,80 @@
import assert from 'node:assert/strict'
import { mkdtemp } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import {
AUTOMATIC_CHECK_INTERVALS,
AutomaticCheckStateStore,
} from '../../control-plane/modules/updater/lib/automatic-check.mjs'
function target(targetSequence, dsh) {
return {
targetSequence,
desired: {
dsh: { version: dsh },
environment: { version: '1.0.0' },
},
}
}
test('automatic checks use persistent defaults and fixed user-facing intervals', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-automatic-check-'))
const store = new AutomaticCheckStateStore(join(root, 'automatic-check.json'))
assert.deepEqual(AUTOMATIC_CHECK_INTERVALS, [3_600, 10_800, 21_600, 43_200, 86_400])
assert.deepEqual(await store.read(), {
schema: 1,
automaticCheck: { enabled: true, intervalSeconds: 21_600, notificationsEnabled: true },
latestAutomatic: { stable: null, upstream: null },
})
assert.throws(() => store.configure({ enabled: true, intervalSeconds: 60, notificationsEnabled: true }), /interval/)
})
test('automatic checks retain only the newest Stable and Upstream candidates', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-automatic-candidates-'))
const store = new AutomaticCheckStateStore(join(root, 'automatic-check.json'), () => new Date('2026-08-20T08:00:00Z'))
const current = { targetSequence: 1, dsh: '0.1.0-rc.7', environment: '1.0.0' }
await store.record({ channel: 'experimental', current, target: target(2, '0.1.0-rc.8'), upstream: { version: '0.1.0-rc.10' }, stableAvailable: true })
await store.record({ channel: 'experimental', current, target: target(3, '0.1.0-rc.9'), upstream: { version: '0.1.0-rc.11' }, stableAvailable: true })
await store.record({ channel: 'experimental', current, target: target(2, '0.1.0-rc.8'), upstream: { version: '0.1.0-rc.10' }, stableAvailable: true })
const state = await store.read()
assert.equal(state.latestAutomatic.stable.targetSequence, 3)
assert.equal(state.latestAutomatic.upstream.version, '0.1.0-rc.11')
await store.clearSatisfied({ targetSequence: 3, dsh: '0.1.0-rc.11', environment: '1.0.0' })
assert.deepEqual((await store.read()).latestAutomatic, { stable: null, upstream: null })
})
test('Stable channel, Holds, and disabled settings suppress notification candidates', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-automatic-suppression-'))
const store = new AutomaticCheckStateStore(join(root, 'automatic-check.json'))
const current = { targetSequence: 1, dsh: '0.1.0-rc.7', environment: '1.0.0' }
const stable = target(2, '0.1.0-rc.8')
await store.record({ channel: 'experimental', current, target: stable, upstream: { version: '0.1.0-rc.10' }, stableAvailable: true })
await store.record({ channel: 'stable', current, target: stable, upstream: { version: '0.1.0-rc.11' }, stableAvailable: true })
assert.equal((await store.read()).latestAutomatic.upstream, null)
await store.record({
channel: 'experimental', current, target: stable, upstream: { version: '0.1.0-rc.11' },
stableAvailable: true, holds: [{ type: 'version', dshVersion: '0.1.0-rc.11' }],
})
assert.equal((await store.read()).latestAutomatic.upstream, null)
await store.configure({ enabled: false, intervalSeconds: 3_600, notificationsEnabled: true })
assert.deepEqual((await store.read()).latestAutomatic, { stable: null, upstream: null })
})
test('a sequence-only metadata advance does not advertise a Stable update', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-automatic-ahead-'))
const store = new AutomaticCheckStateStore(join(root, 'automatic-check.json'))
await store.record({
channel: 'experimental',
current: { targetSequence: 2, dsh: '0.1.0-rc.10', environment: '1.0.0' },
target: target(3, '0.1.0-rc.10'),
upstream: null,
stableAvailable: false,
})
assert.equal((await store.read()).latestAutomatic.stable, null)
})
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,67 @@
import assert from 'node:assert/strict'
import { chmod, mkdir, mkdtemp, readFile, symlink, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { spawnSync } from 'node:child_process'
import test from 'node:test'
async function fixture(environment = '2.3.4') {
const root = await mkdtemp(join(tmpdir(), 'dsh-bump-environment-'))
const definitionPath = join(root, 'container/environment/definition.json')
const targetPath = join(root, 'release/supported-target.json')
const scriptPath = join(root, 'scripts/bump-environment.sh')
await mkdir(join(root, 'container/environment'), { recursive: true })
await mkdir(join(root, 'container/platform'), { recursive: true })
await mkdir(join(root, 'release'), { recursive: true })
await mkdir(join(root, 'scripts'), { recursive: true })
await symlink(new URL('../lib', import.meta.url).pathname, join(root, 'container/platform/lib'))
await symlink(new URL('../tools', import.meta.url).pathname, join(root, 'container/platform/tools'))
await writeFile(scriptPath, await readFile(new URL('../../../scripts/bump-environment.sh', import.meta.url)))
await chmod(scriptPath, 0o755)
await writeFile(definitionPath, `${JSON.stringify({ version: environment, bootstrapApi: 1 }, null, 2)}\n`)
await writeFile(targetPath, `${JSON.stringify({
schema: 1,
latestSupportedDsh: '9.8.7-rc.6',
environment,
}, null, 2)}\n`)
return { definitionPath, root, scriptPath, targetPath }
}
function run(paths, version) {
return spawnSync(paths.scriptPath, [version], { cwd: paths.root, encoding: 'utf8' })
}
test('bumps both authoritative Environment versions and is idempotent', async () => {
const paths = await fixture()
let result = run(paths, '2.4.0')
assert.equal(result.status, 0, result.stderr)
assert.deepEqual(JSON.parse(result.stdout), { changed: true, previousVersion: '2.3.4', version: '2.4.0' })
assert.equal(JSON.parse(await readFile(paths.definitionPath, 'utf8')).version, '2.4.0')
const target = JSON.parse(await readFile(paths.targetPath, 'utf8'))
assert.equal(target.environment, '2.4.0')
assert.equal(target.latestSupportedDsh, '9.8.7-rc.6')
result = run(paths, '2.4.0')
assert.equal(result.status, 0, result.stderr)
assert.deepEqual(JSON.parse(result.stdout), { changed: false, version: '2.4.0' })
})
test('rejects invalid, rollback, and mismatched Environment versions without writing', async () => {
const paths = await fixture()
let result = run(paths, 'not-a-version')
assert.notEqual(result.status, 0)
assert.match(result.stderr, /semantic version/)
result = run(paths, '2.3.3')
assert.notEqual(result.status, 0)
assert.match(result.stderr, /roll back/)
assert.equal(JSON.parse(await readFile(paths.definitionPath, 'utf8')).version, '2.3.4')
assert.equal(JSON.parse(await readFile(paths.targetPath, 'utf8')).environment, '2.3.4')
await writeFile(paths.targetPath, `${JSON.stringify({
schema: 1,
latestSupportedDsh: '9.8.7-rc.6',
environment: '2.3.3',
}, null, 2)}\n`)
result = run(paths, '2.4.0')
assert.notEqual(result.status, 0)
assert.match(result.stderr, /does not match/)
})
@@ -0,0 +1,73 @@
import assert from 'node:assert/strict'
import { mkdtemp } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { ChannelStateStore, planDesiredState } from '../../control-plane/modules/updater/lib/channel-state.mjs'
const current = { dsh: '0.1.0-rc.7', environment: 'env-1', runtime: 'runtime-a' }
const supported = { dsh: '0.1.0-rc.7', environment: 'env-1' }
test('persists channel selection and retryable version or combination Holds', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-channel-state-'))
const store = new ChannelStateStore(join(root, 'channel.json'), () => new Date('2026-08-19T00:00:00.000Z'))
assert.equal((await store.read()).updateChannel, 'stable')
await store.setChannel('experimental')
const version = await store.addHold({ type: 'version', dshVersion: '0.1.0-rc.8', reason: 'build failed' })
const combination = await store.addHold({
type: 'combination', dshVersion: '0.1.0-rc.9', environmentVersion: 'env-1', reason: 'health failed',
})
assert.deepEqual((await store.read()).holds.map(hold => hold.type), ['version', 'combination'])
await store.retry(version.id)
assert.deepEqual((await store.read()).holds.map(hold => hold.id), [combination.id])
})
test('plans Stable convergence before Experimental and freezes Ahead-of-Stable combinations', () => {
const local = { updateChannel: 'experimental', holds: [], experimentalBlocked: null }
assert.equal(planDesiredState({
local, current: { ...current, environment: 'env-old' }, supported,
upstream: { version: '0.1.0-rc.8' },
}).action, 'stable')
assert.equal(planDesiredState({
local, current: { ...current, dsh: '0.1.0-rc.8' }, supported,
upstream: { version: '0.1.0-rc.9' },
}).action, 'frozen')
assert.equal(planDesiredState({ local, current, supported, upstream: { version: '0.1.0-rc.8' } }).action, 'experimental')
assert.equal(planDesiredState({
local,
current: { ...current, authority: 'development', targetSequence: 0 },
supported, stableTargetSequence: 11,
upstream: { version: '0.1.0-rc.8' },
}).action, 'stable')
assert.equal(planDesiredState({
local,
current: { ...current, authority: 'stable', targetSequence: 11 },
supported, stableTargetSequence: 11,
upstream: { version: '0.1.0-rc.8' },
}).action, 'experimental')
})
test('suppresses only the held Experimental candidate and Environment combination', () => {
const local = {
updateChannel: 'experimental',
experimentalBlocked: null,
holds: [{
id: 'hold', type: 'combination', dshVersion: '0.1.0-rc.8', environmentVersion: 'env-1',
reason: 'incompatible', createdAt: '2026-08-19T00:00:00.000Z',
}],
}
const plan = planDesiredState({ local, current, supported, upstream: { version: '0.1.0-rc.8' } })
assert.equal(plan.action, 'held')
assert.equal(plan.hold.id, 'hold')
})
test('retries an Experimental Block independently of ordinary Holds', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-channel-block-'))
const store = new ChannelStateStore(join(root, 'channel.json'), () => new Date('2026-08-19T00:00:00.000Z'))
const blocked = await store.block({
dshVersion: '0.1.0-rc.8', environmentVersion: 'env-2', reason: 'combination failed',
})
assert.equal((await store.read()).experimentalBlocked.id, blocked.experimentalBlocked.id)
await store.retry(blocked.experimentalBlocked.id)
assert.equal((await store.read()).experimentalBlocked, null)
})
@@ -0,0 +1,233 @@
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import test from 'node:test'
test('container smoke targets ephemeral views and the separated persistent layout', async () => {
const script = await readFile(new URL('../../test/container-smoke.sh', import.meta.url), 'utf8')
assert.match(script, /\/run\/dsh-platform\/views\/runtime/)
assert.match(script, /\/run\/dsh-platform\/bootstrap\.sock/)
assert.match(script, /\/data\/platform\/state\/updater\/transaction\.json/)
assert.match(script, /\/data\/platform\/store\/snapshots/)
assert.match(script, /recovery\.sock/)
assert.match(script, /dsh-platform restart/)
assert.match(script, /dsh-platform stop/)
assert.match(script, /dsh web --no-open/)
assert.match(script, /requested managed DSH start/)
assert.match(script, /bundled-plugins\/action/)
assert.match(script, /"action":"uninstall"/)
assert.match(script, /managed by the platform/)
assert.match(script, /\.dshLifecycle\.taskId == \$task and \.dshLifecycle\.state == "running"/)
assert.match(script, /stage0_pid/)
assert.match(script, /\.source == "stage0" and \.message == "stage0\.ready"/)
assert.match(script, /\.source == "bootstrap" and \.message == "platform\.ready"/)
assert.match(script, /\.source == "gateway" and \.message == "gateway\.ready"/)
assert.match(script, /dsh_pid/)
assert.match(script, /kill -9 "\$dsh_pid"/)
assert.match(script, /managed-lifecycle-restart-smoke\.mjs/)
assert.match(script, /\.helperExitCode == 0 and \.sawRestarting == true and \.dshCount == 1/)
assert.match(script, /\.dshLifecycle\.state == "running" and \.recoveryMode == null/)
assert.match(script, /DSH recovered after an explicit stop/)
assert.match(script, /\.RestartCount/)
assert.match(script, /gateway\.upstream\.failed/)
assert.match(script, /gateway\.upstream\.recovered/)
assert.match(script, /standalone-recovery-smoke\.mjs/)
assert.match(script, /standalone-file-management-smoke\.mjs/)
assert.match(script, /profile-package-compatibility-smoke\.sh/)
assert.match(script, /--group-add dsh-sudo-false/)
assert.match(script, /sudo -n true/)
assert.match(script, /--unix-socket \/run\/dsh-platform\/maintenance\.sock/)
assert.match(script, /file-task\.copy\.completed/)
assert.match(script, /files\.content\.saved/)
assert.match(script, /runtime\.reset\.completed/)
assert.match(script, /runtime-reset-sentinel/)
assert.match(script, /terminal\.session\.created/)
assert.match(script, /user-plugin\.apply\.completed/)
assert.match(script, /readiness_timeout_ms=30000/)
assert.match(script, /platform readiness exceeded 30 seconds/)
assert.match(script, /DSH orphaned-bundle restart did not complete within the readiness timeout/)
assert.match(script, /DSH started before interrupted update recovery completed/)
assert.doesNotMatch(script, /echo "DSH restart did not complete"/)
assert.match(script, /clear only \/data\/platform/)
assert.match(script, /Do not delete \/data\/dsh/)
assert.doesNotMatch(script, /\/data\/platform\/(?:runtime|environments|system-plugins|bootstrap|run)\//)
})
test('Bootstrap resolves update and User Plugin recovery before concurrent startup', async () => {
const bootstrap = await readFile(new URL('../bootstrap/index.mjs', import.meta.url), 'utf8')
const updateRecovery = bootstrap.indexOf('await recoverPlatformUpdateBeforeDshStart(')
const userPluginRecovery = bootstrap.indexOf('await recoverUserPluginBeforeDshStart(')
const runtimeStart = bootstrap.indexOf('await runtime.start({')
assert.ok(updateRecovery >= 0)
assert.ok(userPluginRecovery > updateRecovery)
assert.ok(runtimeStart > userPluginRecovery)
})
test('Profile compatibility smoke migrates old stores and preserves node ownership', async () => {
const script = await readFile(new URL('../../test/profile-package-compatibility-smoke.sh', import.meta.url), 'utf8')
assert.match(script, /dsh-platform stop --wait/)
assert.match(script, /pnpm install --offline --frozen-lockfile/)
assert.match(script, /dsh-platform start --wait/)
assert.match(script, /npm_config_userconfig=\/root\/\.config\/npm\/npmrc/)
assert.match(script, /dsh plugin --profile web add/)
assert.match(script, /stat -c %U/)
assert.match(script, /dsh plugin --profile web remove/)
})
test('the public DSH shim keeps Profile operations under the node identity', async () => {
const script = await readFile(new URL('../tools/dsh-shim.sh', import.meta.url), 'utf8')
assert.match(script, /if \[ "\$\(id -u\)" -eq 0 \]/)
assert.match(script, /--reuid=1000/)
assert.match(script, /--regid=1000/)
assert.match(script, /--keep-groups/)
assert.match(script, /-u npm_config_userconfig/)
assert.match(script, /-u NPM_CONFIG_USERCONFIG/)
assert.match(script, /-u PNPM_HOME/)
assert.match(script, /HOME=\/home\/node/)
assert.match(script, /XDG_CONFIG_HOME=\/home\/node\/\.config/)
assert.match(script, /XDG_STATE_HOME=\/home\/node\/\.local\/state/)
assert.match(script, /DSH_HOME="\$\{DSH_HOME:-\/data\/dsh\}"/)
assert.doesNotMatch(script, /HOME=\/root|XDG_CONFIG_HOME=\/root/)
})
test('managed lifecycle smoke rejects replacement instances and converts SIGTERM into restart', async () => {
const script = await readFile(new URL('../../test/managed-lifecycle-restart-smoke.mjs', import.meta.url), 'utf8')
assert.match(script, /spawn\(DSH_EXECUTABLE, \['web', '--no-open'\]/)
assert.match(script, /detached: true/)
assert.match(script, /DSH_PLATFORM_LAUNCH_TOKEN: ''/)
assert.match(script, /process\.kill\(oldPid, 'SIGTERM'\)/)
assert.match(script, /unregistered SIGTERM did not create a managed restart task/)
assert.match(script, /newPids\.length === 1/)
assert.doesNotMatch(script, /dsh-market|better-sidebar|net-proxy/)
})
test('standalone file smoke covers recovery-mode transfer, edit, search, symlink, and mutations', async () => {
const script = await readFile(new URL('../../test/standalone-file-management-smoke.mjs', import.meta.url), 'utf8')
assert.match(script, /files\/upload/)
assert.match(script, /range: 'bytes=3-6'/)
assert.match(script, /edited in recovery mode/)
assert.match(script, /operation: 'copy'/)
assert.match(script, /operation: 'move'/)
assert.match(script, /operation: 'delete'/)
assert.match(script, /operation: 'search'/)
assert.match(script, /operation: 'size'/)
assert.match(script, /operation: 'attributes'/)
assert.match(script, /recursive: true/)
assert.match(script, /type, 'symlink'/)
assert.match(script, /\/data\/dsh\/file-management-smoke/)
assert.match(script, /\/root\/dsh-maintenance-smoke/)
assert.match(script, /config\.privileged, true/)
assert.match(script, /\['queued', 'running'\]\.includes\(state\.status\)/)
})
test('standalone recovery smoke covers live terminal and consecutive faulty plugins', async () => {
const script = await readFile(new URL('../../test/standalone-recovery-smoke.mjs', import.meta.url), 'utf8')
assert.match(script, /terminal-before-restart/)
assert.match(script, /terminal-after-restart-中文/)
assert.match(script, /operation\?\.status \?\? operation\?\.state/)
assert.match(script, /uid=0/)
assert.match(script, /cwd=\\\/root/)
assert.match(script, /smoke-fault-one/)
assert.match(script, /smoke-fault-two/)
assert.match(script, /firstOutcome/)
assert.match(script, /userPluginOperation/)
assert.match(script, /recoveryMode === null/)
assert.match(script, /assert\.doesNotMatch\(JSON\.stringify\(logEntries\.filter/)
})
test('Stage-0 owns privileged User Skill mutations behind a constrained local API', async () => {
const [stage0, management, paths] = await Promise.all([
readFile(new URL('../stage0/index.mjs', import.meta.url), 'utf8'),
readFile(new URL('../../control-plane/services/management/index.mjs', import.meta.url), 'utf8'),
readFile(new URL('../lib/paths.mjs', import.meta.url), 'utf8'),
])
assert.match(stage0, /prepareUserSkillRoots, UserSkillManager/)
assert.match(stage0, /new UserSkillManager\(\{ dshHome, agentsHome \}\)/)
assert.match(stage0, /listenUserSkills\(userSkillServer, paths\.userSkillSocket\)/)
assert.match(management, /new LocalApiClient\(paths\.userSkillSocket\)/)
assert.match(management, /configureUserSkill: value => userSkillApi\.request\('POST', '\/v1\/action', value\)/)
assert.match(paths, /this\.userSkillSocket = join\(this\.runRoot, 'user-skills\.sock'\)/)
})
test('isolated service identities can traverse the runtime root without inheriting platform socket access', async () => {
const paths = await readFile(new URL('../lib/paths.mjs', import.meta.url), 'utf8')
const accessLaunch = await readFile(new URL('../stage0/lib/access-launch-server.mjs', import.meta.url), 'utf8')
assert.match(paths, /chmod\(paths\.runRoot, 0o1771\)/)
assert.match(accessLaunch, /'--clear-groups'/)
assert.doesNotMatch(accessLaunch, /--groups=/)
})
test('Docker health reports Gateway control-plane liveness independently of DSH', async () => {
const dockerfile = await readFile(new URL('../../../Dockerfile', import.meta.url), 'utf8')
const healthcheck = dockerfile.split('HEALTHCHECK')[1]?.split('\n\n')[0] ?? ''
assert.match(healthcheck, /--start-period=60s/)
assert.match(healthcheck, /127\.0\.0\.1:3080\/_dsh_gateway\/health/)
assert.doesNotMatch(healthcheck, /127\.0\.0\.1:3079\/|stage0-trust\.sock/)
})
test('Management readiness does not depend on an authenticated management API', async () => {
const component = JSON.parse(await readFile(new URL('../../control-plane/services/management/component.json', import.meta.url)))
assert.equal(component.health.type, 'exec')
assert.equal(component.health.command.executable, '/usr/local/bin/node')
assert.deepEqual(component.health.command.args, [
'/run/dsh-platform/views/bootstrap/control-plane/services/management/healthcheck.mjs',
])
const probe = await readFile(new URL('../../control-plane/services/management/healthcheck.mjs', import.meta.url), 'utf8')
assert.match(probe, /\/_dsh_platform\/internal\/health/)
assert.doesNotMatch(probe, /\/_dsh_platform\/api\/v1/)
})
test('public platform CLI and Gateway status probes use a separate restricted Management socket', async () => {
const [paths, cli, management, gateway] = await Promise.all([
readFile(new URL('../lib/paths.mjs', import.meta.url), 'utf8'),
readFile(new URL('../../control-plane/services/management/cli.mjs', import.meta.url), 'utf8'),
readFile(new URL('../../control-plane/services/management/index.mjs', import.meta.url), 'utf8'),
readFile(new URL('../../control-plane/services/gateway/lib/lifecycle.mjs', import.meta.url), 'utf8'),
])
assert.match(paths, /this\.managementCliSocket = join\(this\.runRoot, 'management-cli\.sock'\)/)
assert.match(cli, /DSH_PLATFORM_MANAGEMENT_CLI_SOCKET.*management-cli\.sock/)
assert.match(management, /restrictedCliRoute/)
assert.match(gateway, /DSH_PLATFORM_MANAGEMENT_CLI_SOCKET.*management-cli\.sock/)
})
test('Docker installs locked Management dependencies inside the Seed stage only', async () => {
const dockerfile = await readFile(new URL('../../../Dockerfile', import.meta.url), 'utf8')
const ignore = await readFile(new URL('../../../.dockerignore', import.meta.url), 'utf8')
const seed = await readFile(new URL('../stage0/lib/seed.mjs', import.meta.url), 'utf8')
const buildSeed = await readFile(new URL('../tools/build-seed.mjs', import.meta.url), 'utf8')
assert.match(dockerfile, /npm ci --omit=dev --ignore-scripts --no-audit --no-fund[\s\S]*--prefix \/opt\/dsh-platform-source\/control-plane\/services\/management/)
assert.match(dockerfile, /npm ci --omit=dev --ignore-scripts --no-audit --no-fund --legacy-peer-deps[\s\S]*--prefix \/opt\/dsh-platform-source\/environment\/resources\/plugins\/platform-management\/package/)
assert.match(dockerfile, /util-linux/)
assert.match(dockerfile, /\n\s+g\+\+ \\\n/)
assert.match(dockerfile, /\n\s+make \\\n/)
assert.equal(ignore.split('\n').includes('**/node_modules'), true)
assert.match(seed, /paths\.managementStateRoot/)
assert.match(seed, /paths\.managementStateRoot, paths\.fileTasksRoot/)
assert.match(seed, /paths\.userPluginSnapshotsRoot/)
assert.match(buildSeed, /file-manager/)
const managementManifest = dockerfile.indexOf('COPY container/control-plane/services/management/package.json')
const pluginManifest = dockerfile.indexOf('COPY container/environment/resources/plugins/platform-management/package/package.json')
const dependencyInstall = dockerfile.indexOf('RUN npm ci --omit=dev --ignore-scripts --no-audit --no-fund')
const sourceCopy = dockerfile.indexOf('COPY container /opt/dsh-platform-source')
assert.ok(managementManifest >= 0 && pluginManifest > managementManifest)
assert.ok(dependencyInstall > pluginManifest && sourceCopy > dependencyInstall)
})
test('local Docker builds resolve the default DSH from the supported target without a sticky latest cache', async () => {
const dockerfile = await readFile(new URL('../../../Dockerfile', import.meta.url), 'utf8')
assert.match(dockerfile, /COPY release\/supported-target\.json \/opt\/dsh-supported-target\.json/)
assert.match(dockerfile, /ARG SIGNED_IMAGE_INPUT=false/)
assert.match(dockerfile, /case "\$SIGNED_IMAGE_INPUT" in[\s\S]*true\)[\s\S]*npm install --global \/opt\/dsh-platform-image-input\/dsh\.tgz[\s\S]*false\)[\s\S]*latestSupportedDsh/)
assert.match(dockerfile, /if \[ "\$requested_version" = latest \]; then[\s\S]*latestSupportedDsh/)
assert.match(dockerfile, /npm install --global "@deepseek-ai\/dsh@\$\{requested_version\}"/)
assert.match(dockerfile, /false\) image_input=-/)
assert.doesNotMatch(dockerfile, /if \[ -f \/opt\/dsh-platform-(?:image-input|source\/platform\/image-input)/)
})
test('formal images identify DSH, Environment, target sequence, and the signed update channel', async () => {
const dockerfile = await readFile(new URL('../../../Dockerfile', import.meta.url), 'utf8')
assert.match(dockerfile, /org\.opencontainers\.image\.version="\$DSH_VERSION"/)
assert.match(dockerfile, /io\.dsh-docker\.environment\.version="\$ENVIRONMENT_VERSION"/)
assert.match(dockerfile, /io\.dsh-docker\.target-sequence="\$TARGET_SEQUENCE"/)
assert.match(dockerfile, /DSH_UPDATE_METADATA_URL=https:\/\/raw\.githubusercontent\.com\/yjrszcq\/dsh-docker\/release-channel\//)
assert.doesNotMatch(dockerfile, /releases\/latest\/download/)
})
@@ -0,0 +1,197 @@
import assert from 'node:assert/strict'
import { generateKeyPairSync } from 'node:crypto'
import { mkdtemp, readFile, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { spawnSync } from 'node:child_process'
import test from 'node:test'
import { canonicalJson } from '../lib/canonical-json.mjs'
import { parseBootstrapManifest, parseComponentManifest, parseEnvironmentManifest, parseOfficialDshPolicy, parseStable } from '../lib/contracts.mjs'
import {
deriveImageBuildId,
deriveRecordId,
parseArtifactReference,
parseBootstrapRecord,
parseDeploymentRecord,
parseImageInventory,
parseSlots,
recordsFromImageInventory,
} from '../lib/deployment-contracts.mjs'
import { verifyDetached } from '../stage0/lib/signature.mjs'
import { document, officialDshPolicy, registryKeyPair, target } from './helpers.mjs'
const common = manifestType => ({
schema: 1,
manifestType,
version: manifestType === 'bootstrap' ? '1.0.0' : '1.0.0',
keyringGeneration: 1,
targetSequence: 1,
issuedAt: '2026-08-19T00:00:00.000Z',
artifacts: [],
})
const lifecycle = {
prepare: null,
preStart: null,
postStart: null,
preStop: null,
stop: null,
postStop: null,
}
test('canonical JSON recursively orders keys and rejects unsafe numbers', () => {
assert.equal(canonicalJson({ z: 1, a: { y: 2, x: 3 } }).toString(), '{"a":{"x":3,"y":2},"z":1}\n')
assert.throws(() => canonicalJson({ value: 1.5 }))
})
function imageInventory(overrides = {}) {
const content = {
schema: 1,
authority: 'stable',
platformRevision: 'fixture-revision',
targetSequence: 10,
bootstrapApi: 1,
updateApi: 1,
bootstrap: { version: '1.0.0', id: 'bootstrap-fixture', sha256: '1'.repeat(64) },
deployment: {
id: 'deployment-fixture',
dshVersion: '0.1.0-rc.10',
environmentVersion: '1.0.0',
environment: { id: 'environment-fixture', sha256: '2'.repeat(64) },
pristine: { id: 'pristine-fixture', sha256: '3'.repeat(64) },
runtime: { id: 'runtime-fixture', sha256: '4'.repeat(64) },
systemPlugins: { id: 'system-plugins-fixture', sha256: '5'.repeat(64) },
},
...overrides,
}
return { ...content, imageBuildId: deriveImageBuildId(content) }
}
test('parses content-bound image inventory and derives immutable image Records', () => {
const inventory = parseImageInventory(document(imageInventory()))
const records = recordsFromImageInventory(inventory)
assert.match(inventory.imageBuildId, /^sha256:[a-f0-9]{64}$/)
assert.equal(records.bootstrap.version, '1.0.0')
assert.equal(records.deployment.runtime.imageBuildId, inventory.imageBuildId)
assert.equal(records.deployment.targetSequence, 10)
assert.doesNotThrow(() => parseBootstrapRecord(records.bootstrap))
assert.doesNotThrow(() => parseDeploymentRecord(records.deployment))
const changed = imageInventory({ platformRevision: 'different-revision' })
assert.notEqual(changed.imageBuildId, inventory.imageBuildId)
assert.throws(() => parseImageInventory(document({ ...imageInventory(), targetSequence: 11 })), /imageBuildId/)
})
test('restricts inventory authority and Artifact References without accepting paths', () => {
const development = imageInventory({ authority: 'development', targetSequence: 0 })
development.imageBuildId = deriveImageBuildId(Object.fromEntries(Object.entries(development).filter(([key]) => key !== 'imageBuildId')))
assert.equal(parseImageInventory(document(development)).targetSequence, 0)
assert.throws(() => parseImageInventory(document(imageInventory({ authority: 'development' }))), /targetSequence 0/)
assert.throws(() => parseArtifactReference({
storage: 'store', kind: 'runtime', id: '../escape', sha256: 'a'.repeat(64),
}), /invalid/)
assert.throws(() => parseArtifactReference({
storage: 'store', kind: 'runtime', id: 'runtime-one', sha256: 'a'.repeat(64), path: '/tmp/runtime',
}), /fields/)
assert.throws(() => parseArtifactReference({
storage: 'image', imageBuildId: `sha256:${'b'.repeat(64)}`, kind: 'unknown', id: 'one', sha256: 'a'.repeat(64),
}), /kind/)
})
test('rejects mutated Records and invalid atomic slot generations', () => {
const { bootstrap, deployment } = recordsFromImageInventory(parseImageInventory(document(imageInventory())))
assert.throws(() => parseDeploymentRecord({ ...deployment, dshVersion: 'changed' }), /canonical content/)
assert.throws(() => parseBootstrapRecord({ ...bootstrap, artifact: { ...bootstrap.artifact, id: 'changed' } }), /canonical content/)
assert.deepEqual(parseSlots({
schema: 1, generation: 2, current: deployment.id, previous: null,
}, 'deployment-record'), {
schema: 1, generation: 2, current: deployment.id, previous: null,
})
assert.throws(() => parseSlots({
schema: 1, generation: 0, current: deployment.id, previous: null,
}, 'deployment-record'), /positive/)
assert.throws(() => parseSlots({
schema: 1, generation: 1, current: deployment.id, previous: deployment.id,
}, 'deployment-record'), /must differ/)
const content = { schema: 1, authority: 'development' }
assert.equal(deriveRecordId('fixture', content), deriveRecordId('fixture', { authority: 'development', schema: 1 }))
})
test('parses the exact stable desired state and rejects missing referenced Artifacts', () => {
const parsed = parseStable(document(target(1, 1)))
assert.equal(parsed.desired.dsh.version, '0.1.0-rc.7')
const invalid = target(1, 1)
invalid.desired.bootstrap.manifestArtifactId = 'missing'
assert.throws(() => parseStable(document(invalid)), /missing/)
assert.throws(() => parseStable(document({ ...target(1, 1), unknown: true })), /fields/)
})
test('parses the Release-delegated official DSH policy with Stable schema 1', () => {
const policy = officialDshPolicy(registryKeyPair())
assert.equal(parseOfficialDshPolicy(policy).packageName, '@deepseek-ai/dsh')
assert.equal(parseStable(document(target(1, 1, policy))).officialDshPolicy.keys[0].keyId, policy.keys[0].keyId)
const wrongRegistry = { ...policy, registry: 'https://registry.example/' }
assert.throws(() => parseOfficialDshPolicy(wrongRegistry), /official npm registry/)
})
test('parses bootstrap and environment manifests with ordered references', () => {
const bootstrap = { ...common('bootstrap'), bootstrapApi: 1, entrypoint: '/opt/bootstrap/index.mjs' }
assert.equal(parseBootstrapManifest(document(bootstrap)).entrypoint, bootstrap.entrypoint)
const hashes = {
component: '1'.repeat(64),
patch: '2'.repeat(64),
plugin: '3'.repeat(64),
}
const environment = {
...common('environment'),
artifacts: Object.entries(hashes).map(([id, sha256]) => ({
id,
mediaType: 'application/octet-stream',
sha256,
size: 1,
url: `https://release.example/${id}`,
})),
bootstrapApi: 1,
components: [{ id: 'gateway', sha256: hashes.component }],
patches: [{ id: 'directory-picker', sha256: hashes.patch }],
systemPlugins: [{ id: 'update-ui', sha256: hashes.plugin }],
}
assert.equal(parseEnvironmentManifest(document(environment)).components[0].id, 'gateway')
assert.throws(() => parseEnvironmentManifest(document({ ...environment, components: [...environment.components, environment.components[0]] })), /unique/)
})
test('parses service, oneshot, and hook component lifecycle declarations', () => {
const value = {
schema: 1,
id: 'gateway',
type: 'service',
command: { executable: '/usr/local/bin/node', args: ['/opt/gateway/index.mjs'], timeoutSeconds: 30 },
environment: { PLATFORM_SOCKET: '/data/platform/run/platform.sock' },
lifecycle,
health: { type: 'http', host: '127.0.0.1', port: 3080, path: '/health', intervalSeconds: 1, timeoutSeconds: 5 },
logging: { stdout: true, stderr: true },
}
assert.equal(parseComponentManifest(document(value)).type, 'service')
assert.throws(() => parseComponentManifest(document({ ...value, version: '1.0.0' })), /fields/)
assert.throws(() => parseComponentManifest(document({ ...value, type: 'unknown' })), /type/)
assert.throws(() => parseComponentManifest(document({ ...value, command: { ...value.command, executable: 'node' } })), /absolute/)
})
test('offline signing tool emits a signature accepted by Stage-0', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-sign-tool-'))
const pair = generateKeyPairSync('ed25519')
const privatePath = join(root, 'private.pem')
const documentPath = join(root, 'document.json')
const signaturePath = join(root, 'document.sig.json')
await writeFile(privatePath, pair.privateKey.export({ format: 'pem', type: 'pkcs8' }), { mode: 0o600 })
const bytes = canonicalJson({ stable: true })
await writeFile(documentPath, bytes)
const result = spawnSync(process.execPath, [
new URL('../tools/sign.mjs', import.meta.url).pathname,
'sign', privatePath, documentPath, signaturePath,
], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
const publicKey = pair.publicKey.export({ format: 'der', type: 'spki' }).toString('base64')
const signature = JSON.parse(await readFile(signaturePath, 'utf8'))
assert.doesNotThrow(() => verifyDetached(bytes, signature, publicKey))
})
@@ -0,0 +1,953 @@
import assert from 'node:assert/strict'
import { createHash } from 'node:crypto'
import { chmod, lstat, mkdtemp, mkdir, readFile, readdir, readlink, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { DeploymentManager, DeploymentResolutionError } from '../bootstrap/lib/deployments.mjs'
import { PlatformGarbageCollector } from '../bootstrap/lib/gc.mjs'
import { canonicalJson } from '../lib/canonical-json.mjs'
import {
deriveImageBuildId,
deriveRecordId,
parseDeploymentRecord,
parseImageInventory,
recordsFromImageInventory,
} from '../lib/deployment-contracts.mjs'
import { PlatformPaths, preparePersistentLayout, resetRuntimeLayout } from '../lib/paths.mjs'
import { hashTree } from '../lib/tree-hash.mjs'
import { buildRuntime } from '../../control-plane/modules/patch-manager/index.mjs'
const kinds = ['environment', 'pristine', 'runtime', 'system-plugins']
async function fixture({
root,
paths,
authority = 'stable',
targetSequence = 1,
marker = 'image',
} = {}) {
root ??= await mkdtemp(join(tmpdir(), 'dsh-deployments-'))
paths ??= new PlatformPaths(join(root, 'data'), join(root, 'run'))
const seedRoot = join(root, `seed-${marker}`)
const assets = {}
for (const kind of kinds) {
const plural = kind === 'environment' ? 'environments' : kind === 'runtime' ? 'runtimes' : kind
const id = `image-${kind}-${marker}`
const path = join(seedRoot, plural, id)
await mkdir(path, { recursive: true })
await writeFile(join(path, 'sentinel'), `${kind}:${marker}`)
assets[kind] = { id, sha256: await hashTree(path) }
}
const content = {
schema: 1,
authority,
platformRevision: `deployment-fixture-${marker}`,
targetSequence,
bootstrapApi: 1,
updateApi: 1,
bootstrap: { version: '1.0.0', id: 'bootstrap', sha256: 'a'.repeat(64) },
deployment: {
id: `image-deployment-${marker}`,
dshVersion: '0.1.0-rc.1',
environmentVersion: '1.0.0',
environment: assets.environment,
pristine: assets.pristine,
runtime: assets.runtime,
systemPlugins: assets['system-plugins'],
},
}
const inventory = parseImageInventory({ ...content, imageBuildId: deriveImageBuildId(content) })
await preparePersistentLayout(paths)
await resetRuntimeLayout(paths)
const manager = new DeploymentManager({ paths, seedRoot, inventory })
return { root, seedRoot, paths, inventory, manager, image: recordsFromImageInventory(inventory).deployment }
}
async function repairableFixture() {
const root = await mkdtemp(join(tmpdir(), 'dsh-runtime-reset-'))
const paths = new PlatformPaths(join(root, 'data'), join(root, 'run'))
const seedRoot = join(root, 'seed')
const pristineRoot = join(seedRoot, 'pristine', 'dsh')
const environmentRoot = join(seedRoot, 'environments', 'environment')
const runtimeRoot = join(seedRoot, 'runtimes', 'dsh')
const systemPluginsRoot = join(seedRoot, 'system-plugins', 'environment')
await mkdir(join(pristineRoot, 'lib'), { recursive: true })
await mkdir(join(environmentRoot, 'artifacts'), { recursive: true })
await mkdir(systemPluginsRoot, { recursive: true })
await writeFile(join(pristineRoot, 'lib', 'bin.js'), '#!/usr/bin/env node\n')
const patch = [
"import { readFile, writeFile } from 'node:fs/promises'",
"import { join } from 'node:path'",
"export async function applyPatch(root) { await writeFile(join(root, 'patched.txt'), 'patched\\n') }",
"export async function verifyPatch(root) { if (await readFile(join(root, 'patched.txt'), 'utf8') !== 'patched\\n') throw new Error('patch missing') }",
'',
].join('\n')
const patchBytes = Buffer.from(patch)
const patchSha256 = createHash('sha256').update(patchBytes).digest('hex')
await writeFile(join(environmentRoot, 'artifacts', 'runtime-patch'), patchBytes)
await writeFile(join(environmentRoot, 'environment.manifest.json'), JSON.stringify({
schema: 1,
manifestType: 'environment',
version: '1.0.0',
keyringGeneration: 1,
targetSequence: 1,
issuedAt: '2026-08-20T00:00:00.000Z',
artifacts: [{
id: 'runtime-patch',
mediaType: 'text/javascript',
sha256: patchSha256,
size: patchBytes.byteLength,
url: 'https://example.invalid/runtime-patch',
}],
bootstrapApi: 1,
components: [],
patches: [{ id: 'runtime-patch', sha256: patchSha256 }],
systemPlugins: [],
}))
await buildRuntime({
pristineRoot,
versionsRoot: join(seedRoot, 'runtimes'),
runtimeId: 'dsh',
patchPaths: [join(environmentRoot, 'artifacts', 'runtime-patch')],
})
const assets = {
environment: { id: 'environment', sha256: await hashTree(environmentRoot) },
pristine: { id: 'dsh', sha256: await hashTree(pristineRoot) },
runtime: { id: 'dsh', sha256: await hashTree(runtimeRoot) },
systemPlugins: { id: 'environment', sha256: await hashTree(systemPluginsRoot) },
}
const content = {
schema: 1,
authority: 'development',
platformRevision: 'runtime-reset-fixture',
targetSequence: 0,
bootstrapApi: 1,
updateApi: 1,
bootstrap: { version: '1.0.0', id: 'bootstrap', sha256: 'a'.repeat(64) },
deployment: {
id: 'image-deployment-runtime-reset',
dshVersion: '0.1.0-rc.1',
environmentVersion: '1.0.0',
...assets,
},
}
const inventory = parseImageInventory({ ...content, imageBuildId: deriveImageBuildId(content) })
await preparePersistentLayout(paths)
await resetRuntimeLayout(paths)
const manager = new DeploymentManager({ paths, seedRoot, inventory })
const image = recordsFromImageInventory(inventory).deployment
await manager.initialize(image)
return { root, paths, seedRoot, manager, image, environmentRoot }
}
async function managedRecord(
context,
suffix,
sequence = 2,
authority = 'stable',
dshVersion = `0.1.0-rc.${String(sequence)}`,
receiptTokens = [],
) {
const references = {}
for (const kind of kinds) {
const root = kind === 'environment'
? context.paths.environmentsRoot
: kind === 'pristine'
? context.paths.pristineRoot
: kind === 'runtime'
? context.paths.runtimesRoot
: context.paths.systemPluginsRoot
const id = `managed-${kind}-${suffix}`
const path = join(root, id)
await mkdir(path, { recursive: true })
await writeFile(join(path, 'sentinel'), `${kind}:${suffix}`)
references[kind] = { storage: 'store', kind, id, sha256: await hashTree(path) }
}
const content = {
schema: 1,
authority,
targetSequence: sequence,
dshVersion,
environmentVersion: '1.0.0',
environment: references.environment,
pristine: references.pristine,
runtime: references.runtime,
systemPlugins: references['system-plugins'],
receiptTokens,
snapshotId: null,
}
return parseDeploymentRecord({ ...content, id: deriveRecordId('deployment-record', content) })
}
async function experimentalRecord(context, from, suffix, dshVersion, receiptTokens = []) {
const built = await managedRecord(context, suffix, from.targetSequence, 'experimental', dshVersion, receiptTokens)
const content = {
...built,
environmentVersion: from.environmentVersion,
environment: from.environment,
systemPlugins: from.systemPlugins,
}
delete content.id
return parseDeploymentRecord({ ...content, id: deriveRecordId('deployment-record', content) })
}
test('initializes an Image Deployment through one atomic runtime view', async () => {
const context = await fixture()
const state = await context.manager.initialize(context.image)
assert.equal(state.current, context.image.id)
assert.equal(state.previous, null)
assert.equal(await readFile(join(context.paths.viewsRoot, 'runtime', 'sentinel'), 'utf8'), 'runtime:image')
assert.match(await readlink(context.paths.deploymentView), new RegExp(`${context.image.id}$`))
assert.equal(
await readlink(join(context.paths.viewsRoot, 'system-plugins')),
join('..', 'system-plugin-views', 'current'),
)
assert.equal(
await readlink(join(context.paths.systemPluginViewsRoot, 'current')),
join(context.paths.deploymentView, 'system-plugins'),
)
assert.equal((await context.manager.selected()).record.id, context.image.id)
const status = await context.manager.publishStatus()
assert.equal(status.platformLayout, 1)
assert.equal(status.imageBaseline.imageBuildId, context.inventory.imageBuildId)
assert.equal(status.current.recordId, context.image.id)
assert.equal(status.current.source, 'image')
assert.equal(status.recoveryMode, null)
})
test('rebuilds image and managed Runtime bytes without changing versions or previous Deployment', async () => {
const context = await repairableFixture()
let pauses = 0
let restarts = 0
const reset = () => context.manager.resetCurrentRuntime({
pauseDsh: async () => { pauses += 1 },
restartDsh: async () => {
restarts += 1
assert.equal(await readFile(join(context.paths.viewsRoot, 'runtime', 'package', 'patched.txt'), 'utf8'), 'patched\n')
},
})
const first = await reset()
assert.notEqual(first.recordId, context.image.id)
assert.equal(first.slots.previous, null)
const repaired = await context.manager.record(first.recordId)
assert.equal(repaired.runtime.storage, 'store')
assert.equal(repaired.dshVersion, context.image.dshVersion)
assert.equal(repaired.environmentVersion, context.image.environmentVersion)
assert.deepEqual(await readdir(context.paths.runtimesRoot), [repaired.runtime.id])
const materialized = await context.manager.materializeCurrent()
assert.equal(materialized.runtime.storage, 'store')
assert.notEqual(materialized.id, repaired.id)
assert.equal(materialized.runtime.id, repaired.runtime.id)
assert.equal((await context.manager.state()).previous, null)
const managedRuntime = join(context.paths.runtimesRoot, materialized.runtime.id)
await writeFile(join(managedRuntime, 'package', 'patched.txt'), 'corrupt-again\n')
const second = await reset()
assert.equal(second.recordId, materialized.id)
assert.equal(second.slots.previous, null)
assert.equal(await hashTree(managedRuntime), materialized.runtime.sha256)
assert.equal(pauses, 2)
assert.equal(restarts, 2)
await writeFile(join(managedRuntime, 'package', 'patched.txt'), 'runtime-before-failed-reset\n')
let failedResetRestarts = 0
await assert.rejects(context.manager.resetCurrentRuntime({
pauseDsh: async () => { pauses += 1 },
restartDsh: async () => {
failedResetRestarts += 1
const content = await readFile(join(context.paths.viewsRoot, 'runtime', 'package', 'patched.txt'), 'utf8')
if (failedResetRestarts === 1) {
assert.equal(content, 'patched\n')
throw new Error('repaired Runtime failed to start')
}
assert.equal(content, 'runtime-before-failed-reset\n')
},
}), /repaired Runtime failed to start/)
assert.equal(failedResetRestarts, 2)
assert.equal(await readFile(join(managedRuntime, 'package', 'patched.txt'), 'utf8'), 'runtime-before-failed-reset\n')
assert.deepEqual(await context.manager.state(), second.slots)
await writeFile(join(context.paths.environmentsRoot, materialized.environment.id, 'artifacts', 'runtime-patch'), 'corrupt artifact\n')
await assert.rejects(reset(), /differs from (?:its Record|the Environment Manifest)/)
assert.equal(pauses, 3)
assert.equal(restarts, 2)
})
test('updates a runtime operation without replacing deployment identity fields', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const before = await context.manager.publishStatus({ recoveryMode: 'diagnostic-state' })
const restarting = await context.manager.setOperation('restarting')
assert.deepEqual(restarting, { ...before, operation: 'restarting' })
const ready = await context.manager.setOperation(null)
assert.deepEqual(ready, before)
})
test('commits a complete managed Deployment only after candidate health succeeds', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const candidate = await context.manager.writeRecord(await managedRecord(context, 'next'))
let observed
const state = await context.manager.activate(candidate.id, async () => {
observed = await Promise.all(kinds.map(kind => readFile(join(context.paths.deploymentView, kind, 'sentinel'), 'utf8')))
})
assert.deepEqual(observed, kinds.map(kind => `${kind}:next`))
assert.equal(state.current, candidate.id)
assert.equal(state.previous, context.image.id)
})
test('restores the prior complete view and leaves slots unchanged when health fails', async () => {
const context = await fixture()
const before = await context.manager.initialize(context.image)
const candidate = await context.manager.writeRecord(await managedRecord(context, 'broken'))
await assert.rejects(context.manager.activate(candidate.id, async () => {
const value = await readFile(join(context.paths.deploymentView, 'runtime', 'sentinel'), 'utf8')
if (value === 'runtime:broken') throw new Error('candidate unhealthy')
}), /candidate unhealthy/)
assert.deepEqual(await context.manager.state(), before)
assert.equal(await readFile(join(context.paths.viewsRoot, 'runtime', 'sentinel'), 'utf8'), 'runtime:image')
})
test('rejects modified managed content and stale Image References', async () => {
const context = await fixture()
const candidate = await context.manager.writeRecord(await managedRecord(context, 'tampered'))
await writeFile(join(context.paths.runtimesRoot, candidate.runtime.id, 'sentinel'), 'changed')
await assert.rejects(context.manager.prepareView(candidate.id), /content hash differs/)
await context.manager.writeRecord(context.image)
const changedContent = { ...context.inventory.document, platformRevision: 'new-image' }
delete changedContent.imageBuildId
const changedInventory = parseImageInventory({ ...changedContent, imageBuildId: deriveImageBuildId(changedContent) })
const changedManager = new DeploymentManager({ paths: context.paths, seedRoot: context.seedRoot, inventory: changedInventory })
await assert.rejects(changedManager.prepareView(context.image.id), /different image/)
})
test('writes immutable Deployment Record bytes', async () => {
const context = await fixture()
await context.manager.writeRecord(context.image)
assert.deepEqual(
await readFile(context.manager.recordPath(context.image.id)),
canonicalJson(context.image),
)
})
test('materializes an Image Deployment as a complete Managed previous state', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const materialized = await context.manager.materializeCurrent()
assert.notEqual(materialized.id, context.image.id)
assert.equal(materialized.dshVersion, context.image.dshVersion)
for (const field of ['environment', 'pristine', 'runtime', 'systemPlugins']) {
assert.equal(materialized[field].storage, 'store')
const resolved = await context.manager.resolveReference(materialized[field])
assert.equal((await lstat(resolved)).isDirectory(), true)
}
assert.equal((await context.manager.state()).current, materialized.id)
})
test('advances to a newer image only after startup acceptance', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const nextContent = {
...context.inventory.document,
platformRevision: 'deployment-fixture-next',
targetSequence: 2,
}
delete nextContent.imageBuildId
const nextInventory = parseImageInventory({ ...nextContent, imageBuildId: deriveImageBuildId(nextContent) })
const nextRecord = recordsFromImageInventory(nextInventory).deployment
const restarted = new DeploymentManager({ paths: context.paths, seedRoot: context.seedRoot, inventory: nextInventory })
const plan = await restarted.prepareImage(nextRecord)
assert.equal(plan.action, 'image-forward')
assert.equal((await restarted.state()).current, context.image.id)
const state = await restarted.acceptImage(plan)
assert.equal(state.current, nextRecord.id)
assert.equal(state.previous, context.image.id)
})
test('does not advertise a rollback to assets owned by another Docker image', async () => {
const first = await fixture({ targetSequence: 1, marker: 'rollback-first' })
await first.manager.initialize(first.image)
const second = await fixture({
root: first.root,
paths: first.paths,
targetSequence: 2,
marker: 'rollback-second',
})
const plan = await second.manager.prepareImage(second.image)
await second.manager.acceptImage(plan)
const rollback = await second.manager.rollbackPlan()
assert.equal(rollback.current.id, second.image.id)
assert.equal(rollback.previous, null)
})
test('selects the prior Deployment directly after an explicit Docker image rollback', async () => {
const first = await fixture({ targetSequence: 1, marker: 'docker-rollback-first' })
await first.manager.initialize(first.image)
const second = await fixture({
root: first.root,
paths: first.paths,
targetSequence: 2,
marker: 'docker-rollback-second',
})
await second.manager.acceptImage(await second.manager.prepareImage(second.image))
const rolledBack = new DeploymentManager({
paths: first.paths,
seedRoot: first.seedRoot,
inventory: first.inventory,
})
const rollback = await rolledBack.prepareImage(first.image)
assert.equal(rollback.action, 'image-rollback')
assert.equal(rollback.target, first.image.id)
await rolledBack.acceptImage(rollback)
assert.equal((await rolledBack.state()).current, first.image.id)
})
test('rejects an unresolvable newer image candidate and keeps the valid current Deployment', async () => {
const current = await fixture({ targetSequence: 1, marker: 'current' })
await current.manager.initialize(current.image)
const managed = await current.manager.materializeCurrent()
const candidateContent = {
...current.inventory.document,
platformRevision: 'deployment-fixture-candidate-missing',
targetSequence: 2,
}
delete candidateContent.imageBuildId
const inventory = parseImageInventory({
...candidateContent,
imageBuildId: deriveImageBuildId(candidateContent),
})
const candidate = recordsFromImageInventory(inventory).deployment
await rm(join(current.seedRoot, 'runtimes', inventory.deployment.runtime.id), { recursive: true })
const manager = new DeploymentManager({ paths: current.paths, seedRoot: current.seedRoot, inventory })
const plan = await manager.prepareImage(candidate)
assert.equal(plan.action, 'image-rejected')
assert.equal(plan.target, managed.id)
assert.match(plan.candidateError, /ENOENT/)
assert.equal((await manager.selected()).record.id, managed.id)
assert.equal((await manager.state()).current, managed.id)
})
test('commits previous only after an invalid current resolves and previous receipts activate', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const previous = await managedRecord(context, 'previous-valid', 2, 'stable', '0.1.0-rc.2', ['receipt-previous'])
const current = await managedRecord(context, 'current-invalid', 3, 'stable', '0.1.0-rc.3', ['receipt-current'])
await context.manager.activateManaged(previous, { healthCheck: async () => {}, activateReceipts: async () => {} })
await context.manager.activateManaged(current, { healthCheck: async () => {}, activateReceipts: async () => {} })
await rm(join(context.paths.runtimesRoot, current.runtime.id), { recursive: true })
const manager = new DeploymentManager({ paths: context.paths, seedRoot: context.seedRoot, inventory: context.inventory })
await assert.rejects(manager.prepareImage(context.image), DeploymentResolutionError)
const plan = await manager.preparePreviousRecovery()
assert.equal((await manager.selected()).record.id, previous.id)
assert.deepEqual(await manager.state(), { schema: 1, generation: 4, current: current.id, previous: previous.id })
const activations = []
await manager.acceptPreviousRecovery(plan, async tokens => { activations.push(tokens) })
assert.deepEqual(activations, [['receipt-previous']])
assert.deepEqual(await manager.state(), { schema: 1, generation: 5, current: previous.id, previous: current.id })
})
test('finishes automatic previous recovery after interruption between receipts and slots', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const previous = await managedRecord(context, 'recovery-interrupted-previous', 2, 'stable', '0.1.0-rc.2', ['receipt-previous'])
const current = await managedRecord(context, 'recovery-interrupted-current', 3, 'stable', '0.1.0-rc.3', ['receipt-current'])
await context.manager.activateManaged(previous, { healthCheck: async () => {}, activateReceipts: async () => {} })
await context.manager.activateManaged(current, { healthCheck: async () => {}, activateReceipts: async () => {} })
const manager = new DeploymentManager({ paths: context.paths, seedRoot: context.seedRoot, inventory: context.inventory })
const plan = await manager.preparePreviousRecovery()
manager.commit = async () => { throw new Error('simulated interruption before slots commit') }
await assert.rejects(
manager.acceptPreviousRecovery(plan, async tokens => assert.deepEqual(tokens, ['receipt-previous'])),
/simulated interruption/,
)
assert.equal((await manager.activation()).phase, 'receipts-active')
assert.equal((await manager.state()).current, current.id)
const restarted = new DeploymentManager({ paths: context.paths, seedRoot: context.seedRoot, inventory: context.inventory })
const recovered = await restarted.recoverActivation({
activeReceipts: async () => ({ receipts: [{ token: 'receipt-previous' }] }),
activate: async () => { throw new Error('active previous must be completed') },
})
assert.deepEqual(recovered, { action: 'committed', recordId: previous.id })
assert.deepEqual(await restarted.state(), { schema: 1, generation: 5, current: previous.id, previous: current.id })
assert.equal(await restarted.activation(), undefined)
})
test('does not automatically downgrade an unparseable current Deployment Record', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const previous = await managedRecord(context, 'record-valid-previous', 2)
const current = await managedRecord(context, 'record-corrupt-current', 3)
await context.manager.activateManaged(previous, { healthCheck: async () => {}, activateReceipts: async () => {} })
await context.manager.activateManaged(current, { healthCheck: async () => {}, activateReceipts: async () => {} })
await writeFile(context.manager.recordPath(current.id), '{invalid')
const manager = new DeploymentManager({ paths: context.paths, seedRoot: context.seedRoot, inventory: context.inventory })
await assert.rejects(manager.prepareImage(context.image), error => {
assert.equal(error instanceof DeploymentResolutionError, false)
assert.match(error.message, /valid JSON/)
return true
})
await assert.rejects(manager.preparePreviousRecovery(), /valid JSON/)
assert.deepEqual(await manager.state(), { schema: 1, generation: 4, current: current.id, previous: previous.id })
})
test('keeps a newer managed Deployment when the image is behind', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const managed = await context.manager.writeRecord(await managedRecord(context, 'ahead', 3))
await context.manager.activate(managed.id, async () => {})
const plan = await context.manager.prepareImage(context.image)
assert.equal(plan.action, 'retain')
assert.equal(plan.imageBehindCurrent, true)
assert.equal(plan.target, managed.id)
})
test('rejects same-sequence Stable content conflicts', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const conflicting = await context.manager.writeRecord(await managedRecord(context, 'conflict', 1))
await context.manager.activate(conflicting.id, async () => {})
await assert.rejects(context.manager.prepareImage(context.image), /same targetSequence/)
})
test('prefers a same-sequence image when the managed Environment differs only by file mode', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const managed = await context.manager.materializeCurrent()
const environmentPath = await context.manager.resolveReference(managed.environment)
await chmod(join(environmentPath, 'sentinel'), 0o444)
const content = {
...managed,
environment: { ...managed.environment, sha256: await hashTree(environmentPath) },
}
delete content.id
const current = await context.manager.writeRecord({
...content,
id: deriveRecordId('deployment-record', content),
})
await context.manager.commit(current.id, null)
const plan = await context.manager.prepareImage(context.image)
assert.equal(plan.action, 'prefer-image')
assert.equal(plan.target, context.image.id)
})
test('rejects a same-sequence image when the managed Environment content differs', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const managed = await context.manager.materializeCurrent()
const environmentPath = await context.manager.resolveReference(managed.environment)
await writeFile(join(environmentPath, 'sentinel'), 'different environment content')
const content = {
...managed,
environment: { ...managed.environment, sha256: await hashTree(environmentPath) },
}
delete content.id
const current = await context.manager.writeRecord({
...content,
id: deriveRecordId('deployment-record', content),
})
await context.manager.commit(current.id, null)
await assert.rejects(context.manager.prepareImage(context.image), /same targetSequence/)
})
test('replaces a rebuilt development image without retaining stale image slots', async () => {
const first = await fixture({ authority: 'development', targetSequence: 0, marker: 'development-one' })
await first.manager.initialize(first.image)
const rebuilt = await fixture({
root: first.root,
paths: first.paths,
authority: 'development',
targetSequence: 0,
marker: 'development-two',
})
const plan = await rebuilt.manager.prepareImage(rebuilt.image)
assert.equal(plan.action, 'development-refresh')
assert.equal(plan.fallback, null)
assert.equal(await readFile(join(rebuilt.paths.viewsRoot, 'runtime', 'sentinel'), 'utf8'), 'runtime:development-two')
const state = await rebuilt.manager.acceptImage(plan)
assert.equal(state.current, rebuilt.image.id)
assert.equal(state.previous, null)
})
test('replaces stale formal Image Deployment slots when starting a development image', async () => {
const formal = await fixture({ authority: 'stable', targetSequence: 11, marker: 'formal-eleven' })
await formal.manager.initialize(formal.image)
const development = await fixture({
root: formal.root,
paths: formal.paths,
authority: 'development',
targetSequence: 0,
marker: 'local-rebuild',
})
const plan = await development.manager.prepareImage(development.image)
assert.equal(plan.action, 'development-refresh')
assert.equal(plan.fallback, null)
const state = await development.manager.acceptImage(plan)
assert.equal(state.current, development.image.id)
assert.equal(state.previous, null)
assert.equal(await readFile(join(development.paths.viewsRoot, 'runtime', 'sentinel'), 'utf8'), 'runtime:local-rebuild')
})
test('replaces stale Managed Deployment assets when starting a compatible development image', async () => {
const formal = await fixture({ authority: 'stable', targetSequence: 11, marker: 'formal-eleven' })
await formal.manager.initialize(formal.image)
const managed = await formal.manager.writeRecord(await managedRecord(
formal,
'managed-eleven',
11,
'stable',
'0.1.0-rc.1',
))
await formal.manager.activate(managed.id, async () => {})
const development = await fixture({
root: formal.root,
paths: formal.paths,
authority: 'development',
targetSequence: 0,
marker: 'local-managed-refresh',
})
const plan = await development.manager.prepareImage(development.image)
assert.equal(plan.action, 'development-refresh')
assert.equal(plan.fallback, null)
const state = await development.manager.acceptImage(plan)
assert.equal(state.current, development.image.id)
assert.equal(state.previous, null)
assert.equal(
await readFile(join(development.paths.viewsRoot, 'system-plugins', 'sentinel'), 'utf8'),
'system-plugins:local-managed-refresh',
)
})
test('preserves Experimental DSH until a Stable image catches up', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const experimental = await context.manager.writeRecord(await managedRecord(
context,
'experimental',
1,
'experimental',
'0.1.0-rc.3',
))
await context.manager.activate(experimental.id, async () => {})
const behindDshContent = {
...context.inventory.document,
platformRevision: 'stable-environment-forward',
targetSequence: 2,
}
delete behindDshContent.imageBuildId
const behindDshInventory = parseImageInventory({
...behindDshContent,
imageBuildId: deriveImageBuildId(behindDshContent),
})
const environmentForward = new DeploymentManager({
paths: context.paths,
seedRoot: context.seedRoot,
inventory: behindDshInventory,
})
const retained = await environmentForward.prepareImage(recordsFromImageInventory(behindDshInventory).deployment)
assert.equal(retained.target, experimental.id)
assert.equal(retained.requiresExperimentalRebuild, true)
const caughtUpContent = structuredClone(behindDshContent)
caughtUpContent.platformRevision = 'stable-caught-up'
caughtUpContent.deployment.dshVersion = '0.1.0-rc.3'
const caughtUpInventory = parseImageInventory({
...caughtUpContent,
imageBuildId: deriveImageBuildId(caughtUpContent),
})
const caughtUpManager = new DeploymentManager({
paths: context.paths,
seedRoot: context.seedRoot,
inventory: caughtUpInventory,
})
const caughtUp = await caughtUpManager.prepareImage(recordsFromImageInventory(caughtUpInventory).deployment)
assert.equal(caughtUp.action, 'stable-caught-up')
assert.notEqual(caughtUp.target, experimental.id)
})
test('activates receipts and commits one complete Managed Deployment transaction', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const candidate = await managedRecord(context, 'transaction', 2, 'stable', '0.1.0-rc.2', ['candidate-receipt'])
let active = []
const trust = {
activate: async tokens => { active = [...tokens] },
activeReceipts: async () => ({ receipts: active.map(token => ({ token })) }),
}
const slots = await context.manager.activateManaged(candidate, {
healthCheck: async () => {
assert.equal(await readFile(join(context.paths.viewsRoot, 'runtime', 'sentinel'), 'utf8'), 'runtime:transaction')
},
activateReceipts: tokens => trust.activate(tokens),
})
assert.equal(slots.current, candidate.id)
assert.deepEqual(active, ['candidate-receipt'])
const previous = await context.manager.record(slots.previous)
assert.equal(previous.runtime.storage, 'store')
assert.equal(await context.manager.activation(), undefined)
})
test('rolls back only the exact previous Deployment with receipts and health checks', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const candidate = await managedRecord(context, 'rollback-target', 2, 'stable', '0.1.0-rc.2', ['candidate-receipt'])
const active = []
await context.manager.activateManaged(candidate, {
healthCheck: async () => {},
activateReceipts: async tokens => active.push([...tokens]),
})
const before = await context.manager.state()
const previous = await context.manager.record(before.previous)
let healthChecks = 0
const rolledBack = await context.manager.rollback({
healthCheck: async () => {
healthChecks += 1
assert.equal(JSON.parse(await readFile(context.paths.deploymentStatusPath, 'utf8')).operation, 'recovering')
},
activateReceipts: async tokens => active.push([...tokens]),
})
assert.equal(rolledBack.current, previous.id)
assert.equal(rolledBack.previous, candidate.id)
assert.deepEqual(active.at(-1), previous.receiptTokens)
assert.equal(healthChecks, 1)
assert.equal(await context.manager.activation(), undefined)
const status = await context.manager.publishStatus()
assert.equal(status.current.recordId, previous.id)
assert.equal(status.operation, null)
})
test('reports an image behind a newly activated Managed Deployment immediately', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const candidate = await managedRecord(context, 'status-ahead', 2, 'stable', '0.1.0-rc.2')
await context.manager.activateManaged(candidate, {
healthCheck: async () => {},
activateReceipts: async () => {},
})
const status = await context.manager.publishStatus()
assert.equal(status.current.recordId, candidate.id)
assert.equal(status.imageBehindCurrent, true)
})
test('restores the materialized previous Deployment when receipt activation fails', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const candidate = await managedRecord(context, 'receipt-failure', 2, 'stable', '0.1.0-rc.2', ['candidate-receipt'])
await assert.rejects(context.manager.activateManaged(candidate, {
healthCheck: async () => {},
activateReceipts: async () => { throw new Error('receipt activation failed') },
}), /receipt activation failed/)
const state = await context.manager.state()
assert.notEqual(state.current, context.image.id)
assert.equal((await context.manager.record(state.current)).runtime.storage, 'store')
assert.equal(await readFile(join(context.paths.viewsRoot, 'runtime', 'sentinel'), 'utf8'), 'runtime:image')
assert.equal(await context.manager.activation(), undefined)
})
test('publishes switching and recovering states around a failed atomic activation', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
await context.manager.publishStatus()
const candidate = await managedRecord(context, 'operation-status', 2, 'stable', '0.1.0-rc.2')
const observed = []
await assert.rejects(context.manager.activateManaged(candidate, {
healthCheck: async () => {
observed.push(JSON.parse(await readFile(context.paths.deploymentStatusPath, 'utf8')).operation)
if (observed.length === 1) throw new Error('candidate failed')
},
activateReceipts: async () => {},
}), /candidate failed/)
assert.deepEqual(observed, ['switching', 'recovering'])
assert.equal(JSON.parse(await readFile(context.paths.deploymentStatusPath, 'utf8')).operation, null)
})
test('turns a failed image recovery operation into an explicit recovery failure', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
await context.manager.publishStatus({ recoveryMode: 'current deployment failed' })
let operation
await assert.rejects(context.manager.recoverImageBaseline(context.image, {
healthCheck: async () => {
const status = JSON.parse(await readFile(context.paths.deploymentStatusPath, 'utf8'))
operation = status.operation
assert.equal(status.recoveryMode, 'current deployment failed')
throw new Error('image recovery health failed')
},
activateReceipts: async () => {},
}), /image recovery health failed/)
assert.equal(operation, 'recovering')
const failed = JSON.parse(await readFile(context.paths.deploymentStatusPath, 'utf8'))
assert.equal(failed.operation, null)
assert.equal(failed.recoveryMode, 'image recovery health failed')
})
test('finishes a journaled activation after receipts became active before restart', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const from = await context.manager.materializeCurrent()
const candidate = await context.manager.writeRecord(await managedRecord(
context,
'resume',
2,
'stable',
'0.1.0-rc.2',
['candidate-receipt'],
))
await context.manager.writeActivation({ phase: 'healthy', from: from.id, to: candidate.id })
const recovered = await context.manager.recoverActivation({
activeReceipts: async () => ({ receipts: [{ token: 'candidate-receipt' }] }),
activate: async () => { throw new Error('active candidate must be completed') },
})
assert.deepEqual(recovered, { action: 'committed', recordId: candidate.id })
assert.equal((await context.manager.state()).current, candidate.id)
assert.equal(await context.manager.activation(), undefined)
})
test('keeps Experimental candidate slots uncommitted through probation and supports cancellation', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const candidate = await experimentalRecord(
context,
context.image,
'probation',
'0.1.0-rc.2',
['experimental-receipt'],
)
await context.manager.stageCandidate(candidate, async () => {
assert.equal(await readFile(join(context.paths.viewsRoot, 'runtime', 'sentinel'), 'utf8'), 'runtime:probation')
})
const during = await context.manager.state()
assert.notEqual(during.current, candidate.id)
assert.equal((await context.manager.activation()).phase, 'probation')
const cancelled = await context.manager.cancelCandidate()
assert.equal(cancelled.cancelled, true)
assert.equal(await readFile(join(context.paths.viewsRoot, 'runtime', 'sentinel'), 'utf8'), 'runtime:image')
assert.equal(await context.manager.activation(), undefined)
})
test('rejects rollback and cross-authority candidates at the Bootstrap boundary', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const advanced = await managedRecord(context, 'advanced', 3)
await context.manager.activateManaged(advanced, { healthCheck: async () => {}, activateReceipts: async () => {} })
const rollback = await managedRecord(context, 'rollback', 2)
await assert.rejects(context.manager.activateManaged(rollback, {
healthCheck: async () => {}, activateReceipts: async () => {},
}), /roll back/)
const wrongExperimental = await managedRecord(context, 'wrong-environment', 3, 'experimental', '0.1.0-rc.4')
await assert.rejects(context.manager.stageCandidate(wrongExperimental, async () => {}), /retain the current Environment/)
})
test('does not restart previous DSH before snapshot recovery after candidate health failure', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const candidate = await experimentalRecord(context, context.image, 'health-failure', '0.1.0-rc.2')
let healthCalls = 0
await assert.rejects(context.manager.stageCandidate(candidate, async () => {
healthCalls += 1
throw new Error('candidate failed health')
}), /candidate failed health/)
assert.equal(healthCalls, 1)
assert.equal(await readFile(join(context.paths.viewsRoot, 'runtime', 'sentinel'), 'utf8'), 'runtime:image')
})
test('collects only Store assets and records outside slots, transactions, Holds, and snapshots', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const current = await context.manager.writeRecord(await managedRecord(context, 'current', 2))
await context.manager.activateManaged(current, {
healthCheck: async () => {},
activateReceipts: async () => {},
})
const orphan = await context.manager.writeRecord(await managedRecord(context, 'orphan', 3))
const held = await context.manager.writeRecord(await managedRecord(
context,
'held',
4,
'experimental',
'0.1.0-rc.held',
))
await writeFile(join(context.paths.updaterStateRoot, 'channel.json'), JSON.stringify({
schema: 1,
updateChannel: 'experimental',
holds: [{
id: 'hold', type: 'version', dshVersion: held.dshVersion, environmentVersion: null,
reason: 'fixture', createdAt: '2026-08-19T00:00:00.000Z',
}],
experimentalBlocked: null,
}))
await mkdir(join(context.paths.snapshotsRoot, 'versions', 'snapshot-kept'), { recursive: true })
await mkdir(join(context.paths.snapshotsRoot, 'versions', 'snapshot-orphan'), { recursive: true })
await writeFile(join(context.paths.updaterStateRoot, 'transaction.json'), JSON.stringify({
from: { runtime: current.id }, to: { runtime: current.id }, snapshotId: 'snapshot-kept',
}))
await writeFile(join(context.paths.downloadsRoot, 'disposable'), 'cache')
await mkdir(context.paths.systemSkillCatalogsRoot, { recursive: true })
await writeFile(join(context.paths.systemSkillCatalogsRoot, 'catalog-marker'), 'retained')
const removedSnapshots = []
const result = await new PlatformGarbageCollector({
paths: context.paths,
deployments: context.manager,
snapshots: {
remove: async id => {
removedSnapshots.push(id)
await rm(join(context.paths.snapshotsRoot, 'versions', id), { recursive: true, force: true })
return true
},
},
}).collect()
assert.equal(result.deployments.records.includes(orphan.id), true)
assert.equal(result.deployments.records.includes(held.id), false)
await assert.rejects(lstat(context.manager.recordPath(orphan.id)), { code: 'ENOENT' })
assert.equal((await lstat(context.manager.recordPath(held.id))).isFile(), true)
await assert.rejects(lstat(join(context.paths.runtimesRoot, orphan.runtime.id)), { code: 'ENOENT' })
assert.equal((await lstat(join(context.paths.runtimesRoot, held.runtime.id))).isDirectory(), true)
assert.equal((await lstat(join(context.paths.snapshotsRoot, 'versions', 'snapshot-kept'))).isDirectory(), true)
assert.deepEqual(removedSnapshots, ['snapshot-orphan'])
await assert.rejects(lstat(join(context.paths.snapshotsRoot, 'versions', 'snapshot-orphan')), { code: 'ENOENT' })
await assert.rejects(lstat(join(context.paths.downloadsRoot, 'disposable')), { code: 'ENOENT' })
assert.equal(await readFile(join(context.paths.systemSkillCatalogsRoot, 'catalog-marker'), 'utf8'), 'retained')
})
test('explicit recovery replaces an invalid Managed current with the exact Image Baseline', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const managed = await managedRecord(context, 'invalid-managed', 3)
await context.manager.activateManaged(managed, { healthCheck: async () => {}, activateReceipts: async () => {} })
let activated
const state = await context.manager.recoverImageBaseline(context.image, {
healthCheck: async () => {
assert.equal(await readFile(join(context.paths.viewsRoot, 'runtime', 'sentinel'), 'utf8'), 'runtime:image')
},
activateReceipts: async tokens => { activated = tokens },
})
assert.equal(state.current, context.image.id)
assert.equal(state.previous, managed.id)
assert.deepEqual(activated, [])
})
test('explicit recovery repairs only the exact image Record after its persisted bytes are damaged', async () => {
const context = await fixture()
await context.manager.initialize(context.image)
const managed = await managedRecord(context, 'managed-before-repair', 3)
await context.manager.activateManaged(managed, { healthCheck: async () => {}, activateReceipts: async () => {} })
await writeFile(context.manager.recordPath(context.image.id), '{"damaged":true}\n')
const state = await context.manager.recoverImageBaseline(context.image, {
healthCheck: async () => {},
activateReceipts: async () => {},
})
assert.equal(state.current, context.image.id)
assert.deepEqual(await context.manager.record(context.image.id), context.image)
await assert.rejects(context.manager.repairImageRecord(managed), /differs from the current image inventory/)
})
@@ -0,0 +1,135 @@
import assert from 'node:assert/strict'
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import {
DSH_COMPATIBILITY_PACKAGES,
DSH_REMOTE_METHODS,
DSH_STATIC_CLIENT_MODULES,
DSH_WEB_ROWS,
verifyDshCompatibility,
} from '../lib/dsh-compatibility.mjs'
const VERSION = '0.1.5-rc.1'
async function writeJson(path, value) {
await writeFile(path, `${JSON.stringify(value)}\n`)
}
async function fixture() {
const root = await mkdtemp(join(tmpdir(), 'dsh-compatibility-fixture-'))
await mkdir(join(root, 'lib'), { recursive: true })
await writeJson(join(root, 'package.json'), {
name: '@deepseek-ai/dsh',
version: VERSION,
type: 'module',
bin: { dsh: 'lib/bin.js' },
dependencies: Object.fromEntries(Object.keys(DSH_COMPATIBILITY_PACKAGES).map(name => [name, `^${VERSION}`])),
})
const config = Object.entries(DSH_WEB_ROWS)
.map(([id, name]) => `- id: ${id}\n name: '${name}'`)
.join('\n')
await writeFile(join(root, 'lib/bin.js'), `console.log(${JSON.stringify(config)})\n`)
for (const [name, exports] of Object.entries(DSH_COMPATIBILITY_PACKAGES)) {
const directory = join(root, 'node_modules', ...name.split('/'))
await mkdir(join(directory, 'lib'), { recursive: true })
await writeJson(join(directory, 'package.json'), {
name,
version: VERSION,
exports: Object.fromEntries(exports.map(subpath => [subpath, './lib/index.js'])),
})
await writeFile(join(directory, 'lib/index.js'), '')
}
await writeFile(
packageFile(root, '@deepseek-ai/dsh-api-remotes', 'lib/client.js'),
DSH_REMOTE_METHODS.join('\n'),
)
const frontendAssets = packageFile(root, '@deepseek-ai/dsh-web-frontend', 'dist/assets')
await mkdir(frontendAssets, { recursive: true })
await writeFile(join(frontendAssets, 'index.js'), `{${DSH_STATIC_CLIENT_MODULES
.map(name => `${JSON.stringify(name)}:fixture`)
.join(',')}}\n`)
return root
}
function packageFile(root, packageName, file) {
return join(root, 'node_modules', ...packageName.split('/'), file)
}
function removeAfter(t, ...paths) {
t.after(() => Promise.all(paths.map(path => rm(path, { recursive: true, force: true }))))
}
test('accepts the complete DSH package, Web profile, and Remote contract', async t => {
const packageRoot = await fixture()
const home = await mkdtemp(join(tmpdir(), 'dsh-compatibility-home-'))
removeAfter(t, packageRoot, home)
assert.deepEqual(await verifyDshCompatibility({ packageRoot, expectedVersion: VERSION, home }), {
name: '@deepseek-ai/dsh',
version: VERSION,
packages: 10,
remoteMethods: 3,
webRows: 5,
})
})
test('rejects a stale installed package in the DSH dependency closure', async t => {
const packageRoot = await fixture()
const home = await mkdtemp(join(tmpdir(), 'dsh-compatibility-home-'))
removeAfter(t, packageRoot, home)
const manifestPath = packageFile(packageRoot, '@deepseek-ai/dsh-client-connection', 'package.json')
const manifest = JSON.parse(await readFile(manifestPath, 'utf8'))
await writeJson(manifestPath, { ...manifest, version: '0.1.2-rc.1' })
await assert.rejects(
verifyDshCompatibility({ packageRoot, expectedVersion: VERSION, home }),
/dsh-client-connection is not installed at DSH version 0\.1\.5-rc\.1/,
)
})
test('rejects missing Web rows and management Remote methods', async t => {
const packageRoot = await fixture()
const home = await mkdtemp(join(tmpdir(), 'dsh-compatibility-home-'))
removeAfter(t, packageRoot, home)
await writeFile(join(packageRoot, 'lib/bin.js'), 'console.log("- id: connection\\n name: missing")\n')
await assert.rejects(
verifyDshCompatibility({ packageRoot, expectedVersion: VERSION, home }),
/DSH Web profile does not contain/,
)
await writeFile(
packageFile(packageRoot, '@deepseek-ai/dsh-api-remotes', 'lib/client.js'),
DSH_REMOTE_METHODS.slice(1).join('\n'),
)
await assert.rejects(
verifyDshCompatibility({ packageRoot, expectedVersion: VERSION, home }),
/Remote registry is missing/,
)
})
test('rejects each missing static module required by a bundled System Plugin', async t => {
for (const missing of DSH_STATIC_CLIENT_MODULES) {
await t.test(missing, async t => {
const packageRoot = await fixture()
const home = await mkdtemp(join(tmpdir(), 'dsh-compatibility-home-'))
removeAfter(t, packageRoot, home)
const present = DSH_STATIC_CLIENT_MODULES.filter(name => name !== missing)
await writeFile(
packageFile(packageRoot, '@deepseek-ai/dsh-web-frontend', 'dist/assets/index.js'),
`{${present.map(name => `${JSON.stringify(name)}:fixture`).join(',')}}\n`,
)
await assert.rejects(
verifyDshCompatibility({ packageRoot, expectedVersion: VERSION, home }),
new RegExp(`Web static module table is missing ${JSON.stringify(missing).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`),
)
})
}
})
test('requires every static browser module consumed directly by bundled System Plugins', () => {
assert.deepEqual(DSH_STATIC_CLIENT_MODULES, [
'@deepseek-ai/dsh-client-ui-primitives',
'react',
'react-dom',
])
})
@@ -0,0 +1,145 @@
import assert from 'node:assert/strict'
import { request } from 'node:http'
import { mkdtemp, stat } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import {
createDshLifecycleServer,
DshLifecycleBroker,
listenDshLifecycle,
} from '../bootstrap/lib/dsh-lifecycle.mjs'
function call(socketPath, path, body) {
return new Promise((resolve, reject) => {
const req = request({ socketPath, path, method: 'POST', headers: { 'content-type': 'application/json' } }, response => {
const chunks = []
response.on('data', chunk => chunks.push(chunk))
response.on('end', () => resolve({ status: response.statusCode, body: JSON.parse(Buffer.concat(chunks)) }))
})
req.once('error', reject)
req.end(JSON.stringify(body))
})
}
test('authorizes exactly one claim for each supervised DSH launch', async () => {
const reports = []
const broker = new DshLifecycleBroker({ report: (message, fields) => reports.push({ message, fields }) })
const unrelated = broker.prepareLaunch('gateway')
assert.deepEqual(unrelated.environment, {})
const launch = broker.prepareLaunch('dsh-runtime')
const first = broker.claim(launch.environment.DSH_PLATFORM_LAUNCH_TOKEN)
assert.match(first.sessionId, /^[0-9a-f-]{36}$/)
assert.deepEqual(broker.readiness(), {
generation: first.sessionId, publicReady: false, ready: false, readyUrl: null, verified: false,
})
assert.deepEqual(broker.ready(first.sessionId), { ready: true })
assert.deepEqual(broker.readiness(), {
generation: first.sessionId, publicReady: false, ready: true, readyUrl: null, verified: false,
})
broker.verify()
assert.equal(broker.readiness().publicReady, false)
broker.publish()
assert.equal(broker.readiness().publicReady, true)
assert.throws(() => broker.claim(launch.environment.DSH_PLATFORM_LAUNCH_TOKEN), /already consumed/)
launch.release()
await assert.rejects(broker.signal(first.sessionId, 'SIGTERM'), /session is invalid/)
await new Promise(resolve => setImmediate(resolve))
assert.deepEqual(reports.map(value => value.message), [
'dsh.launch.authorized', 'dsh.launch.claimed', 'dsh.launch.ready', 'dsh.launch.verified',
'platform.public-ready', 'dsh.launch.released',
])
assert.doesNotMatch(JSON.stringify(reports), new RegExp(launch.environment.DSH_PLATFORM_LAUNCH_TOKEN))
assert.doesNotMatch(JSON.stringify(reports), new RegExp(first.sessionId))
})
test('invalidates an old launch without allowing its cleanup to release the replacement', () => {
const broker = new DshLifecycleBroker()
const oldLaunch = broker.prepareLaunch('dsh-runtime')
const oldSession = broker.claim(oldLaunch.environment.DSH_PLATFORM_LAUNCH_TOKEN)
broker.ready(oldSession.sessionId)
broker.verify()
broker.publish()
assert.equal(broker.readiness().publicReady, true)
const nextLaunch = broker.prepareLaunch('dsh-runtime')
assert.equal(broker.readiness().publicReady, false)
oldLaunch.release()
const session = broker.claim(nextLaunch.environment.DSH_PLATFORM_LAUNCH_TOKEN)
assert.match(session.sessionId, /^[0-9a-f-]{36}$/)
})
test('returns restart only for an active session outside an owned shutdown', async () => {
let owned = false
const broker = new DshLifecycleBroker({ shouldTerminate: async () => owned })
const launch = broker.prepareLaunch('dsh-runtime')
const { sessionId } = broker.claim(launch.environment.DSH_PLATFORM_LAUNCH_TOKEN)
assert.deepEqual(await broker.signal(sessionId, 'SIGTERM'), { disposition: 'terminate' })
broker.ready(sessionId)
assert.deepEqual(await broker.signal(sessionId, 'SIGTERM'), { disposition: 'request-restart' })
owned = true
assert.deepEqual(await broker.signal(sessionId, 'SIGTERM'), { disposition: 'terminate' })
owned = false
broker.beginShutdown()
assert.deepEqual(await broker.signal(sessionId, 'SIGTERM'), { disposition: 'terminate' })
await assert.rejects(broker.signal(sessionId, 'SIGINT'), /signal is invalid/)
})
test('serves launch ownership and readiness over a private Unix socket', async t => {
const root = await mkdtemp(join(tmpdir(), 'dsh-lifecycle-broker-'))
const socketPath = join(root, 'dsh-lifecycle.sock')
const reports = []
const broker = new DshLifecycleBroker({ report: (message, fields) => reports.push({ message, fields }) })
const server = createDshLifecycleServer(broker)
t.after(() => server.close())
await listenDshLifecycle(server, socketPath)
assert.equal((await stat(socketPath)).mode & 0o777, 0o600)
const launch = broker.prepareLaunch('dsh-runtime')
const claim = await call(socketPath, '/v1/runtime/claim', {
launchToken: launch.environment.DSH_PLATFORM_LAUNCH_TOKEN,
})
assert.equal(claim.status, 200)
assert.deepEqual(await call(socketPath, '/v1/runtime/readiness', {}), {
status: 200,
body: {
generation: claim.body.sessionId, publicReady: false, ready: false, readyUrl: null, verified: false,
},
})
const readyUrl = 'http://127.0.0.1:3079/?token=fixture-token'
assert.deepEqual(await call(socketPath, '/v1/runtime/ready', { readyUrl, sessionId: claim.body.sessionId }), {
status: 200,
body: { ready: true },
})
assert.deepEqual(await call(socketPath, '/v1/runtime/ready', { sessionId: claim.body.sessionId }), {
status: 200,
body: { ready: true },
})
assert.deepEqual(await call(socketPath, '/v1/runtime/readiness', {}), {
status: 200,
body: {
generation: claim.body.sessionId, publicReady: false, ready: true, readyUrl, verified: false,
},
})
broker.verify()
broker.publish()
assert.equal((await call(socketPath, '/v1/runtime/readiness', {})).body.publicReady, true)
const signal = await call(socketPath, '/v1/runtime/signal', {
sessionId: claim.body.sessionId,
signal: 'SIGTERM',
})
assert.deepEqual(signal, { status: 200, body: { disposition: 'request-restart' } })
assert.equal((await call(socketPath, '/v1/runtime/claim', {
launchToken: launch.environment.DSH_PLATFORM_LAUNCH_TOKEN,
})).status, 409)
assert.equal((await call(socketPath, '/v1/runtime/signal', {
sessionId: claim.body.sessionId,
signal: 'SIGTERM',
unexpected: true,
})).status, 400)
assert.equal((await call(socketPath, '/v1/unknown', {})).status, 404)
assert.equal(reports.at(-2).message, 'dsh.lifecycle-request.failed')
assert.equal(reports.at(-2).fields.level, 'warning')
assert.equal(reports.at(-1).message, 'dsh.lifecycle-request.failed')
assert.equal(reports.at(-1).fields.level, 'error')
})
@@ -0,0 +1,303 @@
import assert from 'node:assert/strict'
import { spawn } from 'node:child_process'
import { createServer } from 'node:http'
import { mkdtemp, symlink } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { verifyDshWebReady } from '../../control-plane/hooks/dsh-web-ready.mjs'
async function fixture(routes) {
const requests = new Map()
const server = createServer((request, response) => {
requests.set(request.url, (requests.get(request.url) ?? 0) + 1)
const configured = routes.get(`${request.method} ${request.url}`) ?? routes.get(request.url)
const route = typeof configured === 'function' ? configured(requests.get(request.url)) : configured
response.writeHead(route?.status ?? 404, {
'content-type': route?.type ?? 'text/plain',
...route?.headers,
})
response.end(route?.body ?? '')
})
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve))
return { server, port: server.address().port, requests }
}
test('DSH web readiness requires every boot manifest Plugin bundle', async () => {
const manifest = JSON.stringify({ rev: 'one', entries: [
{ id: 'one', url: '/plugins/one/client.js?rev=one', rev: 'one' },
{ id: 'two', url: '/plugins/two/client.js?rev=two', rev: 'two' },
] })
const routes = new Map([
['/', { status: 200, type: 'text/html', body: `<script>globalThis["__DSH_BOOT__"] = ${manifest}</script>` }],
['/plugins/one/client.js?rev=one', { status: 200, type: 'text/javascript', body: 'one' }],
['/plugins/two/client.js?rev=two', { status: 404 }],
['POST /api/pluginInventory/list', { status: 200, type: 'application/json', body: JSON.stringify({
type: 'server-response',
rpcId: 'dsh-platform-readiness',
result: { ok: true, value: { entries: [{ moduleName: 'ready', enabled: true, fiberPhase: 'active' }] } },
}) }],
])
const context = await fixture(routes)
try {
await assert.rejects(verifyDshWebReady({ port: context.port, managedReady: async () => {} }), /returned HTTP 404/)
routes.set('/plugins/two/client.js?rev=two', { status: 200, type: 'text/javascript', body: 'two' })
await verifyDshWebReady({ port: context.port, managedReady: async () => {} })
assert.equal(context.requests.get('/'), 3)
assert.equal(context.requests.get('/plugins/one/client.js?rev=one'), 3)
assert.equal(context.requests.get('/plugins/two/client.js?rev=two'), 3)
assert.equal(context.requests.get('/api/pluginInventory/list'), 2)
} finally {
await new Promise(resolve => context.server.close(resolve))
}
})
test('DSH web readiness verifies 0.1.5 initial combo batches', async () => {
const entryUrl = '/plugins/??one/client.js&rev=entry-one'
const batchUrl = '/plugins/??one/client.js,two/client.js&rev=batch-one'
const manifest = JSON.stringify({
rev: 'graph-one',
entries: [{ id: 'one', url: entryUrl, rev: 'entry-one' }],
batches: [{ phase: 'application', url: batchUrl, rev: 'batch-one', entries: ['one', 'two'] }],
})
const routes = new Map([
['/', { status: 200, type: 'text/html', body: `<script>window.__DSH_BOOT__ = ${manifest}</script>` }],
[entryUrl, { status: 200, type: 'text/javascript', body: 'one' }],
[batchUrl, { status: 404 }],
['POST /api/pluginInventory/list', { status: 200, type: 'application/json', body: JSON.stringify({
type: 'server-response',
rpcId: 'dsh-platform-readiness',
result: { ok: true, value: { entries: [{ moduleName: 'ready', enabled: true, fiberPhase: 'active' }] } },
}) }],
])
const context = await fixture(routes)
try {
await assert.rejects(
verifyDshWebReady({ port: context.port, managedReady: async () => ({}) }),
/returned HTTP 404/,
)
routes.set(batchUrl, { status: 200, type: 'text/javascript', body: 'one and two' })
await verifyDshWebReady({ port: context.port, managedReady: async () => ({}) })
assert.equal(context.requests.get(entryUrl), 3)
assert.equal(context.requests.get(batchUrl), 3)
} finally {
await new Promise(resolve => context.server.close(resolve))
}
})
test('DSH web readiness rejects a malformed combo batch contract', async () => {
const manifest = JSON.stringify({
rev: 'graph-one',
entries: [{ id: 'one', url: '/plugins/one/client.js?rev=one', rev: 'one' }],
batches: { phase: 'application', url: '/plugins/??one/client.js&rev=one' },
})
const context = await fixture(new Map([
['/', { status: 200, type: 'text/html', body: `<script>window.__DSH_BOOT__ = ${manifest}</script>` }],
]))
try {
await assert.rejects(
verifyDshWebReady({ port: context.port, managedReady: async () => ({}) }),
/boot manifest batches are invalid/,
)
} finally {
await new Promise(resolve => context.server.close(resolve))
}
})
test('DSH web readiness exchanges the private launch token for an authenticated cookie', async () => {
const manifest = JSON.stringify({ rev: 'one', entries: [
{ id: 'one', url: '/plugins/one/client.js?rev=one', rev: 'one' },
] })
let expectedCookie = ''
const routes = new Map([
['/?token=fixture-token', {
status: 303,
headers: { location: '/', 'set-cookie': 'dsh-auth-fixture=session; Path=/; HttpOnly; SameSite=Strict' },
}],
])
const context = await fixture(routes)
context.server.removeAllListeners('request')
context.server.on('request', (request, response) => {
context.requests.set(request.url, (context.requests.get(request.url) ?? 0) + 1)
if (request.url === '/?token=fixture-token') {
response.writeHead(303, {
location: '/',
'set-cookie': 'dsh-auth-fixture=session; Path=/; HttpOnly; SameSite=Strict',
})
response.end()
return
}
expectedCookie = request.headers.cookie ?? ''
if (expectedCookie !== 'dsh-auth-fixture=session') {
response.writeHead(401)
response.end('unauthorized')
return
}
if (request.url === '/') {
response.writeHead(200, { 'content-type': 'text/html' })
response.end(`<script>window.__DSH_BOOT__ = ${manifest}</script>`)
} else if (request.url === '/plugins/one/client.js?rev=one') {
response.writeHead(200, { 'content-type': 'text/javascript' })
response.end('one')
} else if (request.url === '/api/pluginInventory/list') {
response.writeHead(200, { 'content-type': 'application/json' })
response.end(JSON.stringify({
type: 'server-response',
rpcId: 'dsh-platform-readiness',
result: { ok: true, value: { entries: [{ moduleName: 'ready', enabled: true, fiberPhase: 'active' }] } },
}))
} else {
response.writeHead(404)
response.end()
}
})
try {
await verifyDshWebReady({
port: context.port,
managedReady: async () => ({ ready: true, readyUrl: `http://127.0.0.1:${String(context.port)}/?token=fixture-token` }),
})
assert.equal(expectedCookie, 'dsh-auth-fixture=session')
assert.equal(context.requests.get('/?token=fixture-token'), 1)
} finally {
await new Promise(resolve => context.server.close(resolve))
}
})
test('DSH web readiness rejects enabled Plugins that are not active', async () => {
const manifest = JSON.stringify({ rev: 'one', entries: [] })
const routes = new Map([
['/', { status: 200, type: 'text/html', body: `<script>window.__DSH_BOOT__ = ${manifest}</script>` }],
['POST /api/pluginInventory/list', { status: 200, type: 'application/json', body: JSON.stringify({
type: 'server-response',
rpcId: 'dsh-platform-readiness',
result: { ok: true, value: { entries: [
{ moduleName: 'active', enabled: true, fiberPhase: 'active' },
{ moduleName: 'disabled', enabled: false, fiberPhase: null },
{ moduleName: 'broken', enabled: true, fiberPhase: 'failed' },
] } },
}) }],
])
const context = await fixture(routes)
try {
await assert.rejects(
verifyDshWebReady({ port: context.port, managedReady: async () => {} }),
/DSH Plugins are not active: broken \(failed\)/,
)
} finally {
await new Promise(resolve => context.server.close(resolve))
}
})
test('DSH web readiness rejects Plugin inventory RPC failures', async () => {
const manifest = JSON.stringify({ rev: 'one', entries: [] })
const routes = new Map([
['/', { status: 200, type: 'text/html', body: `<script>window.__DSH_BOOT__ = ${manifest}</script>` }],
['POST /api/pluginInventory/list', { status: 200, type: 'application/json', body: JSON.stringify({
type: 'server-response',
rpcId: 'dsh-platform-readiness',
result: { ok: false, error: { code: 'internal', message: 'loader unavailable', details: {} } },
}) }],
])
const context = await fixture(routes)
try {
await assert.rejects(
verifyDshWebReady({ port: context.port, managedReady: async () => {} }),
/DSH Plugin inventory is unavailable: loader unavailable/,
)
} finally {
await new Promise(resolve => context.server.close(resolve))
}
})
test('DSH web readiness rejects a lifecycle generation change during verification', async () => {
const manifest = JSON.stringify({ rev: 'one', entries: [] })
const routes = new Map([
['/', { status: 200, type: 'text/html', body: `<script>window.__DSH_BOOT__ = ${manifest}</script>` }],
['POST /api/pluginInventory/list', { status: 200, type: 'application/json', body: JSON.stringify({
type: 'server-response',
rpcId: 'dsh-platform-readiness',
result: { ok: true, value: { entries: [] } },
}) }],
])
const context = await fixture(routes)
let observation = 0
try {
await assert.rejects(verifyDshWebReady({
port: context.port,
managedReady: async () => ({ generation: ++observation === 1 ? 'one' : 'two', ready: true, readyUrl: null }),
}), /lifecycle changed during Web readiness verification/)
assert.equal(context.requests.get('/'), 1)
assert.equal(context.requests.get('/api/pluginInventory/list'), 1)
} finally {
await new Promise(resolve => context.server.close(resolve))
}
})
test('DSH web readiness rejects a boot manifest mutation between observations', async () => {
const manifest = rev => JSON.stringify({ rev, entries: [] })
const routes = new Map([
['/', observation => ({
status: 200,
type: 'text/html',
body: `<script>window.__DSH_BOOT__ = ${manifest(observation === 1 ? 'one' : 'two')}</script>`,
})],
['POST /api/pluginInventory/list', { status: 200, type: 'application/json', body: JSON.stringify({
type: 'server-response',
rpcId: 'dsh-platform-readiness',
result: { ok: true, value: { entries: [] } },
}) }],
])
const context = await fixture(routes)
try {
await assert.rejects(
verifyDshWebReady({ port: context.port, managedReady: async () => ({ generation: 'one' }) }),
/boot manifest changed during Web readiness verification/,
)
} finally {
await new Promise(resolve => context.server.close(resolve))
}
})
test('DSH web readiness rejects a Plugin inventory mutation between observations', async () => {
const manifest = JSON.stringify({ rev: 'one', entries: [] })
const inventory = moduleName => JSON.stringify({
type: 'server-response',
rpcId: 'dsh-platform-readiness',
result: { ok: true, value: { entries: [{ moduleName, enabled: true, fiberPhase: 'active' }] } },
})
const routes = new Map([
['/', { status: 200, type: 'text/html', body: `<script>window.__DSH_BOOT__ = ${manifest}</script>` }],
['POST /api/pluginInventory/list', observation => ({
status: 200,
type: 'application/json',
body: inventory(observation === 1 ? 'one' : 'two'),
})],
])
const context = await fixture(routes)
try {
await assert.rejects(
verifyDshWebReady({ port: context.port, managedReady: async () => ({ generation: 'one' }) }),
/Plugin inventory changed during Web readiness verification/,
)
} finally {
await new Promise(resolve => context.server.close(resolve))
}
})
test('executes readiness checks when launched through the Bootstrap view symlink', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-web-ready-view-'))
const executable = join(root, 'dsh-web-ready.mjs')
await symlink(new URL('../../control-plane/hooks/dsh-web-ready.mjs', import.meta.url), executable)
const result = await new Promise((resolve, reject) => {
const child = spawn(process.execPath, [executable], {
env: { ...process.env, DSH_PLATFORM_RUN: join(root, 'missing-run') },
stdio: ['ignore', 'pipe', 'pipe'],
})
const stderr = []
child.stderr.on('data', chunk => stderr.push(chunk))
child.once('error', reject)
child.once('exit', code => resolve({ code, stderr: Buffer.concat(stderr).toString('utf8') }))
})
assert.notEqual(result.code, 0)
assert.match(result.stderr, /dsh-lifecycle\.sock/)
})
@@ -0,0 +1,42 @@
import assert from 'node:assert/strict'
import { chmod, mkdtemp, readFile, readdir, stat, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { AtomicFileEditor } from '../../control-plane/modules/file-manager/editor.mjs'
import { FileInventory } from '../../control-plane/modules/file-manager/index.mjs'
test('atomically replaces text with optimistic revision and preserves mode', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-editor-'))
const path = join(root, 'file.txt')
await writeFile(path, 'before')
await chmod(path, 0o640)
const inventory = new FileInventory()
const before = await inventory.content(path)
const saved = await new AtomicFileEditor().write(path, 'after\n', before.revision)
assert.equal(await readFile(path, 'utf8'), 'after\n')
assert.equal((await stat(path)).mode & 0o777, 0o640)
assert.notEqual(saved.revision, before.revision)
await assert.rejects(new AtomicFileEditor().write(path, 'stale', before.revision), error => error.statusCode === 409)
assert.equal(await readFile(path, 'utf8'), 'after\n')
assert.equal((await readdir(root)).some(name => name.startsWith('.dsh-edit-')), false)
})
test('creates a new text file without overwriting a concurrent target', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-create-'))
const path = join(root, 'new.txt')
const editor = new AtomicFileEditor()
const created = await editor.write(path, 'new', null, { create: true })
assert.equal(created.size, 3)
await assert.rejects(editor.write(path, 'replace', null, { create: true }), error => error.statusCode === 409)
assert.equal(await readFile(path, 'utf8'), 'new')
})
test('rejects binary, oversized, missing, and non-file edits', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-editor-invalid-'))
const editor = new AtomicFileEditor()
await assert.rejects(editor.write(join(root, 'nul'), 'a\0b', null, { create: true }), error => error.statusCode === 415)
await assert.rejects(editor.write(join(root, 'large'), 'x'.repeat(2 * 1024 * 1024 + 1), null, { create: true }), error => error.statusCode === 413)
await assert.rejects(editor.write(join(root, 'missing'), 'x', 'sha256:missing'), error => error.statusCode === 404)
await assert.rejects(editor.write(root, 'x', 'sha256:missing'), error => error.statusCode === 415)
})
@@ -0,0 +1,146 @@
import assert from 'node:assert/strict'
import { mkdtemp, mkdir, symlink, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import {
createManagedPathMatcher, fileManagerLocations, FileInventory, FileManagerError,
FileRevisionConflictError, FileSearchManager, FileSizeManager, isManagedPath, normalizeAbsolutePath,
} from '../../control-plane/modules/file-manager/index.mjs'
test('normalizes absolute paths and rejects unsafe path representations', () => {
assert.equal(normalizeAbsolutePath('/workspace/a/../b'), '/workspace/b')
assert.throws(() => normalizeAbsolutePath('workspace'), /absolute/)
assert.throws(() => normalizeAbsolutePath('/workspace/a\nb'), /control/)
assert.throws(() => normalizeAbsolutePath(`/${'a'.repeat(4097)}`), /too long/)
})
test('identifies only exact platform-managed roots and descendants', () => {
assert.equal(isManagedPath('/data/platform/state'), true)
assert.equal(isManagedPath('/data/platform/state/updater/status.json'), true)
assert.equal(isManagedPath('/data/platform/stateful'), false)
assert.equal(isManagedPath('/data/dsh'), false)
const custom = createManagedPathMatcher('/srv/platform')
assert.equal(custom('/srv/platform/store/objects'), true)
assert.equal(custom('/data/platform/store/objects'), false)
})
test('derives the default directory and unique shortcuts from existing environment paths', () => {
assert.deepEqual(fileManagerLocations({
defaultWorkspace: '/work/current', dshHome: '/srv/dsh', platformData: '/srv/platform',
}), {
defaultPath: '/work/current', shortcuts: ['/work/current', '/srv/dsh', '/srv/platform', '/'],
})
assert.deepEqual(fileManagerLocations({ defaultWorkspace: '/data/dsh' }).shortcuts, ['/data/dsh', '/data/platform', '/'])
assert.throws(() => fileManagerLocations({ defaultWorkspace: 'relative' }), /absolute/)
})
test('lists directories with stable metadata, directory-first sorting, and revision cursors', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-list-'))
await mkdir(join(root, 'folder'))
await writeFile(join(root, 'z.txt'), 'z')
await writeFile(join(root, '.hidden'), 'hidden')
await symlink('missing', join(root, 'dangling'))
const inventory = new FileInventory()
const first = await inventory.list(root, { limit: 2 })
assert.equal(first.entries[0].name, 'folder')
assert.equal(first.entries.some(entry => entry.name === '.hidden'), true)
assert.equal(first.total, 4)
assert.equal(typeof first.nextCursor, 'string')
const second = await inventory.list(root, { limit: 2, cursor: first.nextCursor })
assert.equal(second.entries.length, 2)
const dangling = [...first.entries, ...second.entries].find(entry => entry.name === 'dangling')
assert.deepEqual([dangling.type, dangling.targetExists], ['symlink', false])
await writeFile(join(root, 'new.txt'), 'new')
await assert.rejects(inventory.list(root, { cursor: first.nextCursor }), FileRevisionConflictError)
})
test('name pagination resolves detailed metadata only for the requested page', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-page-'))
for (let index = 0; index < 20; index += 1) await writeFile(join(root, `${String(index).padStart(2, '0')}.txt`), 'x')
let resolutions = 0
const inventory = new FileInventory({
identity: { resolve: async () => { resolutions += 1; return { user: 'node', group: 'node' } } },
})
const page = await inventory.list(root, { limit: 5 })
assert.equal(page.entries.length, 5)
assert.equal(page.total, 20)
assert.equal(resolutions, 5)
const thirdPage = await inventory.list(root, { limit: 5, offset: 10 })
assert.deepEqual(thirdPage.entries.map(entry => entry.name), ['10.txt', '11.txt', '12.txt', '13.txt', '14.txt'])
assert.equal(resolutions, 10)
})
test('reads only bounded UTF-8 regular files and detects binary content', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-content-'))
const text = join(root, '中文.txt')
await writeFile(text, 'hello\r\n世界\r\n')
const inventory = new FileInventory()
const result = await inventory.content(text)
assert.equal(result.content, 'hello\r\n世界\r\n')
assert.equal(result.newline, 'crlf')
assert.match(result.revision, /^sha256:[a-f0-9]{64}$/)
await writeFile(join(root, 'binary'), Buffer.from([1, 0, 2]))
await assert.rejects(inventory.content(join(root, 'binary')), error => error.statusCode === 415)
await assert.rejects(inventory.content(root), error => error.statusCode === 415)
})
test('reports container user and group and calculates directory size without following links', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-size-'))
await mkdir(join(root, 'nested'))
await writeFile(join(root, 'one.txt'), '1234')
await writeFile(join(root, 'nested', 'two.txt'), '567890')
await symlink('one.txt', join(root, 'link'))
const identity = { resolve: async () => ({ user: 'node', group: 'node' }) }
const inventory = new FileInventory({ identity })
const listing = await inventory.list(root)
assert.equal(listing.entries.every(entry => entry.user === 'node' && entry.group === 'node'), true)
const size = new FileSizeManager({ inventory })
const directory = await inventory.stat(root)
const task = size.start({ path: root, revision: directory.revision })
await size.tasks.get(task.taskId).completion
const result = size.get(task.taskId)
assert.equal(result.status, 'success')
assert.equal(result.bytes, 10)
assert.equal(result.entries, 4)
const stale = size.start({ path: root, revision: listing.revision })
await size.tasks.get(stale.taskId).completion
assert.equal(size.get(stale.taskId).status, 'failed')
})
test('bounded search does not follow symlinked directories and can be cancelled', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-search-'))
await mkdir(join(root, 'nested'))
await writeFile(join(root, 'nested', 'Needle.txt'), 'found')
await symlink(root, join(root, 'nested', 'loop'))
const states = []
const manager = new FileSearchManager({ onState: state => states.push(state) })
const task = manager.start({ path: root, query: 'needle' })
await manager.tasks.get(task.taskId).completion
const result = manager.get(task.taskId)
assert.equal(result.status, 'success')
assert.deepEqual(result.results.map(entry => entry.name), ['Needle.txt'])
assert.equal(result.scanned, 3)
assert.equal(states.at(-1).status, 'success')
const cancelled = manager.start({ path: root, query: 'x' })
manager.cancel(cancelled.taskId)
await manager.tasks.get(cancelled.taskId).completion
assert.equal(manager.get(cancelled.taskId).status, 'cancelled')
})
test('rejects invalid list, search, and task parameters', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-invalid-'))
const inventory = new FileInventory()
await assert.rejects(inventory.list(root, { limit: 1001 }), FileManagerError)
await assert.rejects(inventory.list(root, { sort: 'owner' }), /sort/)
await assert.rejects(inventory.list(root, { offset: -1 }), /offset/)
await assert.rejects(inventory.list(root, { offset: 1.5 }), /offset/)
await writeFile(join(root, 'one'), '1')
await writeFile(join(root, 'two'), '2')
const first = await inventory.list(root, { limit: 1 })
await assert.rejects(inventory.list(root, { cursor: first.nextCursor, offset: 0 }), /cannot be combined/)
const manager = new FileSearchManager()
assert.throws(() => manager.start({ path: root, query: '' }), /query/)
assert.throws(() => manager.get('missing'), error => error.statusCode === 404)
})
@@ -0,0 +1,450 @@
import assert from 'node:assert/strict'
import { chmod, mkdir, mkdtemp, readFile, readdir, stat, symlink, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { FileInventory } from '../../control-plane/modules/file-manager/index.mjs'
import { FileTaskManager, fileTaskInternals } from '../../control-plane/modules/file-manager/tasks.mjs'
async function setup(prefix = 'dsh-file-tasks-') {
const root = await mkdtemp(join(tmpdir(), prefix))
const files = join(root, 'files')
const tasks = join(root, 'tasks')
await mkdir(files)
const manager = new FileTaskManager({ root: tasks })
await manager.initialize()
return { root, files, tasks, manager, inventory: new FileInventory() }
}
async function finish(manager, task) {
await manager.tasks.get(task.taskId).completion
return manager.get(task.taskId)
}
test('runs mkdir, touch, rename, copy, move, and permanent delete tasks', async () => {
const { files, manager, inventory } = await setup()
assert.equal((await finish(manager, manager.start({ operation: 'mkdir', destination: join(files, 'dir') }))).status, 'success')
assert.equal((await stat(join(files, 'dir'))).isDirectory(), true)
assert.equal((await finish(manager, manager.start({ operation: 'touch', destination: join(files, 'empty') }))).status, 'success')
const empty = await inventory.stat(join(files, 'empty'))
const renamed = await finish(manager, manager.start({
operation: 'rename', sources: [{ path: empty.path, revision: empty.revision }], destination: join(files, 'renamed'),
}))
assert.equal(renamed.status, 'success')
await writeFile(join(files, 'dir', 'nested.txt'), 'nested')
await symlink('nested.txt', join(files, 'dir', 'link'))
await mkdir(join(files, 'copies'))
const source = await inventory.stat(join(files, 'dir'))
const copied = await finish(manager, manager.start({
operation: 'copy', sources: [{ path: source.path, revision: source.revision }], destination: join(files, 'copies'), conflict: 'reject',
}))
assert.equal(copied.status, 'success')
assert.equal(await readFile(join(files, 'copies', 'dir', 'nested.txt'), 'utf8'), 'nested')
const link = await inventory.stat(join(files, 'copies', 'dir', 'link'))
assert.equal(link.type, 'symlink')
const movedSource = await inventory.stat(join(files, 'renamed'))
const moved = await finish(manager, manager.start({
operation: 'move', sources: [{ path: movedSource.path, revision: movedSource.revision }], destination: join(files, 'copies'),
}))
assert.equal(moved.status, 'success')
const deleteSource = await inventory.stat(join(files, 'copies', 'dir'))
const removed = await finish(manager, manager.start({ operation: 'delete', sources: [{ path: deleteSource.path, revision: deleteSource.revision }] }))
assert.equal(removed.status, 'success')
assert.equal(removed.processedBytes, removed.totalBytes)
assert.equal(removed.processedEntries, removed.totalEntries)
assert.equal(await stat(deleteSource.path).then(() => true, error => error.code !== 'ENOENT'), false)
assert.equal((await readdir(join(files, 'copies'))).some(name => name.startsWith('.dsh-')), false)
})
test('releases the mutation lease before publishing a terminal task state', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-terminal-lease-'))
const files = join(root, 'files')
const tasks = join(root, 'tasks')
const destination = join(files, 'destination')
await mkdir(destination, { recursive: true })
await writeFile(join(files, 'item.txt'), 'source')
await writeFile(join(destination, 'item.txt'), 'existing')
let releaseReport
const blockedReport = new Promise(resolve => { releaseReport = resolve })
let observeFailure
const failurePublished = new Promise(resolve => { observeFailure = resolve })
const inventory = new FileInventory()
const manager = new FileTaskManager({
root: tasks,
onState: task => { if (task.status === 'failed') observeFailure() },
report: message => message === 'file-task.copy.failed' ? blockedReport : undefined,
})
await manager.initialize()
const source = await inventory.stat(join(files, 'item.txt'))
const rejected = manager.start({
operation: 'copy', sources: [{ path: source.path, revision: source.revision }], destination, conflict: 'reject',
})
await failurePublished
assert.equal(manager.activeMutation, undefined)
assert.equal(manager.queue.some(task => task.taskId === rejected.taskId), false)
manager.activeMutation = manager.tasks.get(rejected.taskId)
const renamed = manager.start({
operation: 'copy', sources: [{ path: source.path, revision: source.revision }], destination, conflict: 'rename',
})
releaseReport()
assert.equal((await finish(manager, renamed)).status, 'success')
assert.equal((await finish(manager, rejected)).status, 'failed')
assert.equal((await readdir(destination)).length, 2)
})
test('changes file ownership and permissions recursively without following symbolic links', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-attributes-'))
const files = join(root, 'files')
const tasks = join(root, 'tasks')
const tree = join(files, 'tree')
const nested = join(tree, 'nested')
const target = join(root, 'outside.txt')
await mkdir(nested, { recursive: true })
await writeFile(join(nested, 'inside.txt'), 'inside')
await writeFile(target, 'outside')
await chmod(target, 0o640)
await symlink(target, join(tree, 'outside-link'))
const identity = {
resolve: async () => ({ user: 'current', group: 'current' }),
userId: async value => value === 'current' ? process.getuid() : Promise.reject(new Error('unknown user')),
groupId: async value => value === 'current' ? process.getgid() : Promise.reject(new Error('unknown group')),
}
const inventory = new FileInventory({ identity })
const reports = []
const manager = new FileTaskManager({ root: tasks, inventory, identity, report: (message, fields) => reports.push({ message, fields }) })
await manager.initialize()
const source = await inventory.stat(tree)
const started = manager.start({
operation: 'attributes',
sources: [{ path: tree, revision: source.revision }],
attributes: { user: 'current', group: 'current', mode: '0750', recursive: true },
})
const result = await manager.completion(started.taskId)
assert.equal(result.status, 'success')
assert.equal(result.errorCode, null)
assert.equal(result.processedEntries, 4)
assert.equal((await stat(tree)).mode & 0o7777, 0o750)
assert.equal((await stat(nested)).mode & 0o7777, 0o750)
assert.equal((await stat(join(nested, 'inside.txt'))).mode & 0o7777, 0o750)
assert.equal((await stat(target)).mode & 0o7777, 0o640)
assert.equal(reports.at(-1).message, 'file-task.attributes.completed')
})
test('rejects filesystems that silently ignore Unix ownership or mode changes', () => {
const attributes = { uid: 0, gid: 0, mode: 0o600 }
assert.doesNotThrow(() => fileTaskInternals.assertAttributesApplied('/supported', { uid: 0, gid: 0, mode: 0o100600 }, attributes, false))
assert.throws(
() => fileTaskInternals.assertAttributesApplied('/unsupported', { uid: 1000, gid: 1000, mode: 0o100755 }, attributes, false),
error => error.code === 'FILE_ATTRIBUTES_UNSUPPORTED' && error.statusCode === 403,
)
})
test('reports attribute failures instead of a false completion', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-attributes-failed-log-'))
const path = join(root, 'item')
await writeFile(path, 'value')
const identity = {
resolve: async () => ({ user: 'current', group: 'current' }),
userId: async () => { throw Object.assign(new Error('ownership is unsupported'), { code: 'FILE_ATTRIBUTES_UNSUPPORTED' }) },
groupId: async () => process.getgid(),
}
const inventory = new FileInventory({ identity })
const reports = []
const manager = new FileTaskManager({ root: join(root, 'tasks'), inventory, identity, report: message => reports.push(message) })
await manager.initialize()
const source = await inventory.stat(path)
const started = manager.start({
operation: 'attributes', sources: [{ path, revision: source.revision }],
attributes: { user: 'current', group: 'current', mode: '0600', recursive: false },
})
const result = await manager.completion(started.taskId)
assert.equal(result.status, 'failed')
assert.equal(result.errorCode, 'FILE_ATTRIBUTES_UNSUPPORTED')
assert.deepEqual(reports, ['file-task.attributes.failed'])
})
test('validates attribute requests and resumes an interrupted idempotent change', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-attributes-recovery-'))
const files = join(root, 'files')
const tasks = join(root, 'tasks')
await mkdir(files)
await mkdir(tasks)
const path = join(files, 'item')
await writeFile(path, 'value')
const identity = {
resolve: async () => ({ user: 'current', group: 'current' }),
userId: async () => process.getuid(),
groupId: async () => process.getgid(),
}
const inventory = new FileInventory({ identity })
const source = await inventory.stat(path)
const manager = new FileTaskManager({ root: tasks, inventory, identity })
assert.throws(() => manager.start({
operation: 'attributes', sources: [{ path, revision: source.revision }],
attributes: { user: 'current', group: 'current', mode: '888', recursive: false },
}), /attributes/)
const task = {
schema: 1, taskId: '55555555-5555-4555-8555-555555555555', operation: 'attributes', status: 'running', phase: 'mutating',
sources: [{ path, revision: source.revision }], destination: null, destinationRevision: null, conflict: 'reject', managed: false,
attributes: { user: 'current', group: 'current', mode: '0600', recursive: false },
processedBytes: 0, totalBytes: 5, processedEntries: 0, totalEntries: 1,
published: [], staging: [], hidden: [], currentPath: path, currentSource: null, currentDestination: null,
error: null, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), cancelRequested: false,
}
await writeFile(join(tasks, `${task.taskId}.json`), JSON.stringify(task))
const recovered = new FileTaskManager({ root: tasks, inventory, identity })
await recovered.initialize()
assert.equal(recovered.get(task.taskId).status, 'success')
assert.equal((await stat(path)).mode & 0o7777, 0o600)
})
test('rejects stale revisions, protected roots, and recursive copies while queueing concurrent mutations', async () => {
const { files, manager, inventory } = await setup()
const sourcePath = join(files, 'source')
await writeFile(sourcePath, 'one')
const source = await inventory.stat(sourcePath)
await writeFile(sourcePath, 'two')
const stale = manager.start({ operation: 'delete', sources: [{ path: sourcePath, revision: source.revision }] })
assert.equal((await finish(manager, stale)).status, 'failed')
assert.throws(() => manager.start({ operation: 'delete', sources: [{ path: '/', revision: 'x' }] }), error => error.statusCode === 403)
await mkdir(join(files, 'tree'))
const tree = await inventory.stat(join(files, 'tree'))
const recursive = manager.start({ operation: 'copy', sources: [{ path: tree.path, revision: tree.revision }], destination: tree.path })
assert.equal((await finish(manager, recursive)).status, 'failed')
const first = manager.start({ operation: 'touch', destination: join(files, 'first') })
const second = manager.start({ operation: 'touch', destination: join(files, 'second') })
assert.equal(second.status, 'queued')
assert.equal(second.queuePosition, 1)
await finish(manager, first)
assert.equal((await finish(manager, second)).status, 'success')
})
test('runs queued mutations in FIFO order and cancels queued work without touching files', async () => {
const { files, manager } = await setup('dsh-file-queue-')
const first = manager.start({ operation: 'touch', destination: join(files, 'first') })
const second = manager.start({ operation: 'touch', destination: join(files, 'second') })
const third = manager.start({ operation: 'touch', destination: join(files, 'third') })
assert.deepEqual([second.queuePosition, third.queuePosition], [1, 2])
assert.equal(manager.cancel(second.taskId).status, 'cancelled')
assert.equal(manager.get(third.taskId).queuePosition, 1)
assert.equal((await finish(manager, first)).status, 'success')
assert.equal((await finish(manager, second)).status, 'cancelled')
assert.equal((await finish(manager, third)).status, 'success')
assert.equal(await stat(join(files, 'second')).then(() => true, error => error.code !== 'ENOENT'), false)
})
test('persists queued work before the active task releases its lease', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-queue-persisted-'))
const files = join(root, 'files')
const tasks = join(root, 'tasks')
await mkdir(files)
let releaseReport
const blocked = new Promise(resolve => { releaseReport = resolve })
const manager = new FileTaskManager({ root: tasks, report: message => message === 'file-task.touch.completed' ? blocked : undefined })
await manager.initialize()
const first = manager.start({ operation: 'touch', destination: join(files, 'first') })
const second = manager.start({ operation: 'touch', destination: join(files, 'second') })
await manager.tasks.get(second.taskId).persisted
const journal = JSON.parse(await readFile(join(tasks, `${second.taskId}.json`), 'utf8'))
assert.equal(journal.status, 'queued')
assert.equal(journal.queuePosition, 1)
releaseReport()
assert.equal((await finish(manager, first)).status, 'success')
assert.equal((await finish(manager, second)).status, 'success')
})
test('reports real transfer progress and cancels a queued transfer', async () => {
const { files, manager } = await setup('dsh-file-transfer-progress-')
const transfer = manager.startTransfer({ operation: 'upload', path: join(files, 'upload.bin'), totalBytes: 10 }, async ({ progress }) => {
progress({ processedBytes: 4, totalBytes: 10 })
progress({ processedBytes: 10, totalBytes: 10 })
return { size: 10 }
})
assert.deepEqual(await transfer.result, { size: 10 })
await manager.completion(transfer.task.taskId)
assert.deepEqual(
[manager.get(transfer.task.taskId).status, manager.get(transfer.task.taskId).processedBytes],
['success', 10],
)
let release
const blocked = new Promise(resolve => { release = resolve })
const active = manager.startTransfer({ operation: 'download', path: join(files, 'active') }, () => blocked)
const queued = manager.startTransfer({ operation: 'download', path: join(files, 'queued') }, async () => undefined)
const rejection = assert.rejects(queued.result, error => error.code === 'FILE_TASK_CANCELLED')
assert.equal(manager.cancel(queued.task.taskId).status, 'cancelled')
await rejection
release()
await active.result
})
test('marks persisted transfers interrupted because HTTP streams cannot resume', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-transfer-recovery-'))
const tasks = join(root, 'tasks')
await mkdir(tasks)
for (const [index, status] of ['running', 'queued'].entries()) {
const taskId = `${String(index + 8).repeat(8)}-${String(index + 8).repeat(4)}-4${String(index + 8).repeat(3)}-8${String(index + 8).repeat(3)}-${String(index + 8).repeat(12)}`
await writeFile(join(tasks, `${taskId}.json`), JSON.stringify({
schema: 1, taskId, operation: index === 0 ? 'upload' : 'download', status,
phase: status === 'queued' ? 'queued' : 'transferring', path: join(root, 'value'),
managed: false, staging: [], hidden: [], published: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
}))
}
const recovered = new FileTaskManager({ root: tasks })
await recovered.initialize()
assert.equal(recovered.list().filter(task => ['upload', 'download'].includes(task.operation)).every(task => task.status === 'interrupted'), true)
})
test('recovers committed deletes and marks uncommitted tasks interrupted', async () => {
const { files, tasks } = await setup()
const hidden = join(files, '.dsh-delete-task.tmp')
await writeFile(hidden, 'remove')
const runningDelete = {
schema: 1, taskId: '11111111-1111-4111-8111-111111111111', operation: 'delete', status: 'running', phase: 'source-hidden',
sources: [], destination: null, managed: false, hidden: [hidden], staging: [], published: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
}
const staging = join(files, '.dsh-file-task.tmp')
await writeFile(staging, 'partial')
const runningCopy = {
...runningDelete, taskId: '22222222-2222-4222-8222-222222222222', operation: 'copy', phase: 'mutating', hidden: [], staging: [staging],
}
await writeFile(join(tasks, `${runningDelete.taskId}.json`), JSON.stringify(runningDelete))
await writeFile(join(tasks, `${runningCopy.taskId}.json`), JSON.stringify(runningCopy))
const recovered = new FileTaskManager({ root: tasks })
await recovered.initialize()
assert.equal(recovered.get(runningDelete.taskId).status, 'success')
assert.equal(recovered.get(runningCopy.taskId).status, 'interrupted')
assert.equal(await stat(hidden).then(() => true, error => error.code !== 'ENOENT'), false)
assert.equal(await stat(staging).then(() => true, error => error.code !== 'ENOENT'), false)
})
test('move recovery cleans only the source bound to the committed destination', async () => {
const { files, tasks } = await setup('dsh-file-move-recovery-')
const committedSource = join(files, 'committed-source')
const untouchedSource = join(files, 'untouched-source')
const destination = join(files, 'destination')
await writeFile(committedSource, 'old duplicate')
await writeFile(untouchedSource, 'must remain')
await writeFile(destination, 'committed')
const task = {
schema: 1, taskId: '33333333-3333-4333-8333-333333333333', operation: 'move', status: 'running', phase: 'destination-committed',
sources: [{ path: committedSource, revision: 'old' }, { path: untouchedSource, revision: 'untouched' }],
destination: files, currentSource: committedSource, currentDestination: destination,
managed: false, hidden: [], staging: [], published: [destination], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
}
await writeFile(join(tasks, `${task.taskId}.json`), JSON.stringify(task))
const recovered = new FileTaskManager({ root: tasks })
await recovered.initialize()
assert.equal(recovered.get(task.taskId).status, 'success')
assert.equal(await stat(committedSource).then(() => true, error => error.code !== 'ENOENT'), false)
assert.equal(await readFile(untouchedSource, 'utf8'), 'must remain')
})
test('move recovery recognizes an atomic rename completed before its journal commit', async () => {
const { files, tasks } = await setup('dsh-file-rename-recovery-')
const source = join(files, 'source')
const destination = join(files, 'destination')
await writeFile(destination, 'moved')
const task = {
schema: 1, taskId: '44444444-4444-4444-8444-444444444444', operation: 'move', status: 'running', phase: 'move-prepared',
sources: [{ path: source, revision: 'old' }], destination: files,
currentSource: source, currentDestination: destination, managed: false, hidden: [], staging: [], published: [],
createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
}
await writeFile(join(tasks, `${task.taskId}.json`), JSON.stringify(task))
const recovered = new FileTaskManager({ root: tasks })
await recovered.initialize()
assert.equal(recovered.get(task.taskId).status, 'success')
assert.deepEqual(recovered.get(task.taskId).published, [destination])
})
test('managed mutations expose a lease and reject active platform work', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-managed-tasks-'))
const manager = new FileTaskManager({ root: join(root, 'tasks'), platformBusy: () => true })
await manager.initialize()
assert.equal(manager.wouldManage({ operation: 'touch', destination: '/data/platform/state/test' }), true)
assert.throws(() => manager.start({ operation: 'touch', destination: '/data/platform/state/test' }), error => error.statusCode === 409)
const custom = new FileTaskManager({ root: join(root, 'custom-tasks'), protectedRoots: ['/custom/workspace'] })
assert.throws(() => custom.start({
operation: 'delete', sources: [{ path: '/custom/workspace', revision: 'sha256:unused' }],
}), error => error.statusCode === 403)
})
test('creates and extracts zip, 7z, and tar.gz archives through file tasks', async () => {
for (const [format, extension] of [['zip', 'zip'], ['7z', '7z'], ['tar.gz', 'tar.gz']]) {
const { files, manager, inventory } = await setup(`dsh-file-archive-${format.replace('.', '-')}-`)
const sourcePath = join(files, 'source')
await mkdir(sourcePath)
await writeFile(join(sourcePath, 'nested.txt'), `content-${format}`)
const source = await inventory.stat(sourcePath)
const archivePath = join(files, `bundle.${extension}`)
const archived = await finish(manager, manager.start({
operation: 'archive', archiveFormat: format,
sources: [{ path: source.path, revision: source.revision }], destination: archivePath,
}))
assert.equal(archived.status, 'success')
assert.equal((await stat(archivePath)).isFile(), true)
const extractRoot = join(files, 'extracted')
await mkdir(extractRoot)
const archive = await inventory.stat(archivePath)
const extracted = await finish(manager, manager.start({
operation: 'extract', archiveFormat: format,
sources: [{ path: archive.path, revision: archive.revision }], destination: extractRoot,
}))
assert.equal(extracted.status, 'success')
assert.equal(await readFile(join(extractRoot, 'source', 'nested.txt'), 'utf8'), `content-${format}`)
}
})
test('rejects invalid archive formats and escaping extracted links', async () => {
const { files, manager, inventory } = await setup('dsh-file-archive-unsafe-')
const sourcePath = join(files, 'source')
await mkdir(sourcePath)
await symlink('../../outside', join(sourcePath, 'escape'))
const source = await inventory.stat(sourcePath)
assert.throws(() => manager.start({
operation: 'archive', archiveFormat: 'rar', sources: [{ path: source.path, revision: source.revision }],
destination: join(files, 'bad.rar'),
}), /format/)
const archivePath = join(files, 'unsafe.tar.gz')
assert.equal((await finish(manager, manager.start({
operation: 'archive', archiveFormat: 'tar.gz', sources: [{ path: source.path, revision: source.revision }], destination: archivePath,
}))).status, 'success')
const archive = await inventory.stat(archivePath)
const destination = join(files, 'destination')
await mkdir(destination)
const extracted = await finish(manager, manager.start({
operation: 'extract', archiveFormat: 'tar.gz', sources: [{ path: archive.path, revision: archive.revision }], destination,
}))
assert.equal(extracted.status, 'failed')
assert.equal(extracted.errorCode, 'ARCHIVE_UNSAFE')
})
test('recovers committed archives and resumes staged extraction publication', async () => {
const { files, tasks } = await setup('dsh-file-archive-recovery-')
const archivePath = join(files, 'published.zip')
await writeFile(archivePath, 'published')
const archiveTask = {
schema: 1, taskId: '66666666-6666-4666-8666-666666666666', operation: 'archive', status: 'running', phase: 'destination-committed',
sources: [], destination: archivePath, managed: false, hidden: [], staging: [], published: [archivePath],
createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
}
const destination = join(files, 'destination')
const staging = join(destination, '.dsh-extract-recovery.tmp')
await mkdir(staging, { recursive: true })
await writeFile(join(staging, 'remaining.txt'), 'remaining')
const extractTask = {
schema: 1, taskId: '77777777-7777-4777-8777-777777777777', operation: 'extract', status: 'running', phase: 'publishing',
sources: [], destination, conflict: 'reject', managed: false, hidden: [], staging: [staging], published: [],
processedBytes: 0, totalBytes: 9, processedEntries: 0, totalEntries: 1,
createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), cancelRequested: false,
}
await writeFile(join(tasks, `${archiveTask.taskId}.json`), JSON.stringify(archiveTask))
await writeFile(join(tasks, `${extractTask.taskId}.json`), JSON.stringify(extractTask))
const recovered = new FileTaskManager({ root: tasks })
await recovered.initialize()
assert.equal(recovered.get(archiveTask.taskId).status, 'success')
assert.equal(recovered.get(extractTask.taskId).status, 'success')
assert.equal(await readFile(join(destination, 'remaining.txt'), 'utf8'), 'remaining')
})
@@ -0,0 +1,92 @@
import assert from 'node:assert/strict'
import { mkdir, mkdtemp, readFile, stat, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { Readable } from 'node:stream'
import { spawnSync } from 'node:child_process'
import test from 'node:test'
import { FileTransferManager, fileTransferInternals } from '../../control-plane/modules/file-manager/transfers.mjs'
async function collect(stream) {
const chunks = []
for await (const chunk of stream) chunks.push(chunk)
return Buffer.concat(chunks)
}
test('streams complete, ranged, suffix, and empty downloads', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-download-'))
const path = join(root, '中文 name.txt')
await writeFile(path, '0123456789')
const transfers = new FileTransferManager()
const complete = await transfers.openDownload(path)
assert.equal((await collect(complete.stream)).toString(), '0123456789')
await complete.handle.close()
assert.equal(complete.headers['content-length'], '10')
assert.match(complete.headers['content-disposition'], /filename\*=UTF-8''/)
const ranged = await transfers.openDownload(path, { range: 'bytes=2-5', revision: complete.revision })
assert.equal(ranged.status, 206)
assert.equal((await collect(ranged.stream)).toString(), '2345')
await ranged.handle.close()
const suffix = await transfers.openDownload(path, { range: 'bytes=-3' })
assert.equal((await collect(suffix.stream)).toString(), '789')
await suffix.handle.close()
const emptyPath = join(root, 'empty')
await writeFile(emptyPath, '')
const empty = await transfers.openDownload(emptyPath)
assert.equal((await collect(empty.stream)).byteLength, 0)
await empty.handle.close()
await assert.rejects(transfers.openDownload(path, { range: 'bytes=20-30' }), error => error.statusCode === 416)
})
test('streams directories as temporary zip downloads', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-directory-download-'))
const directory = join(root, 'folder')
await mkdir(directory)
await writeFile(join(directory, 'entry.txt'), 'directory-content')
const transfers = new FileTransferManager({ stagingRoot: join(root, 'cache') })
const download = await transfers.openDownload(directory)
const archive = join(root, 'folder.zip')
await writeFile(archive, await collect(download.stream))
await download.handle.close()
await download.cleanup()
assert.match(download.headers['content-disposition'], /folder\.zip/)
const extracted = spawnSync('unzip', ['-p', archive, 'folder/entry.txt'], { encoding: 'utf8' })
assert.equal(extracted.status, 0, extracted.stderr)
assert.equal(extracted.stdout, 'directory-content')
})
test('uploads through sibling staging with reject, overwrite, and rename policies', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-upload-'))
const path = join(root, 'file.txt')
const transfers = new FileTransferManager()
const first = await transfers.upload(Readable.from(['first']), path, { contentLength: 5 })
assert.equal(first.path, path)
assert.equal(await readFile(path, 'utf8'), 'first')
await assert.rejects(transfers.upload(Readable.from(['no']), path), error => error.statusCode === 409)
const renamed = await transfers.upload(Readable.from(['second']), path, { conflict: 'rename' })
assert.equal(renamed.path, join(root, 'file (1).txt'))
assert.equal(await readFile(renamed.path, 'utf8'), 'second')
await mkdir(join(root, 'directory'))
const directoryConflict = await transfers.upload(Readable.from(['safe']), join(root, 'directory'), { conflict: 'rename' })
assert.equal(directoryConflict.path, join(root, 'directory (1)'))
const oldMode = (await stat(path)).mode & 0o777
await transfers.upload(Readable.from(['replacement']), path, { conflict: 'overwrite' })
assert.equal(await readFile(path, 'utf8'), 'replacement')
assert.equal((await stat(path)).mode & 0o777, oldMode)
})
test('cleans staging files after interrupted or mismatched uploads', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-file-upload-failure-'))
const path = join(root, 'file')
const transfers = new FileTransferManager()
await assert.rejects(transfers.upload(Readable.from(['short']), path, { contentLength: 100 }), /Content-Length/)
assert.equal(await readFile(path).then(() => true, error => error.code !== 'ENOENT'), false)
const entries = await import('node:fs/promises').then(fs => fs.readdir(root))
assert.deepEqual(entries, [])
})
test('sanitizes download names and parses only a single byte range', () => {
assert.match(fileTransferInternals.disposition('a"b.txt'), /filename="a_b.txt"/)
assert.deepEqual(fileTransferInternals.parseRange('bytes=0-0', 5), { start: 0, end: 0, partial: true })
assert.throws(() => fileTransferInternals.parseRange('bytes=0-1,3-4', 5), /invalid/)
})
@@ -0,0 +1,30 @@
import { mkdir, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
export async function writeDshEntrypointFixture(packageRoot) {
await mkdir(join(packageRoot, 'lib'), { recursive: true })
await writeFile(join(packageRoot, 'lib/bin.js'), `#!/usr/bin/env node
const invocation = parseDshArgs(process.argv.slice(2), readVersion());
switch (invocation.mode) {
case "profile": {
const { runProfile } = await import("./profile-boot-fixture.js");
}
}
`)
await writeFile(join(packageRoot, 'lib/profile-boot-fixture.js'), 'import { r as runProfile } from "./profile-boot-implementation.js";\nexport { runProfile };\n')
await writeFile(join(packageRoot, 'lib/profile-boot-implementation.js'), `async function runProfile(options) {
\tprocess.on("SIGTERM", () => {
\t\tinterrupt(0);
\t});
\tprocess.on("SIGINT", () => { interrupt(130); });
\tconst ctx = await boot(NAME, rootConfig, patches, (hostCtx) => {
\t\thostCtx.provide(DSH_LAUNCH_ENVIRONMENT_KEY, options.environment);
\t});
\treturn {
\t\tctx,
\t\tshutdown
\t};
}
export { runProfile as r };
`)
}
@@ -0,0 +1,102 @@
import { createHash, generateKeyPairSync, sign } from 'node:crypto'
export function keyPair() {
const pair = generateKeyPairSync('ed25519')
const der = pair.publicKey.export({ format: 'der', type: 'spki' })
return {
privateKey: pair.privateKey,
publicKey: der.toString('base64'),
keyId: createHash('sha256').update(der).digest('hex'),
}
}
export function registryKeyPair() {
const pair = generateKeyPairSync('ec', { namedCurve: 'prime256v1' })
const der = pair.publicKey.export({ format: 'der', type: 'spki' })
return {
privateKey: pair.privateKey,
publicKey: der.toString('base64'),
keyId: `SHA256:${createHash('sha256').update(der).digest('base64').replace(/=$/, '')}`,
}
}
const defaultRegistry = registryKeyPair()
export function officialDshPolicy(pair = defaultRegistry, expires = null) {
return {
registry: 'https://registry.npmjs.org/',
packageName: '@deepseek-ai/dsh',
keys: [{ algorithm: 'ECDSA-P256-SHA256', keyId: pair.keyId, publicKey: pair.publicKey, expires }],
}
}
export function registryCandidate(pair, version = '0.1.0-rc.8', content = Buffer.alloc(0)) {
const integrity = `sha512-${createHash('sha512').update(content).digest('base64')}`
const message = `@deepseek-ai/dsh@${version}:${integrity}`
return {
schema: 1,
name: '@deepseek-ai/dsh',
version,
dist: {
integrity,
tarball: `https://registry.npmjs.org/@deepseek-ai/dsh/-/dsh-${version}.tgz`,
signatures: [{ keyid: pair.keyId, sig: sign('sha256', Buffer.from(message), pair.privateKey).toString('base64') }],
},
}
}
export function releaseKey(pair) {
return { algorithm: 'Ed25519', keyId: pair.keyId, publicKey: pair.publicKey }
}
export function document(value) {
return Buffer.from(`${JSON.stringify(value)}\n`)
}
export function signature(bytes, pair) {
return {
schema: 1,
algorithm: 'Ed25519',
keyId: pair.keyId,
signature: sign(null, bytes, pair.privateKey).toString('base64'),
}
}
export function keyring(generation, current, next, revokedKeyIds = []) {
return {
schema: 1,
generation,
issuedAt: new Date(Date.UTC(2026, 7, 19, 0, 0, generation)).toISOString(),
current: releaseKey(current),
next: releaseKey(next),
revokedKeyIds: [...revokedKeyIds].sort(),
}
}
export function target(generation, sequence, policy = officialDshPolicy()) {
const emptyHash = '0'.repeat(64)
const artifacts = ['bootstrap-manifest', 'bootstrap-signature', 'environment-manifest', 'environment-signature'].map(id => ({
id,
mediaType: 'application/octet-stream',
sha256: emptyHash,
size: 0,
url: `https://example.com/${id}`,
}))
return {
schema: 1,
updateApi: 1,
keyringGeneration: generation,
targetSequence: sequence,
issuedAt: new Date(Date.UTC(2026, 7, 19, 1, 0, sequence)).toISOString(),
artifacts,
desired: {
bootstrap: { version: '1.0.0', manifestArtifactId: 'bootstrap-manifest', signatureArtifactId: 'bootstrap-signature' },
environment: { version: '1.0.0', manifestArtifactId: 'environment-manifest', signatureArtifactId: 'environment-signature' },
dsh: {
version: '0.1.0-rc.7',
integrity: `sha512-${Buffer.alloc(64).toString('base64')}`,
},
},
officialDshPolicy: policy,
}
}
@@ -0,0 +1,59 @@
import assert from 'node:assert/strict'
import { mkdtemp } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { UpdateJournal } from '../../control-plane/modules/updater/lib/journal.mjs'
const transaction = {
transactionId: 'transaction-one',
mode: 'experimental',
from: {
dsh: '0.1.0-rc.7', environment: 'env-1', runtime: 'runtime-a', dataSnapshot: null, receiptTokens: [],
},
to: { dsh: '0.1.0-rc.8', environment: 'env-1', runtime: 'runtime-b' },
}
test('persists a complete update transaction across process restarts', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-journal-'))
const path = join(root, 'state', 'update-transaction.json')
let tick = 0
const now = () => new Date(Date.UTC(2026, 7, 19, 0, 0, tick++))
const journal = new UpdateJournal(path, now)
await journal.begin(transaction)
await journal.transition('candidate-ready', { receiptTokens: ['receipt-one'] })
await journal.transition('suspended')
await journal.transition('snapshot-created', { snapshotId: 'snapshot-one' })
const resumed = await new UpdateJournal(path, now).read()
assert.equal(resumed.phase, 'snapshot-created')
assert.equal(resumed.from.runtime, 'runtime-a')
assert.equal(resumed.to.runtime, 'runtime-b')
assert.equal(resumed.snapshotId, 'snapshot-one')
assert.deepEqual(resumed.receiptTokens, ['receipt-one'])
})
test('rejects skipped phases and clears only terminal journals', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-journal-transition-'))
const journal = new UpdateJournal(join(root, 'state', 'update-transaction.json'))
await journal.begin(transaction)
await assert.rejects(journal.transition('switched'), /cannot transition/)
await assert.rejects(journal.clear(), /nonterminal/)
await journal.transition('failed', { error: 'candidate build failed' })
await journal.clear()
assert.equal(await journal.read(), undefined)
})
test('records an idempotent data-restoration phase before rollback completion', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-journal-restore-'))
const path = join(root, 'state', 'update-transaction.json')
const journal = new UpdateJournal(path)
await journal.begin(transaction)
await journal.transition('candidate-ready')
await journal.transition('suspended')
await journal.transition('snapshot-created', { snapshotId: 'snapshot-one' })
await journal.transition('switched')
await journal.transition('restoring-data')
assert.equal((await new UpdateJournal(path).read()).phase, 'restoring-data')
await journal.transition('rolled-back')
})
@@ -0,0 +1,50 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { parseKeyring, validateKeyringTransition, verifyRecoveryKeyring } from '../stage0/lib/keyring.mjs'
import { document, keyPair, keyring, signature } from './helpers.mjs'
test('accepts a Recovery-signed keyring with derived current and next IDs', () => {
const recovery = keyPair()
const value = keyring(1, keyPair(), keyPair())
const bytes = document(value)
assert.deepEqual(verifyRecoveryKeyring(bytes, signature(bytes, recovery), recovery.publicKey), value)
})
test('rejects a keyring signed by a non-Recovery key or modified after signing', () => {
const recovery = keyPair()
const other = keyPair()
const bytes = document(keyring(1, keyPair(), keyPair()))
assert.throws(() => verifyRecoveryKeyring(bytes, signature(bytes, other), recovery.publicKey), {
code: 'TRUST_UNKNOWN_KEY',
})
const signed = signature(bytes, recovery)
assert.throws(() => verifyRecoveryKeyring(Buffer.concat([bytes, Buffer.from(' ')]), signed, recovery.publicKey), {
code: 'TRUST_BAD_SIGNATURE',
})
})
test('rejects mismatched IDs, duplicate active keys, revoked active keys, and unknown algorithms', () => {
const current = keyPair()
const next = keyPair()
const cases = [
{ ...keyring(1, current, next), current: { ...keyring(1, current, next).current, keyId: next.keyId } },
{ ...keyring(1, current, next), next: { ...keyring(1, current, next).current } },
{ ...keyring(1, current, next), revokedKeyIds: [current.keyId] },
{ ...keyring(1, current, next), current: { ...keyring(1, current, next).current, algorithm: 'RSA' } },
]
for (const value of cases) assert.throws(() => parseKeyring(document(value)))
})
test('requires transitions to preserve revocations and revoke removed keys', () => {
const first = keyPair()
const second = keyPair()
const third = keyPair()
const fourth = keyPair()
const initial = parseKeyring(document(keyring(1, first, second)))
const rotated = parseKeyring(document(keyring(2, second, third, [first.keyId])))
assert.equal(validateKeyringTransition(initial, rotated), rotated)
assert.throws(() => validateKeyringTransition(initial, parseKeyring(document(keyring(2, second, third)))))
const next = parseKeyring(document(keyring(3, third, fourth, [first.keyId, second.keyId])))
assert.equal(validateKeyringTransition(rotated, next), next)
assert.throws(() => validateKeyringTransition(next, parseKeyring(document(keyring(4, first, fourth, [second.keyId, third.keyId])))))
})
@@ -0,0 +1,162 @@
import assert from 'node:assert/strict'
import { createHash } from 'node:crypto'
import { mkdtemp, readFile, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { TrustLedger } from '../stage0/lib/ledger.mjs'
import { document, officialDshPolicy, keyPair, keyring, registryCandidate, registryKeyPair, signature, target } from './helpers.mjs'
async function fixture() {
const recovery = keyPair()
return {
recovery,
ledger: new TrustLedger(await mkdtemp(join(tmpdir(), 'dsh-trust-ledger-')), recovery.publicKey),
}
}
test('persists only increasing keyring generations and identical repeats', async () => {
const { ledger, recovery } = await fixture()
const current = keyPair()
const next = keyPair()
const first = document(keyring(1, current, next))
await ledger.acceptKeyring(first, signature(first, recovery))
await ledger.acceptKeyring(first, signature(first, recovery))
const conflicting = document({ ...keyring(1, current, next), issuedAt: '2026-08-19T02:00:00.000Z' })
await assert.rejects(ledger.acceptKeyring(conflicting, signature(conflicting, recovery)))
const older = document(keyring(1, current, next))
const third = keyPair()
const rotated = document(keyring(2, next, third, [current.keyId]))
await ledger.acceptKeyring(rotated, signature(rotated, recovery))
await assert.rejects(ledger.acceptKeyring(older, signature(older, recovery)), { code: 'TRUST_ROLLBACK' })
})
test('accepts targets only from current release key and prevents sequence rollback', async () => {
const { ledger, recovery } = await fixture()
const current = keyPair()
const next = keyPair()
const ring = document(keyring(1, current, next))
await ledger.acceptKeyring(ring, signature(ring, recovery))
const first = document(target(1, 1))
await assert.rejects(ledger.acceptTarget(first, signature(first, next)), { code: 'TRUST_UNKNOWN_KEY' })
await ledger.acceptTarget(first, signature(first, current))
await ledger.acceptTarget(first, signature(first, current))
const conflicting = document({ ...target(1, 1), target: { changed: true } })
await assert.rejects(ledger.acceptTarget(conflicting, signature(conflicting, current)))
const second = document(target(1, 2))
await ledger.acceptTarget(second, signature(second, current))
await assert.rejects(ledger.acceptTarget(first, signature(first, current)), { code: 'TRUST_ROLLBACK' })
})
test('requires an accepted keyring before accepting a target', async () => {
const { ledger } = await fixture()
const release = keyPair()
const bytes = document(target(1, 1))
await assert.rejects(ledger.acceptTarget(bytes, signature(bytes, release)), /keyring/)
})
test('records official DSH identity monotonically without choosing an update channel', async () => {
const { ledger, recovery } = await fixture()
const current = keyPair()
const next = keyPair()
const registry = registryKeyPair()
const ring = document(keyring(1, current, next))
await ledger.acceptKeyring(ring, signature(ring, recovery))
const stable = document(target(1, 4, officialDshPolicy(registry)))
await ledger.acceptTarget(stable, signature(stable, current))
const content = Buffer.from('rc8')
const first = registryCandidate(registry, '0.1.0-rc.8', content)
const digest = createHash('sha256').update(content).digest('hex')
await ledger.acceptOfficialDsh(first, digest, content.byteLength)
await ledger.acceptOfficialDsh(first, digest, content.byteLength)
assert.equal((await ledger.currentOfficialDsh()).value.version, first.version)
const older = registryCandidate(registry, '0.1.0-rc.6', content)
await assert.rejects(ledger.acceptOfficialDsh(older, digest, content.byteLength), { code: 'TRUST_ROLLBACK' })
const conflicting = registryCandidate(registry, first.version, Buffer.from('changed'))
await assert.rejects(
ledger.acceptOfficialDsh(conflicting, createHash('sha256').update('changed').digest('hex'), 7),
/different content/,
)
})
test('rejects new official DSH imports after the target Release Key is revoked', async () => {
const { ledger, recovery } = await fixture()
const current = keyPair()
const next = keyPair()
const registry = registryKeyPair()
const ring = document(keyring(1, current, next))
await ledger.acceptKeyring(ring, signature(ring, recovery))
const stable = document(target(1, 4, officialDshPolicy(registry)))
await ledger.acceptTarget(stable, signature(stable, current))
const content = Buffer.from('rc8')
const candidate = registryCandidate(registry, '0.1.0-rc.8', content)
const digest = createHash('sha256').update(content).digest('hex')
await ledger.acceptOfficialDsh(candidate, digest, content.byteLength)
const future = keyPair()
const rotated = document(keyring(2, next, future, [current.keyId]))
await ledger.acceptKeyring(rotated, signature(rotated, recovery))
await assert.rejects(ledger.acceptOfficialDsh(candidate, digest, content.byteLength), { code: 'TRUST_REVOKED' })
await assert.rejects(ledger.currentOfficialDsh(), { code: 'TRUST_REVOKED' })
})
test('retains historical keyrings so an accepted target remains verifiable after rotation', async () => {
const { ledger, recovery } = await fixture()
const first = keyPair()
const second = keyPair()
const third = keyPair()
const initialRing = document(keyring(1, first, second))
await ledger.acceptKeyring(initialRing, signature(initialRing, recovery))
const initialTarget = document(target(1, 7))
await ledger.acceptTarget(initialTarget, signature(initialTarget, first))
const rotatedRing = document(keyring(2, second, third, [first.keyId]))
await ledger.acceptKeyring(rotatedRing, signature(rotatedRing, recovery))
assert.equal((await ledger.currentTarget()).value.targetSequence, 7)
const rotatedTarget = document(target(2, 8))
await ledger.acceptTarget(rotatedTarget, signature(rotatedTarget, second))
assert.equal((await ledger.currentTarget()).value.keyId, second.keyId)
})
test('serializes concurrent target acceptance in call order', async () => {
const { ledger, recovery } = await fixture()
const current = keyPair()
const next = keyPair()
const ring = document(keyring(1, current, next))
await ledger.acceptKeyring(ring, signature(ring, recovery))
const first = document(target(1, 1))
const second = document(target(1, 2))
await Promise.all([
ledger.acceptTarget(first, signature(first, current)),
ledger.acceptTarget(second, signature(second, current)),
])
assert.equal((await ledger.currentTarget()).value.targetSequence, 2)
})
test('does not overwrite a conflicting historical generation after interrupted promotion', async () => {
const { ledger, recovery } = await fixture()
const first = keyPair()
const second = keyPair()
const third = keyPair()
const initial = document(keyring(1, first, second))
await ledger.acceptKeyring(initial, signature(initial, recovery))
const rotated = document(keyring(2, second, third, [first.keyId]))
await ledger.acceptKeyring(rotated, signature(rotated, recovery))
const rotatedRecord = await readFile(ledger.keyringPath('current.record.json'))
const initialRecord = await readFile(ledger.keyringPath('generations/1.record.json'))
await writeFile(ledger.keyringPath('current.record.json'), initialRecord)
const replacement = keyPair()
const conflicting = document(keyring(2, second, replacement, [first.keyId, third.keyId]))
await assert.rejects(
ledger.acceptKeyring(conflicting, signature(conflicting, recovery)),
/history conflicts/,
)
assert.deepEqual(await readFile(ledger.keyringPath('generations/2.record.json')), rotatedRecord)
})
@@ -0,0 +1,321 @@
import assert from 'node:assert/strict'
import { EventEmitter } from 'node:events'
import { appendFile, chmod, mkdtemp, readFile, readdir, stat, utimes, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { PassThrough } from 'node:stream'
import test from 'node:test'
import { errorDetails, JsonlLogManager } from '../../control-plane/modules/log-manager/index.mjs'
test('writes source-separated JSONL and queries bounded chronological entries', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-logs-'))
const logs = new JsonlLogManager({ root })
await logs.append('gateway', 'stdout', 'ready')
await logs.append('updater', 'platform', 'checked', { taskId: 'one' })
const entries = await logs.query({ sources: ['updater'], limit: 10 })
assert.equal(entries.length, 1)
assert.equal(entries[0].taskId, 'one')
assert.equal(entries[0].level, 'info')
assert.deepEqual((await readdir(root)).sort(), ['gateway.jsonl', 'updater.jsonl'])
assert.equal((await logs.query({ limit: 5_000 })).length, 2)
await assert.rejects(logs.query({ limit: 5_001 }), /log query limit is invalid/)
})
test('filters transaction logs by task, operation, and phase', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-log-transaction-'))
const logs = new JsonlLogManager({ root })
await logs.append('updater', 'platform', 'download', { taskId: 'task-a', operation: 'update', phase: 'downloading' })
await logs.append('updater', 'platform', 'health', { taskId: 'task-a', operation: 'update', phase: 'switching' })
await logs.append('updater', 'platform', 'other', { taskId: 'task-b', operation: 'rollback', phase: 'switching' })
const entries = await logs.query({ taskId: 'task-a', operation: 'update', phase: 'downloading' })
assert.deepEqual(entries.map(entry => entry.message), ['download'])
const received = []
const follower = await logs.follow({ taskId: 'task-a', operation: 'update', phase: 'switching' }, entry => received.push(entry), { intervalMs: 50 })
try {
await logs.append('updater', 'platform', 'switching-now', { taskId: 'task-a', operation: 'update', phase: 'switching' })
await new Promise(resolve => setTimeout(resolve, 100))
assert.deepEqual(received.map(entry => entry.message), ['switching-now'])
} finally {
follower.close()
}
})
test('follows entries appended by another process without replaying existing logs', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-log-follow-'))
const logs = new JsonlLogManager({ root })
await logs.append('gateway', 'stdout', 'existing')
const received = []
const follower = await logs.follow({}, entry => received.push(entry), { intervalMs: 50 })
try {
const external = {
timestamp: '2026-08-21T00:00:00.000Z', source: 'gateway', stream: 'stdout', level: 'info', message: '外部日志',
}
const encoded = Buffer.from(`${JSON.stringify(external)}\n`)
const split = encoded.indexOf(Buffer.from('外')) + 1
await appendFile(logs.currentPath('gateway'), encoded.subarray(0, split))
await new Promise(resolve => setTimeout(resolve, 80))
await appendFile(logs.currentPath('gateway'), encoded.subarray(split))
await new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
clearInterval(poll)
reject(new Error('timed out waiting for followed log'))
}, 1_000)
const poll = setInterval(() => {
if (received.length === 0) return
clearTimeout(timeout)
clearInterval(poll)
resolve()
}, 10)
})
assert.deepEqual(received.map(entry => entry.message), ['外部日志'])
} finally {
follower.close()
}
})
test('assigns default levels and accepts only explicit supported levels', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-log-levels-'))
const logs = new JsonlLogManager({ root })
await logs.append('component', 'stdout', 'ordinary')
await logs.append('component', 'stderr', 'failed')
await logs.append('component', 'platform', 'slow response', { level: 'warning' })
await logs.diagnostic('component', 'completed', { error: null })
assert.deepEqual((await logs.query()).map(entry => entry.level), ['info', 'error', 'warning', 'info'])
await assert.rejects(logs.append('component', 'stdout', 'invalid', { level: 'fatal' }), /log level is invalid/)
})
test('rate-limits repeated diagnostics and reports the suppressed count', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-log-suppression-'))
let now = Date.parse('2026-08-19T00:00:00.000Z')
const logs = new JsonlLogManager({ root, now: () => new Date(now) })
await logs.diagnosticRateLimited('authentication.failed', 'access-manager', 'access.authentication.failed', {
level: 'warning', kind: 'main',
})
now += 1_000
await logs.diagnosticRateLimited('authentication.failed', 'access-manager', 'access.authentication.failed', {
level: 'warning', kind: 'main',
})
now += 30_000
await logs.diagnosticRateLimited('authentication.failed', 'access-manager', 'access.authentication.failed', {
level: 'warning', kind: 'main',
})
const entries = await logs.query({ sources: ['access-manager'] })
assert.equal(entries.length, 2)
assert.equal(entries[0].suppressedCount, undefined)
assert.equal(entries[1].suppressedCount, 1)
await assert.rejects(
Promise.resolve().then(() => logs.diagnosticRateLimited('invalid key', 'gateway', 'failed')),
/suppression key is invalid/,
)
})
test('applies an explicit shared-reader mode to newly created and rotated log files', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-log-mode-'))
const logs = new JsonlLogManager({ root, fileMode: 0o640, rotateBytes: 1_024 })
await logs.append('stage0', 'platform', 'first')
assert.equal((await stat(join(root, 'stage0.jsonl'))).mode & 0o777, 0o640)
await logs.append('stage0', 'platform', 'x'.repeat(1_000))
await logs.append('stage0', 'platform', 'rotated')
assert.equal((await stat(join(root, 'stage0.jsonl'))).mode & 0o777, 0o640)
})
test('repairs existing log files for shared Control Plane writers', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-log-shared-writers-'))
const path = join(root, 'audit.jsonl')
await writeFile(path, '{}\n')
await chmod(path, 0o640)
const logs = new JsonlLogManager({ root, fileMode: 0o660 })
await logs.prepare()
assert.equal((await stat(path)).mode & 0o777, 0o660)
await logs.append('audit', 'audit', 'shared-writer.ready')
assert.equal((await readFile(path, 'utf8')).includes('shared-writer.ready'), true)
})
test('rotates files and enforces the aggregate byte budget', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-log-rotation-'))
const logs = new JsonlLogManager({ root, maxBytes: 70_000, rotateBytes: 10_000 })
for (let index = 0; index < 150; index += 1) await logs.append('gateway', 'stdout', 'x'.repeat(800))
const files = await logs.files()
assert.ok(files.length > 1)
assert.ok(files.reduce((sum, file) => sum + file.details.size, 0) <= 70_000)
})
test('prunes expired logs and captures complete child output lines', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-log-retention-'))
const logs = new JsonlLogManager({ root, retentionDays: 1 })
await logs.append('old', 'stdout', 'expired')
const oldPath = join(root, 'old.jsonl')
const old = new Date(Date.now() - 2 * 86_400_000)
await utimes(oldPath, old, old)
await logs.prune()
await assert.rejects(stat(oldPath), { code: 'ENOENT' })
const child = new EventEmitter()
child.stdout = new PassThrough()
child.stderr = new PassThrough()
logs.capture(child, 'component', { stdout: true, stderr: false })
child.stdout.end('one\ntwo')
await new Promise(resolve => setImmediate(resolve))
await logs.queue
assert.deepEqual((await logs.query({ sources: ['component'] })).map(entry => entry.message), ['one', 'two'])
})
test('captures one multiline Node error as one log entry', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-log-multiline-error-'))
const logs = new JsonlLogManager({ root })
const child = new EventEmitter()
child.stderr = new PassThrough()
logs.capture(child, 'dsh-runtime', { stdout: false, stderr: true })
child.stderr.write('ordinary before\nfile:///app/index.js:2\n throw new Error("startup failed")\n')
child.stderr.end(' ^\n\nError: startup failed\n at file:///app/index.js:2:9\n\nNode.js v24.20.0\nordinary after\n')
await new Promise(resolve => setImmediate(resolve))
await logs.queue
assert.deepEqual((await logs.query({ sources: ['dsh-runtime'] })).map(entry => ({
level: entry.level,
message: entry.message,
})), [
{ level: 'info', message: 'ordinary before' },
{
level: 'error',
message: 'file:///app/index.js:2\n throw new Error("startup failed")\n ^\n\nError: startup failed\n at file:///app/index.js:2:9\n\nNode.js v24.20.0',
},
{ level: 'info', message: 'ordinary after' },
])
})
test('does not group multiline stderr from other components', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-log-other-stderr-'))
const logs = new JsonlLogManager({ root })
const child = new EventEmitter()
child.stderr = new PassThrough()
logs.capture(child, 'gateway', { stdout: false, stderr: true })
child.stderr.end('Error: first line\n at second line\nordinary line\n')
await new Promise(resolve => setImmediate(resolve))
await logs.queue
assert.deepEqual((await logs.query({ sources: ['gateway'] })).map(entry => entry.message), [
'Error: first line', ' at second line', 'ordinary line',
])
})
test('mirrors new entries to the matching container output without replaying history', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-log-output-'))
const stdout = []
const stderr = []
const logs = new JsonlLogManager({
root,
now: () => new Date('2026-08-19T00:00:00.000Z'),
output: {
stdout: { write: value => stdout.push(value) },
stderr: { write: value => stderr.push(value) },
},
})
await logs.append('gateway', 'stdout', 'ready')
await logs.append('dsh-runtime', 'stderr', 'failed')
assert.deepEqual(stdout.map(line => JSON.parse(line)), [{
timestamp: '2026-08-19T00:00:00.000Z', source: 'gateway', stream: 'stdout', message: 'ready',
level: 'info', platformLog: 'dsh-platform-log-v1',
}])
assert.deepEqual(stderr.map(line => JSON.parse(line)), [{
timestamp: '2026-08-19T00:00:00.000Z', source: 'dsh-runtime', stream: 'stderr', message: 'failed',
level: 'error', platformLog: 'dsh-platform-log-v1',
}])
const secondOutput = []
const restarted = new JsonlLogManager({
root,
output: { stdout: { write: value => secondOutput.push(value) }, stderr: { write: value => secondOutput.push(value) } },
})
assert.equal((await restarted.query()).length, 2)
assert.deepEqual(secondOutput, [])
})
test('passes a platform-management envelope through without persisting it twice', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-log-forwarded-'))
const stdout = []
const stderr = []
const logs = new JsonlLogManager({
root,
output: {
stdout: { write: value => stdout.push(value) },
stderr: { write: value => stderr.push(value) },
},
})
const child = new EventEmitter()
child.stdout = new PassThrough()
child.stderr = new PassThrough()
logs.capture(child, 'platform-management', { stdout: true, stderr: true }, { acceptForwarded: true })
const audit = JSON.stringify({
timestamp: '2026-08-19T00:00:00.000Z', source: 'audit', stream: 'audit', message: 'update.started',
platformLog: 'dsh-platform-log-v1', taskId: 'task-one',
})
child.stdout.end(`${audit}\nplain output\n`)
child.stderr.end('[net-proxy] 已启用代理 http://172.17.0.1:7890\nwarning: slow proxy\nplain error\n')
await new Promise(resolve => setImmediate(resolve))
await logs.queue
assert.equal(stdout[0], `${audit}\n`)
assert.equal(JSON.parse(stdout[1]).source, 'platform-management')
assert.equal(JSON.parse(stderr[0]).stream, 'stderr')
assert.deepEqual(
(await logs.query({ sources: ['platform-management'] })).map(entry => [entry.stream, entry.level, entry.message]),
[
['stdout', 'info', 'plain output'],
['stderr', 'info', '[net-proxy] 已启用代理 http://172.17.0.1:7890'],
['stderr', 'warning', 'warning: slow proxy'],
['stderr', 'error', 'plain error'],
],
)
assert.deepEqual(await logs.query({ sources: ['audit'] }), [])
const bootstrap = await readFile(new URL('../bootstrap/index.mjs', import.meta.url), 'utf8')
assert.match(bootstrap, /acceptForwarded: \['gateway', 'platform-management'\]\.includes\(source\)/)
})
test('records bounded structured error diagnostics in JSONL and container output', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-log-diagnostic-'))
const stdout = []
const logs = new JsonlLogManager({
root,
now: () => new Date('2026-08-20T00:00:00.000Z'),
output: { stdout: { write: value => stdout.push(value) } },
})
const cause = Object.assign(new Error('socket unavailable'), { code: 'ECONNREFUSED' })
const failure = new AggregateError([cause, 'secondary failure'], 'startup failed', { cause })
await logs.diagnostic('stage0', 'bootstrap.launch.failed', { error: failure, recordId: 'bootstrap-one' })
const [entry] = await logs.query({ sources: ['stage0'] })
assert.equal(entry.message, 'bootstrap.launch.failed')
assert.equal(entry.recordId, 'bootstrap-one')
assert.equal(entry.error, 'startup failed')
assert.equal(entry.errorCause.errorCode, 'ECONNREFUSED')
assert.equal(entry.errors[1].error, 'secondary failure')
assert.match(entry.errorStack, /AggregateError: startup failed/)
assert.equal(JSON.parse(stdout[0]).platformLog, 'dsh-platform-log-v1')
})
test('falls back to container stderr when a diagnostic cannot be persisted', async () => {
const stderr = []
const logs = new JsonlLogManager({
root: '/dev/null/not-a-directory',
now: () => new Date('2026-08-20T00:00:00.000Z'),
output: { stderr: { write: value => stderr.push(value) } },
})
await logs.diagnostic('updater', 'update.failed', { error: new Error('network failed'), taskId: 'one' })
const fallback = JSON.parse(stderr[0])
assert.equal(fallback.message, 'diagnostic.write.failed')
assert.equal(fallback.diagnostic.message, 'update.failed')
assert.equal(fallback.diagnostic.error, 'network failed')
assert.equal(fallback.loggingError.errorCode, 'ENOTDIR')
})
test('bounds recursive and oversized error details', () => {
let failure = new Error('root')
for (let index = 0; index < 6; index += 1) failure = new Error(`level-${String(index)}`, { cause: failure })
const details = errorDetails(new AggregateError(Array.from({ length: 10 }, (_, index) => new Error(String(index))), 'x'.repeat(20_000), { cause: failure }))
assert.equal(details.error.endsWith('...[truncated]'), true)
assert.equal(details.errors.length, 8)
assert.equal(details.errorsTruncated, 2)
assert.equal(details.errorCause.errorCause.errorCause.errorCause, undefined)
})
@@ -0,0 +1,372 @@
import assert from 'node:assert/strict'
import { chmod, mkdir, mkdtemp, readFile, rename, rm, writeFile } from 'node:fs/promises'
import { createServer } from 'node:http'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { pathToFileURL } from 'node:url'
import test from 'node:test'
import {
applyPatch,
MANAGED_LIFECYCLE_MODULE,
PROFILE_PACKAGE_STORAGE_MODULE,
verifyPatch,
} from '../../environment/resources/patches/managed-lifecycle.mjs'
async function fixture({ duplicateImport = false, sigterm = true, wrapper = true } = {}) {
const root = await mkdtemp(join(tmpdir(), 'dsh-managed-lifecycle-patch-'))
await mkdir(join(root, 'lib'))
await writeFile(join(root, 'package.json'), '{"type":"module"}\n')
const dynamicImport = 'const { runProfile } = await import("./profile-boot-wrapper_42.js");'
await writeFile(join(root, 'lib/bin.js'), `#!/usr/bin/env node
const invocation = parseDshArgs(process.argv.slice(2), readVersion());
switch (invocation.mode) {
case "profile": {
${dynamicImport}
${duplicateImport ? dynamicImport : ''}
}
}
`)
await writeFile(join(root, 'lib/profile-boot-wrapper_42.js'), wrapper
? 'import { x as runProfile } from "./profile-boot-anyHash.js";\nexport { runProfile };\n'
: 'export const unrelated = true;\n')
await writeFile(join(root, 'lib/profile-boot-anyHash.js'), `async function runProfile(options) {
${sigterm ? '\tprocess.on("SIGTERM", () => {\n\t\tinterrupt(0);\n\t});' : '\tprocess.on("SIGHUP", () => interrupt(0));'}
\tprocess.on("SIGINT", () => { interrupt(130); });
\tconst ctx = await boot(NAME, rootConfig, patches, (hostCtx) => {
\t\thostCtx.provide(DSH_LAUNCH_ENVIRONMENT_KEY, options.environment);
\t});
\treturn {
\t\tctx,
\t\tshutdown
\t};
}
export { runProfile as x };
`)
return root
}
async function listen(socketPath, handler) {
const server = createServer(handler)
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(socketPath, resolve)
})
await chmod(socketPath, 0o600)
return server
}
function json(response, status, value) {
response.writeHead(status, { 'content-type': 'application/json' })
response.end(JSON.stringify(value))
}
test('patches a hash-independent Profile import chain exactly once', async () => {
const root = await fixture()
applyPatch(root)
verifyPatch(root)
assert.match(await readFile(join(root, 'lib/bin.js'), 'utf8'), /prepareManagedInvocation/)
assert.match(await readFile(join(root, 'lib/profile-boot-anyHash.js'), 'utf8'), /managedSigtermHandler/)
assert.match(await readFile(join(root, MANAGED_LIFECYCLE_MODULE), 'utf8'), /dshPlatformLifecycle/)
assert.match(await readFile(join(root, PROFILE_PACKAGE_STORAGE_MODULE), 'utf8'), /prepareProfilePackageStorage/)
assert.throws(() => applyPatch(root), /already applied/)
})
test('fails closed when upstream Profile anchors are missing or ambiguous', async () => {
await assert.rejects(Promise.resolve().then(async () => applyPatch(await fixture({ duplicateImport: true }))), /found 2/)
await assert.rejects(Promise.resolve().then(async () => applyPatch(await fixture({ wrapper: false }))), /found 0/)
await assert.rejects(Promise.resolve().then(async () => applyPatch(await fixture({ sigterm: false }))), /SIGTERM handler.*found 0/)
})
async function fakePnpm(root) {
const bin = join(root, 'bin')
await mkdir(bin, { recursive: true })
const executable = join(bin, 'pnpm')
await writeFile(executable, `#!/usr/bin/env node
import { mkdirSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
if (process.env.DSH_TEST_PNPM_FAIL === '1') {
process.stderr.write('fixture pnpm failed\\n')
process.exit(1)
}
const workspace = await import('node:fs').then(({ readFileSync }) => readFileSync('pnpm-workspace.yaml', 'utf8'))
const match = /^storeDir:\\s*(.+)$/m.exec(workspace)
if (match === null) process.exit(2)
const storeRoot = JSON.parse(match[1])
mkdirSync('node_modules', { recursive: true })
writeFileSync(join('node_modules', '.modules.yaml'), JSON.stringify({ storeDir: join(storeRoot, 'v11') }))
writeFileSync(join('node_modules', 'migration-marker'), 'rebuilt\\n')
`)
await chmod(executable, 0o755)
return bin
}
test('pins compatible Profiles to their custom DSH_HOME store', async () => {
const root = await fixture()
applyPatch(root)
const storage = await import(`${pathToFileURL(join(root, PROFILE_PACKAGE_STORAGE_MODULE)).href}?storage`)
const dshHome = await mkdtemp(join(tmpdir(), 'dsh-profile-storage-home-'))
const profileDir = join(dshHome, 'profiles', 'web')
const workspacePath = join(profileDir, 'pnpm-workspace.yaml')
const modulesRoot = join(profileDir, 'node_modules')
await mkdir(modulesRoot, { recursive: true })
await writeFile(join(profileDir, 'package.json'), '{"name":"dsh-profile-web"}\n')
await writeFile(workspacePath, 'packages:\n - .\n\nnodeLinker: hoisted\n')
const previousHome = process.env.DSH_HOME
process.env.DSH_HOME = dshHome
try {
assert.deepEqual(storage.prepareProfilePackageStorage('web'), {
status: 'ready',
currentStore: null,
storeRoot: join(dshHome, '.pnpm-store'),
})
assert.match(await readFile(workspacePath, 'utf8'), new RegExp(
`storeDir: ${JSON.stringify(join(dshHome, '.pnpm-store')).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`,
))
await writeFile(join(modulesRoot, '.modules.yaml'), JSON.stringify({
storeDir: join(dshHome, '.pnpm-store', 'v11'),
}))
assert.equal(storage.prepareProfilePackageStorage('web').status, 'ready')
assert.equal((await readFile(workspacePath, 'utf8')).match(/^storeDir:/gm)?.length, 1)
const ambiguous = `${await readFile(workspacePath, 'utf8')}storeDir: "/another/store"\n`
await writeFile(workspacePath, ambiguous)
assert.throws(() => storage.prepareProfilePackageStorage('web'), /multiple storeDir entries/)
assert.equal(await readFile(workspacePath, 'utf8'), ambiguous)
} finally {
if (previousHome === undefined) delete process.env.DSH_HOME
else process.env.DSH_HOME = previousHome
}
})
test('migrates a legacy Profile store and recovers a failed or interrupted rebuild', async () => {
const root = await fixture()
applyPatch(root)
const storage = await import(`${pathToFileURL(join(root, PROFILE_PACKAGE_STORAGE_MODULE)).href}?migration`)
const dshHome = await mkdtemp(join(tmpdir(), 'dsh-profile-migration-home-'))
const profileDir = join(dshHome, 'profiles', 'web')
const modulesRoot = join(profileDir, 'node_modules')
const workspacePath = join(profileDir, 'pnpm-workspace.yaml')
const legacyStore = join(root, 'workspace', '.pnpm-store', 'v11')
const originalWorkspace = `packages:\n - .\n\nstoreDir: ${JSON.stringify(dirname(legacyStore))}\n`
await mkdir(modulesRoot, { recursive: true })
await mkdir(legacyStore, { recursive: true })
await writeFile(join(legacyStore, 'store-marker'), 'cached\n')
await writeFile(join(modulesRoot, '.modules.yaml'), JSON.stringify({ storeDir: legacyStore }))
await writeFile(join(modulesRoot, 'original-marker'), 'original\n')
await writeFile(join(profileDir, 'package.json'), '{"name":"dsh-profile-web","private":true}\n')
await writeFile(join(profileDir, 'pnpm-lock.yaml'), 'lockfileVersion: 9\n')
await writeFile(workspacePath, originalWorkspace)
const bin = await fakePnpm(root)
const previous = {
DSH_HOME: process.env.DSH_HOME,
PATH: process.env.PATH,
DSH_TEST_PNPM_FAIL: process.env.DSH_TEST_PNPM_FAIL,
}
process.env.DSH_HOME = dshHome
process.env.PATH = `${bin}:${process.env.PATH}`
try {
assert.deepEqual(storage.prepareProfilePackageStorage('web'), {
status: 'migrated',
currentStore: join(dshHome, '.pnpm-store', 'v11'),
storeRoot: join(dshHome, '.pnpm-store'),
})
assert.equal(await readFile(join(dshHome, '.pnpm-store', 'v11', 'store-marker'), 'utf8'), 'cached\n')
assert.equal(await readFile(join(modulesRoot, 'migration-marker'), 'utf8'), 'rebuilt\n')
await assert.rejects(readFile(join(profileDir, '.dsh-platform-node_modules.previous')), { code: 'ENOENT' })
await assert.rejects(readFile(join(profileDir, '.dsh-platform-pnpm-workspace.previous')), { code: 'ENOENT' })
await rm(modulesRoot, { recursive: true })
await mkdir(modulesRoot)
await writeFile(join(modulesRoot, '.modules.yaml'), JSON.stringify({ storeDir: legacyStore }))
await writeFile(join(modulesRoot, 'original-marker'), 'restored\n')
await writeFile(workspacePath, originalWorkspace)
process.env.DSH_TEST_PNPM_FAIL = '1'
assert.throws(() => storage.prepareProfilePackageStorage('web'), /fixture pnpm failed/)
assert.equal(await readFile(join(modulesRoot, 'original-marker'), 'utf8'), 'restored\n')
assert.equal(await readFile(workspacePath, 'utf8'), originalWorkspace)
delete process.env.DSH_TEST_PNPM_FAIL
await writeFile(join(profileDir, '.dsh-platform-pnpm-workspace.previous'), originalWorkspace)
await writeFile(workspacePath, `packages:\n - .\n\nstoreDir: ${JSON.stringify(join(dshHome, '.pnpm-store'))}\n`)
assert.equal(storage.prepareProfilePackageStorage('web').status, 'migrated')
assert.equal(await readFile(join(modulesRoot, 'migration-marker'), 'utf8'), 'rebuilt\n')
await rm(modulesRoot, { recursive: true })
await mkdir(modulesRoot)
await writeFile(join(modulesRoot, '.modules.yaml'), JSON.stringify({ storeDir: legacyStore }))
await writeFile(join(modulesRoot, 'original-marker'), 'interrupted\n')
await writeFile(workspacePath, originalWorkspace)
await writeFile(join(profileDir, '.dsh-platform-pnpm-workspace.previous'), originalWorkspace)
await rename(modulesRoot, join(profileDir, '.dsh-platform-node_modules.previous'))
await writeFile(workspacePath, `packages:\n - .\n\nstoreDir: ${JSON.stringify(join(dshHome, '.pnpm-store'))}\n`)
assert.equal(storage.prepareProfilePackageStorage('web').status, 'migrated')
assert.equal(await readFile(join(modulesRoot, 'migration-marker'), 'utf8'), 'rebuilt\n')
await rm(modulesRoot, { recursive: true })
await mkdir(modulesRoot)
await writeFile(join(modulesRoot, '.modules.yaml'), JSON.stringify({
storeDir: join(dshHome, '.pnpm-store', 'v11'),
}))
await writeFile(join(modulesRoot, 'partial-marker'), 'partial\n')
await writeFile(workspacePath, `packages:\n - .\n\nstoreDir: ${JSON.stringify(join(dshHome, '.pnpm-store'))}\n`)
await mkdir(join(profileDir, '.dsh-platform-node_modules.previous'))
await writeFile(join(profileDir, '.dsh-platform-node_modules.previous', '.modules.yaml'), JSON.stringify({
storeDir: legacyStore,
}))
await writeFile(join(profileDir, '.dsh-platform-node_modules.previous', 'original-marker'), 'original\n')
await writeFile(join(profileDir, '.dsh-platform-pnpm-workspace.previous'), originalWorkspace)
assert.equal(storage.prepareProfilePackageStorage('web').status, 'migrated')
assert.equal(await readFile(join(modulesRoot, 'migration-marker'), 'utf8'), 'rebuilt\n')
await assert.rejects(readFile(join(modulesRoot, 'partial-marker')), { code: 'ENOENT' })
} finally {
for (const [name, value] of Object.entries(previous)) {
if (value === undefined) delete process.env[name]
else process.env[name] = value
}
}
})
test('gates only managed Web launches and routes restart through Management', async t => {
const root = await fixture()
applyPatch(root)
const runRoot = await mkdtemp(join(tmpdir(), 'dsh-managed-lifecycle-run-'))
const dshHome = await mkdtemp(join(tmpdir(), 'dsh-managed-lifecycle-home-'))
const profileDir = join(dshHome, 'profiles', 'web')
await mkdir(profileDir, { recursive: true })
await mkdir(join(root, 'node_modules', 'built-in-bundle'), { recursive: true })
await writeFile(join(root, 'node_modules', 'built-in-bundle', 'package.json'), '{"name":"built-in-bundle"}\n')
const profileManifest = join(profileDir, 'package.json')
await writeFile(join(profileDir, 'pnpm-workspace.yaml'), 'packages:\n - .\n\nnodeLinker: hoisted\n')
await writeFile(profileManifest, JSON.stringify({
dependencies: { 'missing-dependency': '1.0.0' },
dsh: { profile: { bundles: ['built-in-bundle', 'removed-plugin', '../../outside', 'missing-dependency'] } },
}, null, 2))
const calls = []
let claimStatus = 200
let disposition = 'request-restart'
let lifecycleState = 'running'
let signalDelayMs = 0
const broker = await listen(join(runRoot, 'dsh-lifecycle.sock'), async (request, response) => {
const chunks = []
for await (const chunk of request) chunks.push(chunk)
calls.push({ socket: 'broker', path: request.url, body: JSON.parse(Buffer.concat(chunks)) })
if (claimStatus !== 200) json(response, claimStatus, { error: 'launch already claimed' })
else if (request.url === '/v1/runtime/claim') json(response, 200, { sessionId: 'session-1' })
else if (request.url === '/v1/runtime/ready') json(response, 200, { ready: true })
else {
if (signalDelayMs > 0) await new Promise(resolve => setTimeout(resolve, signalDelayMs))
json(response, 200, { disposition })
}
})
const management = await listen(join(runRoot, 'management-cli.sock'), (request, response) => {
calls.push({ socket: 'management', path: request.url })
if (request.url?.endsWith('/status')) json(response, 200, { dshLifecycle: { state: lifecycleState }, recoveryMode: null })
else json(response, 202, { taskId: request.url?.endsWith('/start-dsh') ? 'start-task' : 'restart-task' })
})
t.after(() => { broker.close(); management.close() })
const previous = Object.fromEntries(['DSH_HOME', 'DSH_PLATFORM_MANAGED', 'DSH_PLATFORM_RUN', 'DSH_PLATFORM_LAUNCH_TOKEN']
.map(name => [name, process.env[name]]))
process.env.DSH_HOME = dshHome
process.env.DSH_PLATFORM_MANAGED = '1'
process.env.DSH_PLATFORM_RUN = runRoot
process.env.DSH_PLATFORM_LAUNCH_TOKEN = 'launch-token'
try {
const adapter = await import(`${pathToFileURL(join(root, MANAGED_LIFECYCLE_MODULE)).href}?authorized`)
assert.equal(await adapter.prepareManagedInvocation({ mode: 'plugin', profile: 'web' }), null)
assert.equal(calls.length, 0)
assert.match(await readFile(join(profileDir, 'pnpm-workspace.yaml'), 'utf8'), new RegExp(
`storeDir: ${JSON.stringify(join(dshHome, '.pnpm-store')).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`,
))
assert.equal(await adapter.prepareManagedInvocation({ mode: 'profile', profile: 'web' }), null)
assert.equal(process.env.DSH_PLATFORM_LAUNCH_TOKEN, undefined)
assert.deepEqual(
JSON.parse(await readFile(profileManifest, 'utf8')).dsh.profile.bundles,
['built-in-bundle', 'missing-dependency'],
)
await adapter.markManagedReady({
get: name => name === 'connection'
? { authenticatedUrl: base => `${base}/?token=fixture-token` }
: name === 'webServer' ? { port: 3079 } : undefined,
})
assert.equal(calls.some(value => value.socket === 'broker' && value.path === '/v1/runtime/ready'), true)
assert.deepEqual(calls.find(value => value.path === '/v1/runtime/ready')?.body, {
sessionId: 'session-1',
readyUrl: 'http://127.0.0.1:3079/?token=fixture-token',
})
let provided
adapter.provideManagedLifecycle({ provide: (name, value) => { provided = { name, value } } })
assert.equal(provided.name, 'dshPlatformLifecycle')
assert.deepEqual(await provided.value.restart(), { taskId: 'restart-task' })
const restartCallsBeforeSigterm = calls.filter(value => value.path?.endsWith('/restart-dsh')).length
let interrupts = 0
const onSigterm = adapter.managedSigtermHandler(() => { interrupts += 1 })
onSigterm()
for (let attempt = 0; attempt < 50 && calls.filter(value => value.path?.endsWith('/restart-dsh')).length === restartCallsBeforeSigterm; attempt += 1) {
await new Promise(resolve => setTimeout(resolve, 5))
}
assert.equal(calls.some(value => value.socket === 'broker' && value.path === '/v1/runtime/signal'), true)
assert.equal(calls.filter(value => value.path?.endsWith('/restart-dsh')).length, restartCallsBeforeSigterm + 1)
assert.equal(interrupts, 0)
onSigterm()
assert.equal(interrupts, 1)
const restartCallsBeforeCancellation = calls.filter(value => value.path?.endsWith('/restart-dsh')).length
signalDelayMs = 30
let cancellationInterrupts = 0
const cancelSigterm = adapter.managedSigtermHandler(() => { cancellationInterrupts += 1 })
cancelSigterm()
cancelSigterm()
await new Promise(resolve => setTimeout(resolve, 50))
assert.equal(cancellationInterrupts, 1)
assert.equal(calls.filter(value => value.path?.endsWith('/restart-dsh')).length, restartCallsBeforeCancellation)
signalDelayMs = 0
disposition = 'terminate'
let terminationInterrupts = 0
const managementCalls = calls.filter(value => value.path?.endsWith('/restart-dsh')).length
adapter.managedSigtermHandler(() => { terminationInterrupts += 1 })()
for (let attempt = 0; attempt < 50 && terminationInterrupts === 0; attempt += 1) {
await new Promise(resolve => setTimeout(resolve, 5))
}
assert.equal(terminationInterrupts, 1)
assert.equal(calls.filter(value => value.path?.endsWith('/restart-dsh')).length, managementCalls)
claimStatus = 409
disposition = 'request-restart'
const duplicate = await import(`${pathToFileURL(join(root, MANAGED_LIFECYCLE_MODULE)).href}?duplicate`)
assert.equal(await duplicate.prepareManagedInvocation({ mode: 'profile', profile: 'web' }), 0)
lifecycleState = 'stopped'
const stopped = await import(`${pathToFileURL(join(root, MANAGED_LIFECYCLE_MODULE)).href}?stopped`)
assert.equal(await stopped.prepareManagedInvocation({ mode: 'profile', profile: 'web' }), 0)
assert.equal(calls.some(value => value.path?.endsWith('/start-dsh')), true)
lifecycleState = 'failed'
const failed = await import(`${pathToFileURL(join(root, MANAGED_LIFECYCLE_MODULE)).href}?failed`)
assert.equal(await failed.prepareManagedInvocation({ mode: 'profile', profile: 'web' }), 1)
} finally {
for (const [name, value] of Object.entries(previous)) {
if (value === undefined) delete process.env[name]
else process.env[name] = value
}
}
})
test('keeps native behavior without the image marker and fails closed without the Broker', async () => {
const root = await fixture()
applyPatch(root)
const adapter = await import(`${pathToFileURL(join(root, MANAGED_LIFECYCLE_MODULE)).href}?fail-closed`)
const previous = Object.fromEntries(['DSH_PLATFORM_MANAGED', 'DSH_PLATFORM_RUN']
.map(name => [name, process.env[name]]))
try {
delete process.env.DSH_PLATFORM_MANAGED
assert.equal(await adapter.prepareManagedInvocation({ mode: 'profile', profile: 'web' }), null)
process.env.DSH_PLATFORM_MANAGED = '1'
process.env.DSH_PLATFORM_RUN = await mkdtemp(join(tmpdir(), 'dsh-no-broker-'))
assert.equal(await adapter.prepareManagedInvocation({ mode: 'profile', profile: 'web' }), 1)
} finally {
for (const [name, value] of Object.entries(previous)) {
if (value === undefined) delete process.env[name]
else process.env[name] = value
}
}
})
@@ -0,0 +1,208 @@
import assert from 'node:assert/strict'
import { createHash } from 'node:crypto'
import { spawnSync } from 'node:child_process'
import { lstat, mkdir, mkdtemp, readFile, symlink, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { ManagedDeploymentBuilder } from '../../control-plane/modules/updater/lib/managed-store.mjs'
import { canonicalJson } from '../lib/canonical-json.mjs'
import { deriveRecordId } from '../lib/deployment-contracts.mjs'
import { PlatformPaths, preparePersistentLayout } from '../lib/paths.mjs'
test('builds a complete content-addressed Managed Deployment from verified inputs', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-managed-store-'))
const paths = new PlatformPaths(join(root, 'data'), join(root, 'run'))
await preparePersistentLayout(paths)
const dependencyRoot = join(root, 'dependency', 'package')
await mkdir(join(dependencyRoot, 'lib'), { recursive: true })
await writeFile(join(dependencyRoot, 'package.json'), JSON.stringify({ name: 'fixture-dependency', version: '1.0.0' }))
await writeFile(join(dependencyRoot, 'lib', 'index.js'), 'export const installed = true\n')
const dependencyArchive = join(root, 'fixture-dependency.tgz')
const packedDependency = spawnSync('tar', ['-czf', dependencyArchive, '-C', join(root, 'dependency'), 'package'], { encoding: 'utf8' })
assert.equal(packedDependency.status, 0, packedDependency.stderr)
const packageRoot = join(root, 'source', 'package')
await mkdir(join(packageRoot, 'lib'), { recursive: true })
await writeFile(join(packageRoot, 'package.json'), JSON.stringify({
name: '@deepseek-ai/dsh',
version: '0.1.0-rc.8',
dependencies: { 'fixture-dependency': `file:${dependencyArchive}` },
}))
await writeFile(join(packageRoot, 'lib', 'bin.js'), '#!/usr/bin/env node\n')
const archive = join(root, 'dsh.tgz')
const packed = spawnSync('tar', ['-czf', archive, '-C', join(root, 'source'), 'package'], { encoding: 'utf8' })
assert.equal(packed.status, 0, packed.stderr)
const archiveBytes = await readFile(archive)
const objectSha256 = createHash('sha256').update(archiveBytes).digest('hex')
const trustedObjectPath = join(paths.objectsRoot, objectSha256)
await writeFile(trustedObjectPath, archiveBytes)
const manifestPath = join(root, 'environment.manifest.json')
const manifest = {
schema: 1,
manifestType: 'environment',
version: '1.0.0',
keyringGeneration: 1,
targetSequence: 2,
issuedAt: '2026-08-19T00:00:00.000Z',
artifacts: [],
bootstrapApi: 1,
components: [],
patches: [],
systemPlugins: [],
}
await writeFile(manifestPath, canonicalJson(manifest))
const prepared = {
stable: {
targetSequence: 2,
desired: { environment: { manifestArtifactId: 'environment-manifest' } },
},
environment: {
manifest,
manifestReceipt: { objectSha256: createHash('sha256').update(await readFile(manifestPath)).digest('hex') },
},
paths: new Map([['environment-manifest', manifestPath]]),
dsh: { version: '0.1.0-rc.8', receipt: { path: trustedObjectPath, objectSha256 } },
receiptTokens: ['stable-receipt', 'dsh-receipt'],
}
const builder = new ManagedDeploymentBuilder({ paths })
const progress = []
const first = await builder.buildStable(prepared, { onProgress: async value => progress.push(value) })
assert.deepEqual(progress.filter(value => typeof value === 'number'), [80, 82, 87, 89])
const runtimeProgress = progress.filter(value => typeof value === 'object')
assert.ok(runtimeProgress.length > 0)
assert.equal(runtimeProgress.at(-1).processedBytes, runtimeProgress.at(-1).totalBytes)
assert.equal(runtimeProgress.at(-1).processedItems, runtimeProgress.at(-1).totalItems)
assert.equal(first.assets.pristine.id, `pristine-npm-${objectSha256}`)
assert.equal(first.record.authority, 'stable')
assert.equal(first.record.targetSequence, 2)
assert.equal(first.record.dshVersion, '0.1.0-rc.8')
assert.deepEqual(first.record.receiptTokens, ['stable-receipt', 'dsh-receipt'])
for (const reference of [
first.record.environment,
first.record.pristine,
first.record.runtime,
first.record.systemPlugins,
]) {
assert.equal(reference.storage, 'store')
const storeRoot = reference.kind === 'environment'
? paths.environmentsRoot
: reference.kind === 'pristine'
? paths.pristineRoot
: reference.kind === 'runtime'
? paths.runtimesRoot
: paths.systemPluginsRoot
assert.equal((await lstat(join(storeRoot, reference.id))).isDirectory(), true)
}
assert.equal(await readFile(join(first.assets.runtime.path, 'package', 'package.json'), 'utf8'), await readFile(join(packageRoot, 'package.json'), 'utf8'))
assert.equal(
await readFile(join(first.assets.pristine.path, 'node_modules', 'fixture-dependency', 'lib', 'index.js'), 'utf8'),
'export const installed = true\n',
)
await assert.rejects(lstat(join(first.assets.pristine.path, 'npm-cache')), error => error?.code === 'ENOENT')
assert.deepEqual(await readFile(trustedObjectPath), archiveBytes)
assert.equal((await lstat(trustedObjectPath)).isSymbolicLink(), false)
assert.equal((await readFile(join(first.assets.systemPlugins.path, 'cordis.patch.yml'), 'utf8')).trim(), '[]')
const repeated = await builder.buildStable(prepared)
assert.equal(repeated.record.id, first.record.id)
assert.deepEqual(repeated.record, first.record)
const experimentalPackage = join(root, 'experimental-source', 'package')
await mkdir(join(experimentalPackage, 'lib'), { recursive: true })
await writeFile(join(experimentalPackage, 'package.json'), JSON.stringify({ name: '@deepseek-ai/dsh', version: '0.1.0-rc.9' }))
await writeFile(join(experimentalPackage, 'lib', 'bin.js'), '#!/usr/bin/env node\n')
const experimentalArchive = join(root, 'experimental.tgz')
const experimentalPacked = spawnSync('tar', [
'-czf', experimentalArchive, '-C', join(root, 'experimental-source'), 'package',
], { encoding: 'utf8' })
assert.equal(experimentalPacked.status, 0, experimentalPacked.stderr)
await mkdir(paths.viewsRoot, { recursive: true })
await symlink(first.assets.environment.path, join(paths.viewsRoot, 'environment'), 'dir')
const { id: _stableId, ...developmentContent } = first.record
developmentContent.authority = 'development'
developmentContent.targetSequence = 0
const developmentRecord = { ...developmentContent, id: deriveRecordId('deployment-record', developmentContent) }
const experimental = await builder.buildExperimental({
version: '0.1.0-rc.9',
receipt: {
path: experimentalArchive,
objectSha256: createHash('sha256').update(await readFile(experimentalArchive)).digest('hex'),
},
}, developmentRecord, ['stable-receipt', 'experimental-receipt'], { targetSequence: 11 })
assert.equal(experimental.record.authority, 'experimental')
assert.equal(experimental.record.targetSequence, 11)
assert.equal(experimental.record.dshVersion, '0.1.0-rc.9')
assert.deepEqual(experimental.record.environment, first.record.environment)
assert.deepEqual(experimental.record.systemPlugins, first.record.systemPlugins)
assert.notEqual(experimental.record.pristine.id, first.record.pristine.id)
assert.notEqual(experimental.record.runtime.id, first.record.runtime.id)
assert.deepEqual(experimental.record.receiptTokens, ['stable-receipt', 'experimental-receipt'])
})
test('retries a partially published npm dependency graph in fresh install roots', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-managed-store-retry-'))
const paths = new PlatformPaths(join(root, 'data'), join(root, 'run'))
await preparePersistentLayout(paths)
const packageRoot = join(root, 'source', 'package')
await mkdir(packageRoot, { recursive: true })
await writeFile(join(packageRoot, 'package.json'), JSON.stringify({
name: '@deepseek-ai/dsh', version: '0.1.5-rc.1',
}))
const archive = join(root, 'dsh.tgz')
const packed = spawnSync('tar', ['-czf', archive, '-C', join(root, 'source'), 'package'], { encoding: 'utf8' })
assert.equal(packed.status, 0, packed.stderr)
const attempts = []
const delays = []
const builder = new ManagedDeploymentBuilder({
paths,
sleep: async milliseconds => delays.push(milliseconds),
npmInstall: async (installRoot, _archivePath, options) => {
attempts.push({ installRoot, options })
if (attempts.length === 1) {
throw new Error('npm error code E404\nnpm error 404 A dependency package is not published yet.')
}
if (attempts.length === 2) {
throw new Error('npm error code ETARGET\nnpm error notarget No matching version found for a dependency.')
}
if (attempts.length === 3) {
throw new Error('npm error code ENOVERSIONS\nnpm error No versions available for a dependency.')
}
const installed = join(installRoot, 'lib', 'node_modules', '@deepseek-ai', 'dsh')
await mkdir(installed, { recursive: true })
await writeFile(join(installed, 'package.json'), JSON.stringify({
name: '@deepseek-ai/dsh', version: '0.1.5-rc.1',
}))
},
})
const pristine = await builder.pristine('0.1.5-rc.1', {
path: archive,
objectSha256: createHash('sha256').update(await readFile(archive)).digest('hex'),
})
assert.equal(attempts.length, 4)
assert.equal(new Set(attempts.map(attempt => attempt.installRoot)).size, 4)
assert.deepEqual(attempts.map(attempt => attempt.options.preferOnline), [false, true, true, true])
assert.deepEqual(delays, [5_000, 10_000, 20_000])
assert.equal(JSON.parse(await readFile(join(pristine.path, 'package.json'))).version, '0.1.5-rc.1')
let permanentAttempts = 0
const permanent = new ManagedDeploymentBuilder({
paths,
sleep: async () => assert.fail('a permanent npm failure must not wait for a retry'),
npmInstall: async () => {
permanentAttempts += 1
throw new Error('npm error code EACCES\nnpm error permission denied')
},
})
await assert.rejects(permanent.pristine('0.1.5-rc.1', {
path: archive,
objectSha256: 'permanent-error-fixture',
}), /EACCES/)
assert.equal(permanentAttempts, 1)
})
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,35 @@
import assert from 'node:assert/strict'
import { mkdtemp, mkdir, readFile, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { applyPatch, verifyPatch, NATIVE_CORNERS_RELATIVE_TARGET } from '../../environment/resources/patches/native-corners.mjs'
const BEFORE = 'var corner_shape_css_default = "@supports (corner-shape:superellipse(1.5)){:root{--dsw-corner-shape:superellipse(1.5)}*,:before,:after{corner-shape:var(--dsw-corner-shape)}}";'
async function fixture(source = BEFORE) {
const root = await mkdtemp(join(tmpdir(), 'dsh-native-corners-'))
const target = join(root, NATIVE_CORNERS_RELATIVE_TARGET)
await mkdir(join(root, 'node_modules/@deepseek-ai/dsh-client-ui-theme/lib'), { recursive: true })
await writeFile(target, source + '\n')
return { root, target }
}
test('removes the image-injected global superellipse rule exactly once', async () => {
const { root, target } = await fixture()
applyPatch(root)
verifyPatch(root)
assert.equal(await readFile(target, 'utf8'), 'var corner_shape_css_default = "";\n')
assert.throws(() => applyPatch(root), /found 0/)
})
test('fails closed when the upstream theme anchor changes', async () => {
const { root } = await fixture('var corner_shape_css_default = "changed";')
assert.throws(() => applyPatch(root), /found 0/)
})
test('allows upstream packages without the optional theme module', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-native-corners-absent-'))
assert.doesNotThrow(() => applyPatch(root))
assert.doesNotThrow(() => verifyPatch(root))
})
@@ -0,0 +1,57 @@
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import test from 'node:test'
import { parseOfficialDshPolicy } from '../lib/contracts.mjs'
import { verifyOfficialDshCandidate } from '../stage0/lib/official-dsh.mjs'
import { officialDshPolicy, registryCandidate, registryKeyPair } from './helpers.mjs'
const candidate = {
schema: 1,
name: '@deepseek-ai/dsh',
version: '0.1.0-rc.7',
dist: {
integrity: 'sha512-ZceDCJ8FAywih+USW/OMk9jEhunlvJBGEz4kqrhau23hPzbciOazZrywH0nBRsaalSeAJ1JGBmjtw4OSjToStw==',
tarball: 'https://registry.npmjs.org/@deepseek-ai/dsh/-/dsh-0.1.0-rc.7.tgz',
signatures: [{
keyid: 'SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U',
sig: 'MEUCIQD2wLkZaghGyVZ5rWsOZdS2ue69pTRoGlnAEXvOk/zyOwIgRG6dOCTpAaSYCoYR7PXG6bB/O5ET57rGkBWJSVpvBkQ=',
}],
},
}
test('verifies the official npm Registry signature delegated by Stable metadata', async () => {
const policy = parseOfficialDshPolicy(JSON.parse(await readFile(
new URL('../../../release/official-dsh-policy.json', import.meta.url),
)))
assert.equal(verifyOfficialDshCandidate(candidate, candidate.version, policy).version, candidate.version)
assert.throws(() => verifyOfficialDshCandidate({
...candidate,
dist: { ...candidate.dist, integrity: candidate.dist.integrity.replace('Zce', 'Ace') },
}, candidate.version, policy), { code: 'TRUST_UNKNOWN_KEY' })
assert.throws(() => verifyOfficialDshCandidate(
candidate, candidate.version, { ...policy, keys: [] },
), { code: 'TRUST_UNKNOWN_KEY' })
})
test('accepts an exact requested version and rejects noncanonical npm tarball origins', async () => {
const policy = parseOfficialDshPolicy(JSON.parse(await readFile(
new URL('../../../release/official-dsh-policy.json', import.meta.url),
)))
assert.equal(verifyOfficialDshCandidate(candidate, candidate.version, policy).version, candidate.version)
assert.throws(() => verifyOfficialDshCandidate(candidate, '0.1.0-rc.8', policy), /different version/)
assert.throws(() => verifyOfficialDshCandidate({
...candidate,
dist: { ...candidate.dist, tarball: 'https://mirror.example/dsh.tgz' },
}, candidate.version, policy), /canonical/)
})
test('rejects candidates after a delegated npm Registry key expires', () => {
const registry = registryKeyPair()
const policy = parseOfficialDshPolicy(officialDshPolicy(registry, '2026-08-18T00:00:00.000Z'))
assert.throws(() => verifyOfficialDshCandidate(
registryCandidate(registry),
'0.1.0-rc.8',
policy,
new Date('2026-08-19T00:00:00.000Z'),
), { code: 'TRUST_UNKNOWN_KEY' })
})
@@ -0,0 +1,170 @@
import assert from 'node:assert/strict'
import { mkdtemp, mkdir, readFile, readdir, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { spawnSync } from 'node:child_process'
import test from 'node:test'
import { parseComponentManifest, parseEnvironmentManifest } from '../lib/contracts.mjs'
import {
materializeSystemSkillCatalog,
readSystemSkillCatalog,
retainLegacySystemSkillCatalog,
} from '../../control-plane/modules/skill-manager/index.mjs'
const platformRoot = dirname(dirname(fileURLToPath(import.meta.url)))
const containerRoot = dirname(platformRoot)
const definition = join(containerRoot, 'environment', 'definition.json')
const tool = join(platformRoot, 'tools', 'package-environment.mjs')
async function build() {
const root = await mkdtemp(join(tmpdir(), 'dsh-environment-package-'))
const output = join(root, 'output')
const result = spawnSync(process.execPath, [tool, definition, output], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
return output
}
test('packages the initial Environment deterministically from real resources', async () => {
const first = await build()
const second = await build()
const firstManifest = await readFile(join(first, 'environment.manifest.json'))
const secondManifest = await readFile(join(second, 'environment.manifest.json'))
assert.deepEqual(firstManifest, secondManifest)
const manifest = parseEnvironmentManifest(firstManifest)
assert.deepEqual(manifest.components.map(component => component.id), ['dsh-runtime'])
assert.deepEqual(manifest.patches.map(patch => patch.id), [
'directory-picker', 'browser-loopback', 'managed-lifecycle', 'native-corners',
])
assert.deepEqual(manifest.systemPlugins.map(plugin => plugin.id), [
'platform-management', 'settings-navigation', 'settings-document-editor',
])
for (const reference of [...manifest.components, ...manifest.patches, ...manifest.systemPlugins]) {
assert.deepEqual(Object.keys(reference).sort(), ['id', 'sha256'])
}
assert.deepEqual(await readdir(join(first, 'artifacts')), await readdir(join(second, 'artifacts')))
const skillArtifact = manifest.artifacts.find(artifact => artifact.id === 'system-skill-catalog')
assert.equal(skillArtifact?.mediaType, 'application/vnd.dsh-platform.system-skill-catalog.v1+tar+gzip')
const skillArchive = join(first, 'artifacts', 'system-skill-catalog')
const skillListing = spawnSync('tar', ['-tzf', skillArchive], { encoding: 'utf8' })
assert.equal(skillListing.status, 0, skillListing.stderr)
assert.match(skillListing.stdout, /^skills\/catalog\.json$/m)
assert.match(skillListing.stdout, /^skills\/dsh-docker-operations\/SKILL\.md$/m)
const navigationArchive = join(first, 'artifacts', 'system-plugin-settings-navigation')
const listing = spawnSync('tar', ['-tzf', navigationArchive], { encoding: 'utf8' })
assert.equal(listing.status, 0, listing.stderr)
assert.match(listing.stdout, /^package\/lib\/client\.bundle\.js$/m)
assert.match(listing.stdout, /^package\/cordis\.patch\.json$/m)
})
test('can emit flat Artifact URLs for GitHub Release assets', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-environment-flat-'))
const output = join(root, 'output')
const result = spawnSync(process.execPath, [
tool, definition, output, '1', '1', 'https://release.example/platform-1/', 'flat',
], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
const manifest = parseEnvironmentManifest(await readFile(join(output, 'environment.manifest.json')))
assert.equal(manifest.artifacts.every(artifact => artifact.url.startsWith('https://release.example/platform-1/')), true)
assert.equal(manifest.artifacts.every(artifact => !artifact.url.includes('/artifacts/')), true)
})
test('materializes the verified System Skill catalog from an Environment Artifact', async () => {
const environmentRoot = await build()
const outputRoot = await mkdtemp(join(tmpdir(), 'dsh-system-skill-catalogs-'))
const materialized = await materializeSystemSkillCatalog({ environmentRoot, outputRoot })
const catalog = await readSystemSkillCatalog(materialized)
assert.deepEqual(catalog.map(skill => skill.id), ['dsh-docker-operations'])
await writeFile(join(environmentRoot, 'artifacts', 'system-skill-catalog'), 'tampered')
await assert.rejects(
materializeSystemSkillCatalog({ environmentRoot, outputRoot }),
/differs from its Environment manifest/,
)
})
test('retains a legacy Bootstrap System Skill catalog without reading Stage-0 state', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-legacy-system-skill-'))
const source = join(root, 'seed', 'bootstrap', '1.0.9', 'control-plane', 'skills')
const skill = join(source, 'legacy-skill')
const output = join(root, 'environments', 'versions', '.system-skill-catalogs')
await mkdir(skill, { recursive: true })
await writeFile(join(source, 'catalog.json'), JSON.stringify({
schema: 1,
skills: [{
id: 'legacy-skill',
source: 'legacy-skill',
description: { en: 'Legacy skill', zh: 'Legacy skill' },
}],
}))
await writeFile(join(skill, 'SKILL.md'), '---\nname: legacy-skill\ndescription: Legacy skill\n---\n')
const retained = await retainLegacySystemSkillCatalog({ sourceRoot: source, outputRoot: output })
assert.equal(retained, join(output, 'legacy-bootstrap'))
assert.deepEqual((await readSystemSkillCatalog(retained)).map(entry => entry.id), ['legacy-skill'])
assert.equal(await retainLegacySystemSkillCatalog({ sourceRoot: join(root, 'missing'), outputRoot: output }), retained)
const bootstrap = await readFile(new URL('../bootstrap/index.mjs', import.meta.url), 'utf8')
assert.match(bootstrap, /inventory\.bootstrap\.id, 'control-plane', 'skills'/)
assert.doesNotMatch(bootstrap, /bootstrapStateRoot/)
})
test('checked-in Component manifests satisfy the public contract', async () => {
for (const path of [
join(containerRoot, 'control-plane', 'services', 'gateway', 'component.json'),
join(containerRoot, 'environment', 'dsh-runtime', 'component.json'),
join(containerRoot, 'control-plane', 'services', 'management', 'component.json'),
join(containerRoot, 'control-plane', 'hooks', 'recovery', 'component.json'),
]) {
const bytes = await readFile(path)
const component = parseComponentManifest(bytes)
assert.equal(Object.hasOwn(component, 'version'), false)
}
})
test('DSH Runtime uses the generic node home environment without package-manager overrides', async () => {
const component = parseComponentManifest(await readFile(
join(containerRoot, 'environment', 'dsh-runtime', 'component.json'),
))
assert.deepEqual(component.environment, {
DSH_BUNDLED_SKILL_DIR: '/run/dsh-platform/views/skills',
HOME: '/home/node',
LOGNAME: 'node',
USER: 'node',
XDG_CACHE_HOME: '/home/node/.cache',
XDG_CONFIG_HOME: '/home/node/.config',
XDG_DATA_HOME: '/home/node/.local/share',
})
assert.doesNotMatch(JSON.stringify(component.environment), /NPM_CONFIG_CACHE|PNPM_HOME/)
})
test('container images opt into the managed DSH lifecycle without exposing a user setting', async () => {
const dockerfile = await readFile(join(dirname(containerRoot), 'Dockerfile'), 'utf8')
assert.match(dockerfile, /ENV DSH_PLATFORM_DATA=\/data\/platform \\\n+ DSH_PLATFORM_MANAGED=1 \\\n+ DSH_PLATFORM_RUN=\/run\/dsh-platform \\/)
})
test('platform CLI shim follows the current verified Bootstrap view', async () => {
const dockerfile = await readFile(join(dirname(containerRoot), 'Dockerfile'), 'utf8')
const shim = await readFile(join(platformRoot, 'tools', 'dsh-platform-shim.sh'), 'utf8')
assert.match(dockerfile, /COPY container\/platform\/tools\/dsh-platform-shim\.sh \/usr\/local\/bin\/dsh-platform/)
assert.match(shim, /views\/bootstrap\/control-plane\/services\/management\/dsh-platform\.mjs/)
assert.match(shim, /opt\/dsh-platform\/runtime\/control-plane\/services\/management\/dsh-platform\.mjs/)
assert.ok(shim.indexOf('managed_cli') < shim.indexOf('image_cli'))
})
test('packager rejects source escapes and duplicate Artifact IDs without publishing output', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-environment-invalid-'))
const original = JSON.parse(await readFile(definition, 'utf8'))
original.components[0].source = '../../../../README.md'
original.components.push({ ...original.components[0] })
const invalid = join(dirname(definition), `invalid-${process.pid}.json`)
const output = join(root, 'output')
try {
await writeFile(invalid, JSON.stringify(original))
const result = spawnSync(process.execPath, [tool, invalid, output], { encoding: 'utf8' })
assert.notEqual(result.status, 0)
await assert.rejects(readdir(output), { code: 'ENOENT' })
} finally {
await import('node:fs/promises').then(({ rm }) => rm(invalid, { force: true }))
}
})
@@ -0,0 +1,82 @@
import assert from 'node:assert/strict'
import { lstat, mkdir, mkdtemp, readFile, readlink, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import {
PlatformPaths,
preparePersistentLayout,
rejectLegacyLayout,
resetRuntimeLayout,
trustStateRootForAuthority,
} from '../lib/paths.mjs'
test('creates state, store, cache, and logs without persistent runtime views', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-layout-'))
const paths = new PlatformPaths(join(root, 'platform'), join(root, 'run'))
await preparePersistentLayout(paths)
assert.equal(paths.developmentTrustStateRoot, join(paths.trustStateRoot, 'development'))
assert.equal(paths.proxyStateRoot, join(paths.stateRoot, 'proxy'))
assert.equal(paths.proxyTestStatePath, join(paths.proxyStateRoot, 'test-tasks.json'))
assert.equal(paths.accessStateRoot, join(paths.stateRoot, 'access'))
assert.equal(paths.accessSocket, join(paths.runRoot, 'access', 'access.sock'))
assert.equal(paths.accessRecoverySocket, join(paths.runRoot, 'access', 'recovery.sock'))
assert.equal(paths.accessLaunchSocket, join(paths.runRoot, 'access-launch.sock'))
assert.equal(paths.proxyProviderInventoryPath, join(paths.managementStateRoot, 'proxy-provider-inventory.json'))
for (const path of [
paths.trustStateRoot, paths.bootstrapStateRoot, paths.deploymentStateRoot, paths.updaterStateRoot,
paths.managementStateRoot,
paths.objectsRoot, paths.bootstrapStoreRoot, paths.environmentsRoot, paths.pristineRoot,
paths.runtimesRoot, paths.systemPluginsRoot, paths.snapshotsRoot, paths.userPluginSnapshotsRoot,
paths.downloadsRoot, paths.npmCacheRoot, paths.logsRoot,
]) assert.equal((await lstat(path)).isDirectory(), true)
assert.equal(paths.systemSkillCatalogsRoot, join(paths.environmentsRoot, 'versions', '.system-skill-catalogs'))
await assert.rejects(lstat(paths.systemSkillCatalogsRoot), { code: 'ENOENT' })
await assert.rejects(lstat(paths.runRoot), { code: 'ENOENT' })
})
test('isolates development trust without changing the formal trust ledger', () => {
const paths = new PlatformPaths('/data/platform', '/run/dsh-platform')
assert.equal(trustStateRootForAuthority(paths, 'development'), paths.developmentTrustStateRoot)
assert.equal(trustStateRootForAuthority(paths, 'stable'), paths.trustStateRoot)
})
test('rejects legacy platform volumes without changing DSH user data', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-legacy-layout-'))
const dataRoot = join(root, 'platform')
const dshRoot = join(root, 'dsh')
const paths = new PlatformPaths(dataRoot, join(root, 'run'))
await mkdir(join(dataRoot, 'runtime'), { recursive: true })
await mkdir(dshRoot)
await writeFile(join(dshRoot, 'sentinel'), 'user-data')
await assert.rejects(rejectLegacyLayout(paths), error => (
/clear only .*platform/.test(error.message) && /Do not delete \/data\/dsh/.test(error.message)
))
assert.equal(await readFile(join(dshRoot, 'sentinel'), 'utf8'), 'user-data')
})
test('rebuilds only the ephemeral run directory', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-run-layout-'))
const paths = new PlatformPaths(join(root, 'platform'), join(root, 'run'))
await preparePersistentLayout(paths)
await mkdir(paths.runRoot)
await writeFile(join(paths.runRoot, 'stale.sock'), 'stale')
await writeFile(join(paths.updaterStateRoot, 'sentinel'), 'persistent')
await resetRuntimeLayout(paths)
await assert.rejects(lstat(join(paths.runRoot, 'stale.sock')), { code: 'ENOENT' })
assert.equal((await lstat(paths.viewsRoot)).isDirectory(), true)
assert.equal((await lstat(paths.deploymentViewsRoot)).isDirectory(), true)
assert.equal(await readlink(join(paths.viewsRoot, 'runtime')), join('..', 'deployment', 'runtime'))
assert.equal(await readlink(join(paths.viewsRoot, 'environment')), join('..', 'deployment', 'environment'))
assert.equal(await readlink(join(paths.viewsRoot, 'system-plugins')), join('..', 'system-plugin-views', 'current'))
assert.equal((await lstat(paths.systemPluginViewsRoot)).isDirectory(), true)
assert.equal(paths.dshLifecycleSocket, join(paths.runRoot, 'dsh-lifecycle.sock'))
assert.equal(paths.proxyControlSocket, join(paths.runRoot, 'outbound-proxy.sock'))
assert.equal(paths.proxyRoutingStatePath, join(paths.runRoot, 'outbound-proxy-routing.json'))
assert.equal(paths.proxyLaunchSocket, join(paths.runRoot, 'proxy-launch.sock'))
assert.equal(
await readlink(join(paths.systemPluginViewsRoot, 'current')),
join('..', 'deployment', 'system-plugins'),
)
assert.equal(await readFile(join(paths.updaterStateRoot, 'sentinel'), 'utf8'), 'persistent')
})
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,424 @@
import assert from 'node:assert/strict'
import { chmod, mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises'
import { spawn } from 'node:child_process'
import { once } from 'node:events'
import { createServer, request } from 'node:http'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import {
defaultProxyConfiguration,
PROXY_PORTS,
sanitizedProxyConfiguration,
validateProxyConfiguration,
} from '../../control-plane/services/outbound-proxy/lib/contracts.mjs'
import { ProxyConfigurationError, proxyErrorBody } from '../../control-plane/services/outbound-proxy/lib/errors.mjs'
import {
matchesProxyRules,
noProxyEnvironment,
normalizeProxyRules,
} from '../../control-plane/services/outbound-proxy/lib/rules.mjs'
import { ProxyConfigurationStore } from '../../control-plane/services/outbound-proxy/lib/store.mjs'
import { PROXY_SCOPE_CATALOG } from '../../control-plane/services/outbound-proxy/lib/scope-catalog.mjs'
import { createOutboundProxyControl } from '../../control-plane/services/outbound-proxy/lib/control.mjs'
import { OutboundProxyControlClient } from '../../control-plane/services/management/outbound-proxy-client.mjs'
function configured(overrides = {}) {
const defaults = defaultProxyConfiguration()
return {
...defaults,
enabled: true,
proxy: {
...defaults.proxy,
host: 'Proxy.Example.COM.',
port: 1080,
username: 'user:@/',
password: 'secret:@/',
...overrides.proxy,
},
scopes: { ...defaults.scopes, updates: true, ...overrides.scopes },
environment: { ...defaults.environment, ...overrides.environment },
modelApi: { ...defaults.modelApi, providers: {}, ...overrides.modelApi },
noProxy: { user: ['GOOGLE.com.', '.Example.COM', 'google.com'], ...overrides.noProxy },
bypass: { additional: ['192.168.1.0/24', '[::1]:8443'], ...overrides.bypass },
}
}
async function listen(server) {
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(0, '127.0.0.1', resolve)
})
return server.address().port
}
function exchange(port, method, path, body) {
return new Promise((resolve, reject) => {
const bytes = body === undefined ? undefined : Buffer.from(JSON.stringify(body))
const outgoing = request({
host: '127.0.0.1', port, method, path,
headers: bytes === undefined ? {} : { 'content-type': 'application/json', 'content-length': bytes.byteLength },
}, response => {
const chunks = []
response.on('data', chunk => chunks.push(chunk))
response.on('end', () => resolve({
status: response.statusCode,
body: JSON.parse(Buffer.concat(chunks).toString('utf8')),
}))
})
outgoing.once('error', reject)
outgoing.end(bytes)
})
}
test('normalizes strict proxy configuration without exposing a password', () => {
const { configuration, credentials } = validateProxyConfiguration(configured())
assert.equal(configuration.proxy.host, 'proxy.example.com')
assert.equal(configuration.proxy.passwordConfigured, true)
assert.equal('password' in configuration.proxy, false)
assert.deepEqual(configuration.noProxy.user, ['.example.com', 'google.com'])
assert.deepEqual(configuration.bypass.additional, ['[::1]:8443', '192.168.1.0/24'])
assert.equal(credentials.password, 'secret:@/')
assert.equal(Object.isFrozen(configuration.proxy), true)
assert.equal(Object.isFrozen(configuration.scopes), true)
assert.deepEqual(PROXY_PORTS, {
updates: 17891, platform: 17892, dshCore: 17893, dshPlugins: 17894,
agentNetwork: 17895, managementTerminal: 17896, modelApi: 17897, sharedDsh: 17898,
})
const view = sanitizedProxyConfiguration(configuration, 'revision')
assert.equal(JSON.stringify(view).includes('secret'), false)
})
test('rejects unknown schemas, fields, wildcards, URLs, incomplete endpoints, and CIDR in NO_PROXY', () => {
for (const value of [
{ ...configured(), schema: 2 },
{ ...configured(), unexpected: true },
configured({ proxy: { host: '', port: 1080 } }),
configured({ noProxy: { user: ['*.example.com'] } }),
configured({ noProxy: { user: ['https://example.com'] } }),
configured({ noProxy: { user: ['192.168.0.0/16'] } }),
]) assert.throws(() => validateProxyConfiguration(value), ProxyConfigurationError)
})
test('enforces SOCKS5 credential byte limits without rejecting valid UTF-8 credentials', () => {
const boundary = validateProxyConfiguration(configured({ proxy: {
username: 'u'.repeat(255), password: 'p'.repeat(255), protocol: 'socks5',
} }))
assert.equal(Buffer.byteLength(boundary.credentials.username), 255)
assert.equal(Buffer.byteLength(boundary.credentials.password), 255)
assert.equal(validateProxyConfiguration(configured({ proxy: {
username: '代理', password: '密钥', protocol: 'socks5',
} })).credentials.username, '代理')
assert.throws(() => validateProxyConfiguration(configured({ proxy: { username: 'u'.repeat(256) } })), ProxyConfigurationError)
assert.throws(() => validateProxyConfiguration(configured({ proxy: { password: 'p'.repeat(256) } })), ProxyConfigurationError)
})
test('matches exact, suffix, port, IPv6, CIDR, wildcard, and domain boundaries', () => {
const noProxy = normalizeProxyRules(['example.com', '.example.net', 'service.local:8443', '*'], { allowWildcard: true })
assert.equal(matchesProxyRules(noProxy, 'anything.invalid', 443), true)
const precise = normalizeProxyRules(['example.com', '.example.net', 'service.local:8443'])
assert.equal(matchesProxyRules(precise, 'example.com', 443), true)
assert.equal(matchesProxyRules(precise, 'sub.example.com', 443), false)
assert.equal(matchesProxyRules(precise, 'example.net', 443), true)
assert.equal(matchesProxyRules(precise, 'sub.example.net.', 443), true)
assert.equal(matchesProxyRules(precise, 'badexample.net', 443), false)
assert.equal(matchesProxyRules(precise, 'service.local', 8443), true)
assert.equal(matchesProxyRules(precise, 'service.local', 443), false)
const bypass = normalizeProxyRules(['192.168.0.0/16', 'fd00::/8', '[::1]:8443'], { allowCidr: true })
assert.equal(matchesProxyRules(bypass, '192.168.1.4', 80), true)
assert.equal(matchesProxyRules(bypass, '192.169.1.4', 80), false)
assert.equal(matchesProxyRules(bypass, 'fd00::1', 80), true)
assert.equal(matchesProxyRules(bypass, '::1', 8443), true)
assert.equal(noProxyEnvironment(['.example.com']), '::1,127.0.0.1,localhost,.example.com')
})
test('atomically stores revisions, preserves and clears write-only credentials, and rejects stale writes', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-proxy-state-'))
const store = new ProxyConfigurationStore(root, { now: () => new Date('2026-08-25T00:00:00.000Z') })
const initial = await store.load()
assert.match(initial.revision, /^[A-Za-z0-9_-]{43}$/)
const saved = await store.commit({ baseRevision: initial.revision, value: configured() })
assert.equal(saved.credentials.password, 'secret:@/')
assert.equal((await stat(root)).mode & 0o777, 0o700)
assert.equal((await stat(join(root, 'revisions', saved.revision, 'credentials.json'))).mode & 0o777, 0o600)
assert.equal((await readFile(join(root, 'revisions', saved.revision, 'config.json'), 'utf8')).includes('secret'), false)
const preserving = configured({ proxy: { password: null } })
const preserved = await store.commit({ baseRevision: saved.revision, value: preserving })
assert.equal(preserved.credentials.password, 'secret:@/')
const clearing = configured({ proxy: { password: null, clearPassword: true } })
const cleared = await store.commit({ baseRevision: preserved.revision, value: clearing })
assert.equal(cleared.credentials.password, null)
assert.equal(cleared.configuration.proxy.passwordConfigured, false)
await assert.rejects(store.commit({ baseRevision: saved.revision, value: configured() }), error => (
error.code === 'REVISION_CONFLICT' && error.statusCode === 409
))
})
test('control API returns only sanitized configuration and hot-activates one current revision', async t => {
const root = await mkdtemp(join(tmpdir(), 'dsh-proxy-control-'))
const store = new ProxyConfigurationStore(root)
let snapshot = Object.freeze({ ...await store.load(), recovery: 'none' })
let handlesCleared = 0
const server = createOutboundProxyControl({
getSnapshot: () => snapshot,
routeHealth: { status: current => ({ updates: current.configuration.scopes.updates ? 'unknown' : 'direct' }) },
providerHandles: { clear: () => { handlesCleared += 1 } },
commitConfiguration: async update => {
snapshot = Object.freeze({ ...await store.commit(update), recovery: 'none' })
return snapshot
},
})
const port = await listen(server)
t.after(() => new Promise(resolve => server.close(resolve)))
const initial = await exchange(port, 'GET', '/v1/configuration')
assert.equal(initial.status, 200)
assert.equal(initial.body.componentReady, true)
assert.equal(initial.body.enabled, false)
assert.equal('credentials' in initial.body, false)
const activated = await exchange(port, 'PUT', '/v1/configuration', {
baseRevision: initial.body.revision,
value: configured(),
})
assert.equal(activated.status, 200)
assert.equal(activated.body.enabled, true)
assert.equal(activated.body.proxy.passwordConfigured, true)
assert.equal(JSON.stringify(activated.body).includes('secret:@/'), false)
assert.equal(snapshot.credentials.password, 'secret:@/')
assert.equal(handlesCleared, 1)
const stale = await exchange(port, 'PUT', '/v1/configuration', {
baseRevision: initial.body.revision,
value: configured(),
})
assert.equal(stale.status, 409)
assert.equal(stale.body.error.code, 'REVISION_CONFLICT')
assert.equal(snapshot.revision, activated.body.revision)
})
test('control API accepts dynamic model Provider routing before persistence', async t => {
const current = Object.freeze({
revision: 'revision-one', recovery: 'none',
...validateProxyConfiguration(defaultProxyConfiguration()),
})
let committed = false
const server = createOutboundProxyControl({
getSnapshot: () => current,
routeHealth: { status: () => ({}) },
commitConfiguration: async request => {
committed = true
return Object.freeze({ revision: 'revision-two', recovery: 'none', ...validateProxyConfiguration(request.value) })
},
})
const port = await listen(server)
t.after(() => new Promise(resolve => server.close(resolve)))
const value = configured({ modelApi: { providers: { unsupported: { proxyEnabled: true } } } })
const result = await exchange(port, 'PUT', '/v1/configuration', { baseRevision: current.revision, value })
assert.equal(result.status, 200)
assert.deepEqual(result.body.modelApi.providers.unsupported, { proxyEnabled: true })
assert.equal(committed, true)
})
test('rejects unreleased legacy Provider policy shapes', () => {
assert.throws(() => validateProxyConfiguration(configured({
modelApi: { default: 'direct', providers: {} },
})), /must be an object/)
assert.throws(() => validateProxyConfiguration(configured({
modelApi: { default: { proxyEnabled: false }, providers: { legacy: { followDsh: true, proxyEnabled: true } } },
})), /unsupported fields: followDsh/)
})
test('Management outbound proxy client preserves structured control errors', async t => {
const root = await mkdtemp(join(tmpdir(), 'dsh-proxy-client-'))
const socket = join(root, 'control.sock')
const server = createServer((_request, response) => {
response.writeHead(409, { 'content-type': 'application/json' })
response.end(JSON.stringify({ error: {
code: 'REVISION_CONFLICT', message: 'proxy configuration changed', stage: 'activate', retryable: true,
} }))
})
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(socket, resolve)
})
t.after(() => new Promise(resolve => server.close(resolve)))
const client = new OutboundProxyControlClient(socket)
await assert.rejects(client.updateConfiguration({}), error => (
error.statusCode === 409 && error.code === 'REVISION_CONFLICT'
&& error.stage === 'activate' && error.retryable === true
))
})
test('Management outbound proxy client redacts unavailable control sockets', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-proxy-client-unavailable-'))
const client = new OutboundProxyControlClient(join(root, 'missing.sock'), { timeoutMs: 100 })
await assert.rejects(client.configuration(), error => (
error.statusCode === 503
&& error.code === 'PROXY_MANAGER_UNAVAILABLE'
&& error.stage === 'control'
&& error.retryable === true
&& !error.message.includes(root)
&& !error.message.includes('missing.sock')
))
})
test('recovers the newest intact immutable revision when current state is corrupt', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-proxy-recovery-'))
let tick = 0
const store = new ProxyConfigurationStore(root, { now: () => new Date(Date.UTC(2026, 7, 25, 0, 0, tick++)) })
const initial = await store.load()
const first = await store.commit({ baseRevision: initial.revision, value: configured() })
const second = await store.commit({ baseRevision: first.revision, value: configured({ proxy: { port: 1081 } }) })
await writeFile(join(root, 'revisions', second.revision, 'config.json'), '{}\n')
const recovered = await store.load()
assert.equal(recovered.revision, first.revision)
assert.equal(JSON.parse(await readFile(join(root, 'current.json'), 'utf8')).revision, first.revision)
})
test('returns stable redacted error structures', () => {
const body = proxyErrorBody(new ProxyConfigurationError('proxy configuration changed', {
code: 'REVISION_CONFLICT', statusCode: 409, stage: 'activate',
}))
assert.deepEqual(body, {
error: { code: 'REVISION_CONFLICT', message: 'proxy configuration changed', stage: 'activate', retryable: false },
})
})
test('recovers a corrupt pointer and safely disables proxy when every revision is corrupt', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-proxy-reset-'))
const store = new ProxyConfigurationStore(root)
const initial = await store.load()
await writeFile(join(root, 'revisions', initial.revision, 'credentials.json'), '{broken')
await writeFile(join(root, 'current.json'), '{broken')
const recovered = await store.load()
assert.equal(recovered.recovery, 'reset-disabled')
assert.equal(recovered.configuration.enabled, false)
assert.equal(recovered.configuration.proxy.passwordConfigured, false)
assert.equal((await readFile(join(root, 'revisions', initial.revision, 'credentials.json'), 'utf8')), '{broken')
const names = await import('node:fs/promises').then(({ readdir }) => readdir(root))
assert.equal(names.some(name => name.startsWith('current.corrupt.')), true)
})
test('publishes one complete bilingual proxy scope catalog', () => {
assert.equal(PROXY_SCOPE_CATALOG.schema, 1)
assert.equal(PROXY_SCOPE_CATALOG.entries.length, 24)
assert.equal(new Set(PROXY_SCOPE_CATALOG.entries.map(entry => entry.id)).size, 24)
assert.deepEqual(new Set(PROXY_SCOPE_CATALOG.entries.map(entry => entry.group)), new Set(Object.keys(PROXY_SCOPE_CATALOG.groups)))
for (const entry of PROXY_SCOPE_CATALOG.entries) {
assert.equal(typeof entry.source.zh, 'string')
assert.equal(typeof entry.source.en, 'string')
assert.equal(typeof entry.detail.zh, 'string')
assert.equal(typeof entry.detail.en, 'string')
}
const pluginTerminal = PROXY_SCOPE_CATALOG.entries.find(entry => entry.id === 'plugin-terminal')
assert.match(pluginTerminal.detail.zh, /可识别子进程接口/)
assert.match(pluginTerminal.detail.zh, /共享 DSH/)
assert.match(pluginTerminal.detail.en, /identifiable subprocess interface/)
assert.match(pluginTerminal.detail.en, /shared DSH policy/)
})
test('keeps the Bootstrap proxy supervisor alive until an explicit stop', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-proxy-supervisor-'))
const runRoot = join(root, 'run')
const socket = join(runRoot, 'proxy-launch.sock')
await mkdir(runRoot)
let running = false
const calls = []
const server = createServer((request, response) => {
const chunks = []
request.on('data', chunk => chunks.push(chunk))
request.on('end', () => {
calls.push(`${request.method} ${request.url}`)
if (request.method === 'POST' && request.url === '/v1/start') running = true
if (request.method === 'POST' && request.url === '/v1/stop') running = false
response.writeHead(200, { 'content-type': 'application/json' })
response.end(JSON.stringify({ running, componentReady: running }))
})
})
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(socket, resolve)
})
const script = new URL('../../control-plane/services/outbound-proxy/supervisor.mjs', import.meta.url).pathname
const child = spawn(process.execPath, [script], {
env: {
...process.env,
DSH_PLATFORM_DATA: join(root, 'data'),
DSH_PLATFORM_RUN: runRoot,
DSH_PROXY_LAUNCH_TOKEN: 'x'.repeat(43),
},
stdio: ['ignore', 'pipe', 'pipe'],
})
try {
await new Promise((resolve, reject) => {
const deadline = setTimeout(() => reject(new Error('proxy supervisor did not start')), 3_000)
const check = setInterval(() => {
if (calls.includes('POST /v1/start')) {
clearInterval(check)
clearTimeout(deadline)
resolve()
}
}, 20)
})
await new Promise(resolve => setTimeout(resolve, 750))
assert.equal(child.exitCode, null)
child.kill('SIGTERM')
const [code] = await once(child, 'exit')
assert.equal(code, 0)
assert.equal(calls.includes('POST /v1/stop'), true)
} finally {
if (child.exitCode === null) child.kill('SIGKILL')
await new Promise(resolve => server.close(resolve))
await rm(root, { recursive: true, force: true })
}
})
test('stops the Bootstrap proxy supervisor cleanly after its launch socket disappears', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-proxy-supervisor-missing-socket-'))
const runRoot = join(root, 'run')
const socket = join(runRoot, 'proxy-launch.sock')
await mkdir(runRoot)
let started = false
const server = createServer((request, response) => {
request.resume()
request.on('end', () => {
if (request.method === 'POST' && request.url === '/v1/start') started = true
response.writeHead(200, { 'content-type': 'application/json' })
response.end(JSON.stringify({ running: true, componentReady: true }))
})
})
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(socket, resolve)
})
const script = new URL('../../control-plane/services/outbound-proxy/supervisor.mjs', import.meta.url).pathname
const child = spawn(process.execPath, [script], {
env: {
...process.env,
DSH_PLATFORM_DATA: join(root, 'data'),
DSH_PLATFORM_RUN: runRoot,
DSH_PROXY_LAUNCH_TOKEN: 'x'.repeat(43),
},
stdio: ['ignore', 'ignore', 'pipe'],
})
const stderr = []
child.stderr.on('data', chunk => stderr.push(chunk))
try {
const deadline = Date.now() + 3_000
while (!started && Date.now() < deadline) await new Promise(resolve => setTimeout(resolve, 20))
assert.equal(started, true)
await new Promise(resolve => setTimeout(resolve, 50))
await new Promise(resolve => server.close(resolve))
await rm(socket, { force: true })
child.kill('SIGTERM')
const [code] = await once(child, 'exit')
assert.equal(code, 0)
assert.equal(Buffer.concat(stderr).toString('utf8'), '')
} finally {
if (child.exitCode === null) child.kill('SIGKILL')
if (server.listening) await new Promise(resolve => server.close(resolve))
await rm(root, { recursive: true, force: true })
}
})
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,159 @@
import assert from 'node:assert/strict'
import { request } from 'node:http'
import test from 'node:test'
import { createOutboundProxyControl } from '../../control-plane/services/outbound-proxy/lib/control.mjs'
import {
OUTBOUND_PROXY_PORTS,
outboundProxyEnvironment,
outboundProxyScopeEnabled,
outboundProxyUrl,
parseOutboundProxyEnvironment,
} from '../lib/outbound-proxy.mjs'
function get(port, path) {
return new Promise((resolve, reject) => {
const outgoing = request({ host: '127.0.0.1', port, path }, response => {
const chunks = []
response.on('data', chunk => chunks.push(chunk))
response.once('end', () => resolve({
status: response.statusCode,
body: JSON.parse(Buffer.concat(chunks).toString('utf8')),
}))
})
outgoing.once('error', reject)
outgoing.end()
})
}
test('managed proxy environments use only fixed loopback entries', () => {
assert.equal(outboundProxyUrl('agentNetwork'), 'http://127.0.0.1:17895')
assert.deepEqual(Object.keys(OUTBOUND_PROXY_PORTS), [
'updates', 'platform', 'dshCore', 'dshPlugins', 'agentNetwork',
'managementTerminal', 'modelApi', 'sharedDsh',
])
const environment = outboundProxyEnvironment('managementTerminal', {
noProxy: ['localhost', '127.0.0.1', 'localhost', '.example.com'],
allProxy: true,
})
assert.deepEqual(environment, {
HTTP_PROXY: 'http://127.0.0.1:17896',
HTTPS_PROXY: 'http://127.0.0.1:17896',
http_proxy: 'http://127.0.0.1:17896',
https_proxy: 'http://127.0.0.1:17896',
NO_PROXY: 'localhost,127.0.0.1,.example.com',
no_proxy: 'localhost,127.0.0.1,.example.com',
ALL_PROXY: 'http://127.0.0.1:17896',
all_proxy: 'http://127.0.0.1:17896',
})
assert.deepEqual(parseOutboundProxyEnvironment(environment, 'managementTerminal'), environment)
assert.throws(() => parseOutboundProxyEnvironment({ ...environment, PASSWORD: 'secret' }, 'managementTerminal'), /unsupported fields/)
assert.throws(() => outboundProxyEnvironment('unknown'), /scope unknown is invalid/)
})
test('scope policy selects direct transport whenever the configured route is disabled', () => {
const direct = {
enabled: false,
scopes: { updates: true, platform: true, dshCore: true, dshPlugins: true, agentNetwork: true, managementTerminal: true },
modelApi: { default: { proxyEnabled: true }, providers: {} },
}
for (const scope of ['updates', 'platform', 'dshCore', 'dshPlugins', 'agentNetwork', 'managementTerminal', 'sharedDsh']) {
assert.equal(outboundProxyScopeEnabled(direct, scope), false, scope)
}
assert.equal(outboundProxyScopeEnabled(direct, 'modelApi', 'deepseek'), false)
const configured = {
...direct,
enabled: true,
scopes: { updates: true, platform: false, dshCore: false, dshPlugins: false, agentNetwork: true, managementTerminal: false },
modelApi: {
default: { proxyEnabled: true },
providers: {
direct: { proxyEnabled: false },
independent: { proxyEnabled: true },
},
},
}
assert.equal(outboundProxyScopeEnabled(configured, 'updates'), true)
assert.equal(outboundProxyScopeEnabled(configured, 'platform'), false)
assert.equal(outboundProxyScopeEnabled(configured, 'agentNetwork'), true)
assert.equal(outboundProxyScopeEnabled(configured, 'managementTerminal'), false)
assert.equal(outboundProxyScopeEnabled(configured, 'sharedDsh'), false)
assert.equal(outboundProxyScopeEnabled(configured, 'modelApi', 'direct'), false)
assert.equal(outboundProxyScopeEnabled(configured, 'modelApi', 'independent'), true)
assert.equal(outboundProxyScopeEnabled(configured, 'modelApi', 'default'), true)
configured.scopes.dshCore = true
assert.equal(outboundProxyScopeEnabled(configured, 'sharedDsh'), true)
assert.equal(outboundProxyScopeEnabled(configured, 'modelApi', 'default'), true)
})
test('outbound proxy control returns sanitized process environments', async () => {
const snapshot = Object.freeze({
revision: 'revision-one',
recovery: 'none',
credentials: Object.freeze({ username: 'alice', password: 'never-return-this' }),
configuration: Object.freeze({
noProxy: Object.freeze({ system: Object.freeze(['localhost', '127.0.0.1', '::1']), user: Object.freeze(['.example.com']) }),
environment: Object.freeze({ allProxy: 'scope-proxy' }),
enabled: true,
scopes: Object.freeze({ agentNetwork: true }),
modelApi: Object.freeze({ default: Object.freeze({ proxyEnabled: false }), providers: Object.freeze({}) }),
}),
})
const server = createOutboundProxyControl({
getSnapshot: () => snapshot,
routeHealth: { status: () => ({ updates: 'direct' }) },
})
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(0, '127.0.0.1', resolve)
})
try {
const result = await get(server.address().port, '/v1/environment?scope=agentNetwork')
assert.equal(result.status, 200)
assert.equal(result.body.revision, 'revision-one')
assert.equal(result.body.environment.HTTP_PROXY, 'http://127.0.0.1:17895')
assert.equal(result.body.environment.NO_PROXY, 'localhost,127.0.0.1,::1,.example.com')
assert.equal(result.body.environment.ALL_PROXY, 'http://127.0.0.1:17895')
assert.doesNotMatch(JSON.stringify(result.body), /alice|never-return-this/)
assert.equal((await get(server.address().port, '/v1/environment?scope=invalid')).status, 400)
} finally {
await new Promise(resolve => server.close(resolve))
}
})
test('outbound proxy control omits proxy entries for every disabled process scope', async () => {
const scopes = ['updates', 'platform', 'dshCore', 'dshPlugins', 'agentNetwork', 'managementTerminal', 'sharedDsh']
const snapshot = Object.freeze({
revision: 'direct-revision', recovery: 'none', credentials: Object.freeze({ username: '', password: null }),
configuration: Object.freeze({
enabled: true,
scopes: Object.freeze({
updates: false, platform: false, dshCore: false, dshPlugins: false,
agentNetwork: false, managementTerminal: false,
}),
modelApi: Object.freeze({ default: Object.freeze({ proxyEnabled: false }), providers: Object.freeze({}) }),
noProxy: Object.freeze({ system: Object.freeze(['localhost', '127.0.0.1', '::1']), user: Object.freeze([]) }),
environment: Object.freeze({ allProxy: 'scope-proxy' }),
}),
})
const server = createOutboundProxyControl({
getSnapshot: () => snapshot,
routeHealth: { status: () => ({}) },
})
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(0, '127.0.0.1', resolve)
})
try {
for (const scope of scopes) {
const result = await get(server.address().port, `/v1/environment?scope=${scope}`)
assert.equal(result.status, 200)
assert.equal(result.body.environment.HTTP_PROXY, undefined, scope)
assert.equal(result.body.environment.HTTPS_PROXY, undefined, scope)
assert.equal(result.body.environment.ALL_PROXY, undefined, scope)
}
} finally {
await new Promise(resolve => server.close(resolve))
}
})
@@ -0,0 +1,195 @@
import assert from 'node:assert/strict'
import { mkdtemp, readFile, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { defaultProxyConfiguration, validateProxyConfiguration } from '../../control-plane/services/outbound-proxy/lib/contracts.mjs'
import { ProviderInventory } from '../../control-plane/services/management/provider-inventory.mjs'
function response(value) {
return new Response(JSON.stringify({
type: 'server-response',
rpcId: 'fixture',
result: { ok: true, value },
}), { status: 200, headers: { 'content-type': 'application/json' } })
}
function proxySnapshot() {
const defaults = defaultProxyConfiguration()
return Object.freeze({
revision: 'revision-one',
recovery: 'none',
...validateProxyConfiguration({
...defaults,
modelApi: { default: defaults.modelApi.default, providers: {
adapted: { proxyEnabled: true },
shared: { proxyEnabled: true },
'shared-explicit': { proxyEnabled: true },
local: { proxyEnabled: true },
} },
}),
})
}
test('builds a sanitized Provider capability inventory from controlled DSH RPCs', async t => {
const root = await mkdtemp(join(tmpdir(), 'dsh-provider-inventory-'))
t.after(() => rm(root, { recursive: true, force: true }))
const calls = []
const inventory = new ProviderInventory({
cachePath: join(root, 'providers.json'),
now: () => new Date('2026-08-25T00:00:00.000Z'),
authentication: { cookie: async () => 'dsh-auth-fixture=signed' },
fetchImpl: async (url, init) => {
const body = JSON.parse(init.body)
calls.push({ url, method: body.method, payload: body.payload, cookie: init.headers.cookie })
if (body.method === 'llm/listProviders') return response([
{ id: 'shared', name: 'Shared' },
{ id: 'shared-explicit', name: 'Shared explicit' },
{ id: 'custom', name: 'Custom route' },
{ id: 'adapted', name: 'Adapted' },
{ id: 'local', name: 'Local' },
])
if (body.method === 'llm/listConfigurableProviders') return response([
{ provider: 'dormant', displayName: 'Dormant', settingsNs: 'llm-pi-ai', settingsPath: ['providers', 'dormant'] },
{ provider: 'shared', displayName: 'Shared', settingsNs: 'llm-pi-ai', settingsPath: ['providers', 'shared'] },
{ provider: 'shared-explicit', displayName: 'Shared explicit', settingsNs: 'llm-pi-ai', settingsPath: ['providers', 'shared-explicit'], supportsIndependentRouting: false },
{ provider: 'custom', displayName: 'Custom route', settingsNs: 'llm-pi-ai', settingsPath: ['providers', 'custom'], declared: true },
{ provider: 'adapted', displayName: 'Adapted', settingsNs: 'adapted', settingsPath: [] },
{ provider: 'local', displayName: 'Local', settingsNs: 'local', settingsPath: [] },
{ provider: '../invalid', displayName: 'Invalid', settingsNs: 'invalid', settingsPath: [] },
])
assert.equal(body.method, 'settings/describe')
return response({ namespaces: [
{ ns: 'llm-pi-ai', value: { providers: { shared: { baseURL: 'https://api.example.test' }, 'shared-explicit': { baseURL: 'https://shared.example.test' }, custom: { baseURL: 'https://custom.example.test' } } }, base: {} },
{ ns: 'adapted', value: {}, base: {} },
{ ns: 'local', value: { baseURL: 'http://127.0.0.1:11434/v1' }, base: {} },
] })
},
})
const result = await inventory.list(proxySnapshot())
assert.equal(result.source, 'live')
assert.equal(result.error, null)
assert.equal(result.updatedAt, '2026-08-25T00:00:00.000Z')
assert.deepEqual(result.providers.map(provider => ({
id: provider.id,
capability: provider.routingCapability,
requested: provider.requestedPolicy,
effective: provider.effectivePolicy,
reason: provider.reason,
})), [
{ id: 'adapted', capability: 'provider', requested: { proxyEnabled: true }, effective: 'proxy', reason: null },
{ id: 'custom', capability: 'provider', requested: { proxyEnabled: false }, effective: 'direct', reason: null },
{ id: 'local', capability: 'forced-direct', requested: null, effective: 'direct', reason: 'local-provider' },
{ id: 'shared', capability: 'provider', requested: { proxyEnabled: true }, effective: 'proxy', reason: null },
{ id: 'shared-explicit', capability: 'shared-dsh', requested: { proxyEnabled: true }, effective: 'shared-dsh', reason: 'client-uses-shared-dsh-route' },
])
assert.deepEqual(calls.map(call => call.method).sort(), [
'llm/listConfigurableProviders', 'llm/listProviders', 'settings/describe',
])
assert.equal(calls.every(call => call.url.endsWith(`/api/${call.method}`)), true)
assert.equal(calls.every(call => call.cookie === 'dsh-auth-fixture=signed'), true)
assert.equal(calls.every(call => JSON.stringify(call.payload) === JSON.stringify({ args: {} })), true)
assert.equal(result.providers.some(provider => provider.id === 'dormant'), false)
const cache = JSON.parse(await readFile(join(root, 'providers.json'), 'utf8'))
assert.doesNotMatch(JSON.stringify(cache), /api\.example|127\.0\.0\.1|baseURL|secret/i)
})
test('keeps the last sanitized Provider inventory when DSH is unavailable', async t => {
const root = await mkdtemp(join(tmpdir(), 'dsh-provider-cache-'))
t.after(() => rm(root, { recursive: true, force: true }))
let online = true
const inventory = new ProviderInventory({
cachePath: join(root, 'providers.json'),
fetchImpl: async (_url, init) => {
if (!online) throw new Error('DSH is stopped')
const method = JSON.parse(init.body).method
if (method === 'llm/listProviders') return response([{ id: 'deepseek', name: 'DeepSeek' }])
if (method === 'llm/listConfigurableProviders') {
return response([{ provider: 'deepseek', displayName: 'DeepSeek', settingsNs: 'deepseek', settingsPath: [] }])
}
return response({ namespaces: [{ ns: 'deepseek', value: {}, base: {} }] })
},
})
assert.equal((await inventory.list(proxySnapshot())).source, 'live')
online = false
const cached = await inventory.list(proxySnapshot())
assert.equal(cached.source, 'cache')
assert.equal(cached.providers[0].id, 'deepseek')
assert.equal(cached.error, 'DSH is stopped')
const empty = new ProviderInventory({
cachePath: join(root, 'missing.json'),
fetchImpl: async () => { throw new Error('DSH is stopped') },
})
const unavailable = await empty.list(proxySnapshot())
assert.equal(unavailable.source, 'unavailable')
assert.deepEqual(unavailable.providers, [])
assert.equal(unavailable.error, 'DSH is stopped')
})
test('replaces deleted Providers with newly configured Providers on refresh', async t => {
const root = await mkdtemp(join(tmpdir(), 'dsh-provider-refresh-'))
t.after(() => rm(root, { recursive: true, force: true }))
let current = { id: 'removed', displayName: 'Removed' }
const inventory = new ProviderInventory({
cachePath: join(root, 'providers.json'),
fetchImpl: async (_url, init) => {
const method = JSON.parse(init.body).method
if (method === 'llm/listProviders') return response([{ id: current.id, name: current.displayName }])
if (method === 'llm/listConfigurableProviders') return response([{
provider: current.id,
displayName: current.displayName,
settingsNs: 'llm-pi-ai',
settingsPath: ['providers', current.id],
declared: true,
}])
return response({ namespaces: [{
ns: 'llm-pi-ai',
value: { providers: { [current.id]: { baseURL: 'https://provider.example.test' } } },
base: {},
}] })
},
})
assert.deepEqual((await inventory.list(proxySnapshot())).providers.map(provider => provider.id), ['removed'])
current = { id: 'created', displayName: 'Created' }
assert.deepEqual((await inventory.list(proxySnapshot())).providers.map(provider => provider.id), ['created'])
assert.deepEqual(JSON.parse(await readFile(join(root, 'providers.json'), 'utf8')).providers.map(provider => provider.id), ['created'])
})
test('keeps configured DSH 0.1.5 Providers with catalog diagnostics manageable', async t => {
const root = await mkdtemp(join(tmpdir(), 'dsh-provider-diagnostic-'))
t.after(() => rm(root, { recursive: true, force: true }))
const inventory = new ProviderInventory({
cachePath: join(root, 'providers.json'),
fetchImpl: async (_url, init) => {
const method = JSON.parse(init.body).method
if (method === 'llm/listProviders') return response([])
if (method === 'llm/listConfigurableProviders') return response([{
provider: 'repairable',
displayName: 'Repairable Provider',
settingsNs: 'llm-pi-ai',
settingsPath: ['providers', 'repairable'],
declared: false,
error: 'configured model needs an api',
}])
return response({ namespaces: [{
ns: 'llm-pi-ai',
value: { providers: { repairable: { baseURL: 'https://repairable.example.test/v1' } } },
base: {},
}] })
},
})
const listed = await inventory.list(proxySnapshot())
assert.equal(listed.source, 'live')
assert.deepEqual(listed.providers.map(provider => ({
id: provider.id,
displayName: provider.displayName,
active: provider.active,
declared: provider.declared,
})), [{
id: 'repairable',
displayName: 'Repairable Provider',
active: false,
declared: false,
}])
})
@@ -0,0 +1,293 @@
import assert from 'node:assert/strict'
import { once } from 'node:events'
import { createServer, request as httpRequest } from 'node:http'
import { createServer as createNetServer, connect } from 'node:net'
import test from 'node:test'
import { defaultProxyConfiguration, validateProxyConfiguration } from '../../control-plane/services/outbound-proxy/lib/contracts.mjs'
import { createOutboundProxyControl } from '../../control-plane/services/outbound-proxy/lib/control.mjs'
import { createScopedProxyServer } from '../../control-plane/services/outbound-proxy/lib/data-plane.mjs'
import { ProviderHandleStore, providerHandleInternals } from '../../control-plane/services/outbound-proxy/lib/provider-handles.mjs'
import { selectProxyRoute } from '../../control-plane/services/outbound-proxy/lib/policy.mjs'
const sockets = new WeakMap()
async function listen(server) {
const active = new Set()
sockets.set(server, active)
server.on('connection', socket => {
active.add(socket)
socket.once('close', () => active.delete(socket))
})
server.listen(0, '127.0.0.1')
await once(server, 'listening')
return server.address().port
}
async function close(server) {
for (const socket of sockets.get(server) ?? []) socket.destroy()
if (!server.listening) return
server.closeAllConnections?.()
await new Promise(resolve => server.close(resolve))
}
function snapshot({ revision = 'revision-one', proxyPort = null, policy = { proxyEnabled: false } } = {}) {
const defaults = defaultProxyConfiguration()
const value = validateProxyConfiguration({
...defaults,
enabled: proxyPort !== null,
proxy: {
...defaults.proxy,
host: proxyPort === null ? '' : '127.0.0.1',
port: proxyPort,
username: proxyPort === null ? '' : 'external-user',
password: proxyPort === null ? undefined : 'external-password',
},
modelApi: { default: defaults.modelApi.default, providers: { deepseek: policy } },
})
return Object.freeze({ revision, recovery: 'none', ...value })
}
test('model Provider switch selects direct or independent proxy routing', async () => {
const defaults = defaultProxyConfiguration()
const make = (policy, scopes = {}) => Object.freeze({
revision: 'revision-one',
recovery: 'none',
...validateProxyConfiguration({
...defaults,
enabled: true,
proxy: { ...defaults.proxy, host: '127.0.0.1', port: 7890 },
scopes: { ...defaults.scopes, ...scopes },
modelApi: { default: defaults.modelApi.default, providers: { custom: policy } },
}),
})
const route = state => selectProxyRoute({
snapshot: state, scope: 'modelApi', providerId: 'custom', host: 'api.example.test', port: 443,
})
assert.equal((await route(make({ proxyEnabled: false }, { dshCore: true }))).reason, 'provider-direct')
assert.equal((await route(make({ proxyEnabled: true }, { dshCore: false, dshPlugins: false }))).reason, 'provider-proxy')
assert.equal((await route(make({ proxyEnabled: true }, { dshPlugins: true }))).reason, 'provider-proxy')
})
test('unconfigured model Providers inherit the complete default policy', async () => {
const defaults = defaultProxyConfiguration()
const snapshot = Object.freeze({
revision: 'revision-one', recovery: 'none',
...validateProxyConfiguration({
...defaults,
enabled: true,
proxy: { ...defaults.proxy, host: '127.0.0.1', port: 7890 },
scopes: { ...defaults.scopes, dshCore: true },
modelApi: { default: { proxyEnabled: true }, providers: {} },
}),
})
const result = await selectProxyRoute({
snapshot, scope: 'modelApi', providerId: 'new-provider', host: 'api.example.test', port: 443,
})
assert.equal(result.reason, 'provider-proxy')
})
function request({ port, path = '/', method = 'GET', headers = {}, body }) {
return new Promise((resolve, reject) => {
const outgoing = httpRequest({ host: '127.0.0.1', port, path, method, headers }, response => {
const chunks = []
response.on('data', chunk => chunks.push(chunk))
response.once('end', () => resolve({
status: response.statusCode,
headers: response.headers,
body: Buffer.concat(chunks),
}))
})
outgoing.once('error', reject)
if (body !== undefined) outgoing.end(body)
else outgoing.end()
})
}
function rawExchange(port, value, waitFor = '\r\n\r\n') {
return new Promise((resolve, reject) => {
const socket = connect({ host: '127.0.0.1', port })
let bytes = Buffer.alloc(0)
const timer = setTimeout(() => {
socket.destroy()
reject(new Error('provider CONNECT fixture timed out'))
}, 3_000)
socket.once('connect', () => socket.write(value))
socket.on('data', chunk => {
bytes = Buffer.concat([bytes, chunk])
if (!bytes.includes(waitFor)) return
clearTimeout(timer)
resolve({ socket, bytes })
})
socket.once('error', reject)
socket.once('close', () => clearTimeout(timer))
})
}
test('issues opaque provider handles and rejects expiry or policy revision changes', () => {
let now = 1_000
let generated = 0
const handles = new ProviderHandleStore({
ttlMs: 100,
now: () => now,
random: size => Buffer.alloc(size, ++generated),
})
const state = snapshot()
const issued = handles.issue({ providerId: 'deepseek', policyRevision: state.revision }, state)
assert.match(issued.handle, providerHandleInternals.HANDLE_PATTERN)
assert.equal(Buffer.from(issued.handle, 'base64url').byteLength, providerHandleInternals.HANDLE_BYTES)
assert.equal(handles.resolve(`DSH-Provider ${issued.handle}`, state), 'deepseek')
assert.throws(() => handles.resolve(`Basic ${issued.handle}`, state), error => error.code === 'PROVIDER_HANDLE_INVALID')
assert.throws(() => handles.resolve(`DSH-Provider ${issued.handle}`, snapshot({ revision: 'revision-two' })), error => error.code === 'PROVIDER_HANDLE_INVALID')
const expiring = handles.issue({ providerId: 'deepseek', policyRevision: state.revision }, state)
now += 101
assert.throws(() => handles.resolve(`DSH-Provider ${expiring.handle}`, state), error => error.code === 'PROVIDER_HANDLE_INVALID')
assert.throws(() => handles.issue({ providerId: '../secret', policyRevision: state.revision }, state), /provider ID/)
assert.throws(() => handles.issue({ providerId: 'deepseek', policyRevision: 'stale' }, state), error => error.code === 'REVISION_CONFLICT' && error.statusCode === 409)
})
test('control socket issues a provider handle only for the current revision', async () => {
const state = snapshot()
const handles = new ProviderHandleStore()
const control = createOutboundProxyControl({
getSnapshot: () => state,
routeHealth: { status: () => ({}) },
providerHandles: handles,
})
const port = await listen(control)
try {
const current = await request({
port,
path: '/v1/provider-handles',
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ providerId: 'deepseek', policyRevision: state.revision }),
})
assert.equal(current.status, 201)
const issued = JSON.parse(current.body)
assert.equal(handles.resolve(`DSH-Provider ${issued.handle}`, state), 'deepseek')
const stale = await request({
port,
path: '/v1/provider-handles',
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ providerId: 'deepseek', policyRevision: 'stale' }),
})
assert.equal(stale.status, 409)
assert.equal(JSON.parse(stale.body).error.code, 'REVISION_CONFLICT')
const oversized = await request({
port,
path: '/v1/provider-handles',
method: 'POST',
body: JSON.stringify({ providerId: 'deepseek', policyRevision: state.revision, padding: 'x'.repeat(4096) }),
})
assert.equal(oversized.status, 400)
} finally {
await close(control)
}
})
test('model Provider entry requires a valid handle and strips it from a direct request', async () => {
let authorization
const target = createServer((incoming, response) => {
authorization = incoming.headers['proxy-authorization']
response.end('provider-direct')
})
const targetPort = await listen(target)
const state = snapshot()
const handles = new ProviderHandleStore()
const proxy = createScopedProxyServer({ scope: 'modelApi', getSnapshot: () => state, providerHandles: handles })
const proxyPort = await listen(proxy)
try {
const missing = await request({ port: proxyPort, path: `http://127.0.0.1:${targetPort}/` })
assert.equal(missing.status, 407)
assert.equal(missing.headers['x-dsh-proxy-error'], 'PROVIDER_HANDLE_INVALID')
assert.equal(missing.headers['proxy-authenticate'], 'DSH-Provider')
const issued = handles.issue({ providerId: 'deepseek', policyRevision: state.revision }, state)
const accepted = await request({
port: proxyPort,
path: `http://127.0.0.1:${targetPort}/`,
headers: { 'proxy-authorization': `DSH-Provider ${issued.handle}` },
})
assert.equal(accepted.status, 200)
assert.equal(accepted.body.toString(), 'provider-direct')
assert.equal(authorization, undefined)
} finally {
await close(proxy)
await close(target)
}
})
test('model Provider proxy replaces the internal handle with external Basic authentication', async () => {
let observed
const upstream = createServer((incoming, response) => {
observed = { path: incoming.url, authorization: incoming.headers['proxy-authorization'] }
response.end('provider-proxied')
})
const upstreamPort = await listen(upstream)
const state = snapshot({ proxyPort: upstreamPort, policy: { proxyEnabled: true } })
const handles = new ProviderHandleStore()
const issued = handles.issue({ providerId: 'deepseek', policyRevision: state.revision }, state)
const proxy = createScopedProxyServer({ scope: 'modelApi', getSnapshot: () => state, providerHandles: handles })
const proxyPort = await listen(proxy)
try {
const result = await request({
port: proxyPort,
path: 'http://provider.example/v1/models',
headers: { 'proxy-authorization': `DSH-Provider ${issued.handle}` },
})
assert.equal(result.status, 200)
assert.equal(result.body.toString(), 'provider-proxied')
assert.equal(observed.path, 'http://provider.example/v1/models')
assert.equal(observed.authorization, `Basic ${Buffer.from('external-user:external-password').toString('base64')}`)
assert.doesNotMatch(observed.authorization, new RegExp(issued.handle))
} finally {
await close(proxy)
await close(upstream)
}
})
test('model Provider CONNECT requires a handle and never forwards it to the external proxy', async () => {
let observed = ''
const upstream = createNetServer(socket => {
let requestBytes = Buffer.alloc(0)
socket.on('data', chunk => {
requestBytes = Buffer.concat([requestBytes, chunk])
if (!requestBytes.includes('\r\n\r\n')) return
observed = requestBytes.toString('latin1')
socket.removeAllListeners('data')
socket.write('HTTP/1.1 200 Connection Established\r\n\r\n')
socket.on('data', value => socket.write(value))
})
})
const upstreamPort = await listen(upstream)
const state = snapshot({ proxyPort: upstreamPort, policy: { proxyEnabled: true } })
const handles = new ProviderHandleStore()
const issued = handles.issue({ providerId: 'deepseek', policyRevision: state.revision }, state)
const proxy = createScopedProxyServer({ scope: 'modelApi', getSnapshot: () => state, providerHandles: handles })
const proxyPort = await listen(proxy)
let socket
try {
const rejected = await rawExchange(proxyPort, 'CONNECT provider.example:443 HTTP/1.1\r\nHost: provider.example:443\r\n\r\n')
rejected.socket.destroy()
assert.match(rejected.bytes.toString('latin1'), /^HTTP\/1\.1 407/)
const accepted = await rawExchange(proxyPort, [
'CONNECT provider.example:443 HTTP/1.1',
'Host: provider.example:443',
`Proxy-Authorization: DSH-Provider ${issued.handle}`,
'',
'',
].join('\r\n'))
socket = accepted.socket
assert.match(accepted.bytes.toString('latin1'), /^HTTP\/1\.1 200/)
assert.match(observed, new RegExp(`Proxy-Authorization: Basic ${Buffer.from('external-user:external-password').toString('base64')}`, 'i'))
assert.doesNotMatch(observed, new RegExp(issued.handle))
} finally {
socket?.destroy()
await close(proxy)
await close(upstream)
}
})
@@ -0,0 +1,151 @@
import assert from 'node:assert/strict'
import { spawn } from 'node:child_process'
import { once } from 'node:events'
import { createServer } from 'node:http'
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { createScopedFetch, PROXY_SCOPE_PORTS } from '../../control-plane/services/management/scoped-fetch.mjs'
async function listen(server, options = {}) {
server.listen(options)
await once(server, 'listening')
return server.address()
}
async function close(server) {
if (!server.listening) return
server.closeAllConnections?.()
await new Promise((resolve, reject) => server.close(error => error === undefined ? resolve() : reject(error)))
}
test('routes enabled update fetches through the fixed outbound proxy entry', async context => {
const target = createServer((_request, response) => response.end('scoped response'))
context.after(() => close(target))
const targetAddress = await listen(target, { host: '127.0.0.1', port: 0 })
const authorities = []
const proxy = createServer((request, response) => {
authorities.push(request.url)
response.end('scoped response')
})
const proxySockets = new Set()
proxy.on('connection', socket => {
proxySockets.add(socket)
socket.once('close', () => proxySockets.delete(socket))
})
context.after(() => close(proxy))
await listen(proxy, { host: '127.0.0.1', port: PROXY_SCOPE_PORTS.updates })
const root = await mkdtemp(join(tmpdir(), 'dsh-scoped-fetch-'))
context.after(() => rm(root, { recursive: true, force: true }))
const routingStatePath = join(root, 'routing.json')
await writeFile(routingStatePath, JSON.stringify({ schema: 1, enabled: true, scopes: { updates: true } }))
const scopedFetch = createScopedFetch('updates', { routingStatePath })
try {
const url = `http://127.0.0.1:${String(targetAddress.port)}/metadata`
const response = await scopedFetch(url)
assert.equal(await response.text(), 'scoped response')
assert.deepEqual(authorities, [url])
} finally {
await scopedFetch.close()
for (const socket of proxySockets) socket.destroy()
await close(proxy)
}
const unavailable = createScopedFetch('updates', { routingStatePath })
try {
await assert.rejects(
unavailable(`http://127.0.0.1:${String(targetAddress.port)}/would-be-direct`, {
signal: AbortSignal.timeout(500),
}),
/fetch failed|ECONNREFUSED|proxy/i,
)
} finally {
await unavailable.close()
}
})
test('restarts Proxy Manager after an unclean exit leaves its control socket behind', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-proxy-process-restart-'))
const runRoot = join(root, 'run')
const dataRoot = join(root, 'data')
await mkdir(runRoot)
const script = new URL('../../control-plane/services/outbound-proxy/index.mjs', import.meta.url).pathname
const launch = () => spawn(process.execPath, [script], {
env: { ...process.env, DSH_PLATFORM_DATA: dataRoot, DSH_PLATFORM_RUN: runRoot },
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
})
const ready = child => {
const stderr = []
child.stderr.on('data', chunk => stderr.push(chunk))
return Promise.race([
once(child, 'message').then(([message]) => {
assert.equal(message.componentReady, true)
}),
once(child, 'exit').then(([code, signal]) => {
const detail = Buffer.concat(stderr).toString('utf8').trim()
throw new Error(`Proxy Manager exited before readiness (${String(code)}, ${String(signal)})${detail === '' ? '' : `: ${detail}`}`)
}),
])
}
const first = launch()
let second
let blocker
let releaseBlocker
try {
await ready(first)
const routing = JSON.parse(await readFile(join(runRoot, 'outbound-proxy-routing.json'), 'utf8'))
assert.equal(routing.schema, 1)
assert.equal(routing.enabled, false)
assert.equal(routing.scopes.agentNetwork, false)
assert.deepEqual(routing.environment, { allProxy: null })
assert.deepEqual(routing.noProxy.system, ['localhost', '127.0.0.1', '::1'])
assert.equal(JSON.stringify(routing).includes('password'), false)
first.kill('SIGKILL')
await once(first, 'exit')
blocker = createServer()
await new Promise((resolve, reject) => {
blocker.once('error', reject)
blocker.listen(PROXY_SCOPE_PORTS.updates, '127.0.0.1', resolve)
})
releaseBlocker = setTimeout(() => blocker.close(), 100)
second = launch()
await ready(second)
assert.equal(second.exitCode, null)
} finally {
clearTimeout(releaseBlocker)
if (blocker?.listening) await new Promise(resolve => blocker.close(resolve))
if (first.exitCode === null) first.kill('SIGKILL')
if (second?.exitCode === null) {
second.kill('SIGTERM')
await once(second, 'exit')
}
await rm(root, { recursive: true, force: true })
}
})
test('bypasses Proxy Manager when an update scope or the global proxy is disabled', async context => {
const target = createServer((_request, response) => response.end('direct response'))
context.after(() => close(target))
const address = await listen(target, { host: '127.0.0.1', port: 0 })
const root = await mkdtemp(join(tmpdir(), 'dsh-direct-fetch-'))
context.after(() => rm(root, { recursive: true, force: true }))
const routingStatePath = join(root, 'routing.json')
const url = `http://127.0.0.1:${String(address.port)}/direct`
for (const state of [
{ schema: 1, enabled: false, scopes: { updates: true } },
{ schema: 1, enabled: true, scopes: { updates: false } },
]) {
await writeFile(routingStatePath, JSON.stringify(state))
const fetch = createScopedFetch('updates', { routingStatePath })
try {
assert.equal(await (await fetch(url)).text(), 'direct response')
} finally {
await fetch.close()
}
}
})
test('rejects unknown outbound proxy scopes', () => {
assert.throws(() => createScopedFetch('unknown'), /unknown outbound proxy scope/)
})
@@ -0,0 +1,141 @@
import assert from 'node:assert/strict'
import { mkdtemp, readFile, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { defaultProxyConfiguration, validateProxyConfiguration } from '../../control-plane/services/outbound-proxy/lib/contracts.mjs'
import { ProxyTestManager, proxyTestInternals } from '../../control-plane/services/outbound-proxy/lib/test-manager.mjs'
function current() {
const validated = validateProxyConfiguration(defaultProxyConfiguration())
return Object.freeze({ revision: 'current-revision', ...validated })
}
function candidate(overrides = {}) {
const defaults = defaultProxyConfiguration()
return {
...defaults,
...overrides,
proxy: { ...defaults.proxy, ...overrides.proxy },
scopes: { ...defaults.scopes, ...overrides.scopes },
environment: { ...defaults.environment, ...overrides.environment },
modelApi: { ...defaults.modelApi, providers: {}, ...overrides.modelApi },
noProxy: { user: [], ...overrides.noProxy },
bypass: { additional: [], ...overrides.bypass },
}
}
async function terminal(manager, taskId) {
for (let attempt = 0; attempt < 1_000; attempt += 1) {
const state = manager.get(taskId)
if (state.status !== 'running') return state
await new Promise(resolve => setTimeout(resolve, 2))
}
throw new Error('proxy test did not reach a terminal state')
}
test('proxy test requires and exercises the candidate proxy without changing current configuration', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-proxy-test-candidate-'))
const manager = new ProxyTestManager({
statePath: join(root, 'tasks.json'),
resolve: async host => {
if (host === 'proxy.invalid') throw Object.assign(new Error('proxy host was not found'), { code: 'ENOTFOUND' })
return [{ address: '127.0.0.1', family: 4 }]
},
targets: [{ id: 'unreachable', host: 'target.invalid', port: 443, path: '/' }],
phaseTimeoutMs: 500,
})
const state = current()
await assert.rejects(
manager.start({ baseRevision: state.revision, value: candidate() }, state),
error => error.code === 'INVALID_CONFIG' && /requires a host and port/.test(error.message),
)
const started = await manager.start({
baseRevision: state.revision,
value: candidate({ proxy: { host: 'proxy.invalid', port: 8080 } }),
}, state)
const completed = await terminal(manager, started.taskId)
assert.equal(completed.status, 'failed')
assert.equal(completed.mode, 'proxy')
assert.equal(completed.stages.find(stage => stage.stage === 'proxy-address').status, 'failed')
assert.deepEqual(completed.stages.slice(1).map(stage => stage.status), ['skipped', 'skipped', 'skipped', 'skipped'])
assert.equal(completed.stages.some(stage => stage.stage === 'target-dns'), false)
assert.equal(completed.stages.find(stage => stage.stage === 'target-http').status, 'skipped')
assert.equal(state.configuration.enabled, false)
})
test('proxy tests list target DNS only when SOCKS5 resolves names locally', () => {
const remote = candidate({ proxy: { protocol: 'socks5', remoteDns: true } })
const local = candidate({ proxy: { protocol: 'socks5', remoteDns: false } })
assert.equal(proxyTestInternals.stagesFor(remote).includes('target-dns'), false)
assert.equal(proxyTestInternals.stagesFor(local).includes('target-dns'), true)
})
test('proxy test details carry locale-independent result data', () => {
assert.deepEqual(
proxyTestInternals.stageDetail(
'resolved 2 proxy address record(s)',
'proxy-addresses-resolved',
{ count: 2 },
),
{
detail: 'resolved 2 proxy address record(s)',
detailCode: 'proxy-addresses-resolved',
detailData: { count: 2 },
},
)
})
test('proxy tests allow one in-memory candidate, persist no credentials, and cancel safely', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-proxy-test-cancel-'))
const statePath = join(root, 'tasks.json')
const never = () => new Promise(() => {})
const manager = new ProxyTestManager({
statePath,
resolve: never,
targets: [{ id: 'blocked', host: 'blocked.example', port: 443, path: '/' }],
})
const state = current()
const value = candidate({
enabled: true,
proxy: { host: 'proxy.example', port: 1080, username: 'alice', password: 'never-persist-this' },
scopes: { updates: true },
})
const started = await manager.start({ baseRevision: state.revision, value }, state)
await assert.rejects(manager.start({ baseRevision: state.revision, value, unexpected: true }, state), error => (
error.code === 'INVALID_CONFIG' && error.stage === 'test'
))
await assert.rejects(manager.start({ baseRevision: state.revision, value }, state), error => error.code === 'PROXY_TEST_BUSY')
assert.equal((await readFile(statePath, 'utf8')).includes('never-persist-this'), false)
await manager.cancel(started.taskId)
const cancelled = await terminal(manager, started.taskId)
assert.equal(cancelled.status, 'cancelled')
assert.equal(cancelled.error.errorCode, 'REQUEST_CANCELLED')
assert.equal(cancelled.stages.find(stage => stage.stage === 'proxy-address').status, 'cancelled')
assert.equal(cancelled.stages.find(stage => stage.stage === 'proxy-address').errorCode, 'REQUEST_CANCELLED')
assert.equal((await readFile(statePath, 'utf8')).includes('alice'), false)
})
test('proxy test reload marks an interrupted credential-free task failed', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-proxy-test-reload-'))
const statePath = join(root, 'tasks.json')
const taskId = '123e4567-e89b-42d3-a456-426614174000'
const timestamp = '2026-08-25T00:00:00.000Z'
await writeFile(statePath, `${JSON.stringify({
schema: 1,
tasks: [{
schema: 1, taskId, status: 'running', baseRevision: 'old-revision',
candidateHash: `sha256:${'a'.repeat(64)}`, mode: 'proxy', currentStage: 'proxy-connect',
stages: proxyTestInternals.STAGES.map(stage => ({
stage, status: stage === 'proxy-connect' ? 'running' : 'pending', durationMs: null, errorCode: null, detail: null,
})),
error: null, createdAt: timestamp, updatedAt: timestamp,
}],
})}\n`)
const manager = new ProxyTestManager({ statePath, now: () => new Date('2026-08-25T00:00:01.000Z') })
await manager.initialize(async path => JSON.parse(await readFile(path, 'utf8')))
const restored = manager.get(taskId)
assert.equal(restored.status, 'failed')
assert.equal(restored.error.errorCode, 'PROXY_TEST_INTERRUPTED')
assert.equal(restored.stages.every(stage => !['running', 'pending'].includes(stage.status)), true)
})
@@ -0,0 +1,434 @@
import assert from 'node:assert/strict'
import { createHash, generateKeyPairSync } from 'node:crypto'
import { cp, lstat, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { spawnSync } from 'node:child_process'
import test from 'node:test'
import { verifyRecoveryKeyring } from '../stage0/lib/keyring.mjs'
import { parseEnvironmentManifest, parseStable } from '../lib/contracts.mjs'
import { parseImageInventory } from '../lib/deployment-contracts.mjs'
import { verifyDetached } from '../stage0/lib/signature.mjs'
import { verifyImageRelease } from '../tools/verify-image-release.mjs'
import { createEnvironmentRelease } from '../tools/environment-release.mjs'
import { verifyManagementDependencies } from '../tools/verify-management-dependencies.mjs'
import { parseBootstrapVersion, validateBootstrapContentTransition } from '../lib/bootstrap-version.mjs'
import { writeDshEntrypointFixture } from './fixtures/dsh-package.mjs'
const supportedTargetUrl = new URL('../../../release/supported-target.json', import.meta.url)
const environmentDefinitionUrl = new URL('../../environment/definition.json', import.meta.url)
const supportedDshVersion = JSON.parse(await readFile(supportedTargetUrl, 'utf8')).latestSupportedDsh
const environmentVersion = JSON.parse(await readFile(environmentDefinitionUrl, 'utf8')).version
test('Bootstrap content changes require a strictly newer Bootstrap version', () => {
assert.equal(parseBootstrapVersion('1.0.1'), '1.0.1')
assert.equal(parseBootstrapVersion('1.0.1\n'), '1.0.1')
assert.throws(() => parseBootstrapVersion('1.0'), /Bootstrap VERSION is invalid/)
assert.throws(() => parseBootstrapVersion(' 1.0.1\n'), /Bootstrap VERSION is invalid/)
assert.throws(() => parseBootstrapVersion('1.0.1\nextra'), /Bootstrap VERSION is invalid/)
assert.throws(() => parseBootstrapVersion('99999999999999999999.0.0'), /Bootstrap VERSION is invalid/)
assert.doesNotThrow(() => validateBootstrapContentTransition({
previousVersion: '1.0.0', previousSha256: 'a'.repeat(64),
nextVersion: '1.0.0', nextSha256: 'a'.repeat(64),
}))
assert.doesNotThrow(() => validateBootstrapContentTransition({
previousVersion: '1.0.0', previousSha256: 'a'.repeat(64),
nextVersion: '1.0.1', nextSha256: 'b'.repeat(64),
}))
for (const nextVersion of ['1.0.0', '0.9.9']) {
assert.throws(() => validateBootstrapContentTransition({
previousVersion: '1.0.0', previousSha256: 'a'.repeat(64),
nextVersion, nextSha256: 'b'.repeat(64),
}), /content changed without increasing Bootstrap VERSION/)
}
})
test('Stage-0 launches the packaged outbound proxy entrypoint', async () => {
const source = await readFile(new URL('../stage0/index.mjs', import.meta.url), 'utf8')
assert.match(source, /'services', 'outbound-proxy', 'index\.mjs'/)
assert.doesNotMatch(source, /'services', 'proxy', 'index\.mjs'/)
})
test('Stage-0 launches the packaged Access Manager entrypoint', async () => {
const source = await readFile(new URL('../stage0/index.mjs', import.meta.url), 'utf8')
assert.match(source, /'services', 'access-manager', 'index\.mjs'/)
})
function pair(root, name) {
const value = generateKeyPairSync('ed25519')
const privatePath = join(root, `${name}-private.pem`)
const publicPath = join(root, `${name}-public.pem`)
return Promise.all([
writeFile(privatePath, value.privateKey.export({ format: 'pem', type: 'pkcs8' })),
writeFile(publicPath, value.publicKey.export({ format: 'pem', type: 'spki' })),
]).then(() => ({ ...value, privatePath, publicPath }))
}
test('offline keyring tool emits a Recovery-verifiable monotonic public bundle', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-keyring-tool-'))
const recovery = await pair(root, 'recovery')
const current = await pair(root, 'current')
const next = await pair(root, 'next')
const output = join(root, 'output')
const result = spawnSync(process.execPath, [
new URL('../tools/keyring.mjs', import.meta.url).pathname,
recovery.privatePath, current.publicPath, next.publicPath, '1', output, '-',
], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
const bytes = await readFile(join(output, 'keyring.json'))
const signature = JSON.parse(await readFile(join(output, 'keyring.sig.json')))
const publicKey = (await readFile(join(output, 'recovery-root.spki.base64'), 'utf8')).trim()
assert.equal(verifyRecoveryKeyring(bytes, signature, publicKey).generation, 1)
})
test('release tool signs an exact supported target with the configured current key', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-release-tool-'))
const release = await pair(root, 'release')
const publicDer = release.publicKey.export({ format: 'der', type: 'spki' })
const currentKeyId = createHash('sha256').update(publicDer).digest('hex')
const names = ['bootstrap-manifest', 'bootstrap-signature', 'environment-manifest', 'environment-signature']
const artifacts = []
for (const id of names) {
const path = join(root, id)
await writeFile(path, id)
artifacts.push({ id, path, mediaType: 'application/octet-stream' })
}
const config = {
currentKeyId,
keyringGeneration: 1,
targetSequence: 2,
artifactBaseUrl: 'https://release.example/v2/',
artifacts,
officialDshPolicy: JSON.parse(await readFile(new URL('../../../release/official-dsh-policy.json', import.meta.url))),
desired: {
bootstrap: { version: '1', manifestArtifactId: 'bootstrap-manifest', signatureArtifactId: 'bootstrap-signature' },
environment: { version: '2', manifestArtifactId: 'environment-manifest', signatureArtifactId: 'environment-signature' },
dsh: { version: '0.1.0-rc.7', integrity: `sha512-${Buffer.alloc(64).toString('base64')}` },
},
}
const configPath = join(root, 'config.json')
const output = join(root, 'output')
await writeFile(configPath, JSON.stringify(config))
const result = spawnSync(process.execPath, [
new URL('../tools/build-release.mjs', import.meta.url).pathname,
configPath, release.privatePath, output,
], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
const stable = await readFile(join(output, 'stable.json'))
assert.equal(parseStable(stable).targetSequence, 2)
verifyDetached(stable, JSON.parse(await readFile(join(output, 'stable.sig.json'))), publicDer.toString('base64'))
})
test('Management terminal dependencies are exact, licensed, and architecture-neutral', async () => {
const root = new URL('../../control-plane/services/management/', import.meta.url).pathname
const manifest = JSON.parse(await readFile(join(root, 'package.json')))
const lock = JSON.parse(await readFile(join(root, 'package-lock.json')))
assert.equal(Object.hasOwn(manifest, 'version'), false)
assert.equal(Object.hasOwn(lock, 'version'), false)
assert.equal(Object.hasOwn(lock.packages[''], 'version'), false)
const result = await verifyManagementDependencies(root)
assert.deepEqual(result.packages, {
'@xterm/addon-fit': '0.11.0', '@xterm/xterm': '6.0.0', 'qrcode-generator': '2.0.4', undici: '8.10.0', ws: '8.21.3',
})
assert.equal(result.nativeModules, 0)
})
test('Environment release identity ignores target metadata but covers packaged content and official DSH policy', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-environment-release-'))
const bootstrapPath = join(root, 'bootstrap.json')
const environmentPath = join(root, 'environment.json')
const policyPath = new URL('../../../release/official-dsh-policy.json', import.meta.url).pathname
const common = {
schema: 1,
keyringGeneration: 1,
targetSequence: 1,
issuedAt: '2026-08-21T00:00:00.000Z',
artifacts: [{
id: 'artifact', mediaType: 'application/octet-stream', sha256: 'a'.repeat(64), size: 1,
url: 'https://release.example/targets/1/artifact',
}],
}
const bootstrap = { ...common, manifestType: 'bootstrap', version: '1.0.0', bootstrapApi: 1, entrypoint: '/platform/bootstrap/index.mjs' }
const environment = {
...common, manifestType: 'environment', version: '1.0.0', bootstrapApi: 1,
components: [{ id: 'component', sha256: 'a'.repeat(64) }], patches: [], systemPlugins: [],
}
await writeFile(bootstrapPath, JSON.stringify(bootstrap))
await writeFile(environmentPath, JSON.stringify(environment))
const first = await createEnvironmentRelease({ bootstrapManifestPath: bootstrapPath, environmentManifestPath: environmentPath, officialDshPolicyPath: policyPath })
bootstrap.targetSequence = 2
bootstrap.issuedAt = '2026-08-22T00:00:00.000Z'
bootstrap.artifacts[0].url = 'https://release.example/targets/2/artifact'
environment.targetSequence = 2
environment.issuedAt = '2026-08-22T00:00:00.000Z'
environment.artifacts[0].url = 'https://release.example/targets/2/artifact'
await writeFile(bootstrapPath, JSON.stringify(bootstrap))
await writeFile(environmentPath, JSON.stringify(environment))
const second = await createEnvironmentRelease({ bootstrapManifestPath: bootstrapPath, environmentManifestPath: environmentPath, officialDshPolicyPath: policyPath })
assert.deepEqual(second, first)
environment.artifacts[0].sha256 = 'b'.repeat(64)
environment.components[0].sha256 = 'b'.repeat(64)
await writeFile(environmentPath, JSON.stringify(environment))
const changed = await createEnvironmentRelease({ bootstrapManifestPath: bootstrapPath, environmentManifestPath: environmentPath, officialDshPolicyPath: policyPath })
assert.notEqual(changed.environmentContentSha256, first.environmentContentSha256)
environment.artifacts[0].sha256 = 'a'.repeat(64)
environment.components[0].sha256 = 'a'.repeat(64)
await writeFile(environmentPath, JSON.stringify(environment))
const changedPolicyPath = join(root, 'official-dsh-policy.json')
const changedPolicy = JSON.parse(await readFile(policyPath, 'utf8'))
changedPolicy.keys[0].expires = '2027-08-21T00:00:00.000Z'
await writeFile(changedPolicyPath, JSON.stringify(changedPolicy))
const policyChanged = await createEnvironmentRelease({
bootstrapManifestPath: bootstrapPath,
environmentManifestPath: environmentPath,
officialDshPolicyPath: changedPolicyPath,
})
assert.notEqual(policyChanged.officialDshPolicySha256, first.officialDshPolicySha256)
})
test('prepares one flat Recovery-rooted release from the reviewed Supported Target', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-prepare-release-'))
const recovery = await pair(root, 'recovery')
const current = await pair(root, 'current')
const next = await pair(root, 'next')
const trust = join(root, 'trust')
let result = spawnSync(process.execPath, [
new URL('../tools/keyring.mjs', import.meta.url).pathname,
recovery.privatePath, current.publicPath, next.publicPath, '1', trust, '-',
], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
const packageRoot = join(root, 'npm', 'package')
await writeDshEntrypointFixture(packageRoot)
await writeFile(join(packageRoot, 'package.json'), JSON.stringify({ name: '@deepseek-ai/dsh', version: supportedDshVersion }))
const picker = join(packageRoot, 'node_modules/@deepseek-ai/dsh-host-directory-picker-browse/lib')
const connection = join(packageRoot, 'node_modules/@deepseek-ai/dsh-client-connection/lib')
await mkdir(picker, { recursive: true })
await mkdir(connection, { recursive: true })
await writeFile(join(picker, 'index.js'), 'const target = resolve(path ?? home);\n')
await writeFile(join(connection, 'client.js'), 'isLoopback: pageLocation === void 0 || isLoopbackHostname(pageLocation.hostname),\n')
const tarball = join(root, `deepseek-ai-dsh-${supportedDshVersion}.tgz`)
result = spawnSync('tar', ['-czf', tarball, '-C', join(root, 'npm'), 'package'], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
const output = join(root, 'release')
result = spawnSync(process.execPath, [
new URL('../tools/prepare-release.mjs', import.meta.url).pathname,
supportedTargetUrl.pathname,
environmentDefinitionUrl.pathname,
new URL('../../../release/official-dsh-policy.json', import.meta.url).pathname,
trust, current.privatePath, tarball, '-', '1', 'https://release.example/release-channel/targets/1/', output,
], { encoding: 'utf8', env: { ...process.env, SOURCE_DATE_EPOCH: '1787068800' } })
assert.equal(result.status, 0, result.stderr)
const files = await readdir(output)
for (const name of [
'bootstrap.tgz', 'bootstrap.manifest.json', 'bootstrap.manifest.sig.json',
'environment.manifest.json', 'environment.manifest.sig.json',
'stable.json', 'stable.sig.json', 'keyring.json', 'keyring.sig.json',
]) assert.ok(files.includes(name), `${name} is missing`)
assert.equal(files.includes(`deepseek-ai-dsh-${supportedDshVersion}.tgz`), false)
assert.equal(files.some(name => name.startsWith('.')), false)
const bootstrapArchive = spawnSync('tar', ['-tzf', join(output, 'bootstrap.tgz')], { encoding: 'utf8' })
assert.equal(bootstrapArchive.status, 0, bootstrapArchive.stderr)
const bootstrapEntries = bootstrapArchive.stdout.trim().split('\n')
for (const name of [
'platform/bootstrap/index.mjs',
'control-plane/definition.json',
'control-plane/hooks/dsh-web-ready.mjs',
'control-plane/hooks/recovery/index.mjs',
'control-plane/services/gateway/index.mjs',
'control-plane/services/access-manager/index.mjs',
'control-plane/services/access-manager/supervisor.mjs',
'control-plane/services/access-manager/lib/server.mjs',
'control-plane/services/outbound-proxy/index.mjs',
'control-plane/services/outbound-proxy/supervisor.mjs',
'control-plane/services/management/index.mjs',
'control-plane/services/management/public/index.html',
'control-plane/services/management/package-lock.json',
'control-plane/services/management/node_modules/ws/index.js',
'control-plane/services/management/node_modules/ws/LICENSE',
'control-plane/services/management/node_modules/undici/index.js',
'control-plane/services/management/node_modules/undici/LICENSE',
'control-plane/services/management/node_modules/@xterm/xterm/lib/xterm.mjs',
'control-plane/services/management/node_modules/@xterm/xterm/css/xterm.css',
'control-plane/services/management/node_modules/@xterm/addon-fit/lib/addon-fit.mjs',
'control-plane/services/management/terminal/pty-helper.py',
'control-plane/services/management/terminal/sessions.mjs',
'control-plane/modules/file-manager/index.mjs',
'control-plane/modules/file-manager/transfers.mjs',
'control-plane/modules/file-manager/editor.mjs',
'control-plane/modules/file-manager/tasks.mjs',
'control-plane/modules/file-manager/archives.mjs',
'control-plane/modules/skill-manager/index.mjs',
]) assert.ok(bootstrapEntries.includes(name), `${name} is missing from bootstrap.tgz`)
assert.equal(bootstrapEntries.some(name => name.startsWith('control-plane/skills/')), false)
const skillArchive = spawnSync('tar', ['-tzf', join(output, 'system-skill-catalog')], { encoding: 'utf8' })
assert.equal(skillArchive.status, 0, skillArchive.stderr)
assert.match(skillArchive.stdout, /^skills\/catalog\.json$/m)
assert.match(skillArchive.stdout, /^skills\/dsh-docker-operations\/SKILL\.md$/m)
assert.equal(bootstrapEntries.some(name => name.endsWith('.node')), false)
const stableBytes = await readFile(join(output, 'stable.json'))
const stable = parseStable(stableBytes)
const ring = JSON.parse(await readFile(join(trust, 'keyring.json'), 'utf8'))
verifyDetached(stableBytes, JSON.parse(await readFile(join(output, 'stable.sig.json'))), ring.current.publicKey)
assert.equal(stable.desired.dsh.version, supportedDshVersion)
assert.equal(stable.desired.environment.version, environmentVersion)
assert.equal(stable.desired.bootstrap.version, (await readFile(new URL('../bootstrap/VERSION', import.meta.url), 'utf8')).trim())
assert.equal(stable.officialDshPolicy.packageName, '@deepseek-ai/dsh')
assert.equal(stable.artifacts.some(artifact => artifact.mediaType === 'application/vnd.npm.package+gzip'), false)
assert.equal(stable.artifacts.every(artifact => !artifact.url.includes('/artifacts/')), true)
const verifiedImage = await verifyImageRelease({
releaseRoot: output,
recoveryPublicKeyPath: join(trust, 'recovery-root.spki.base64'),
dshTarballPath: tarball,
supportedTargetPath: supportedTargetUrl.pathname,
environmentDefinitionPath: environmentDefinitionUrl.pathname,
})
assert.equal(verifiedImage.stable.targetSequence, 1)
assert.equal(verifiedImage.environment.manifest.version, environmentVersion)
const imageInput = join(root, 'image-input')
const seedOutput = join(root, 'formal-seed')
await mkdir(imageInput)
await cp(output, join(imageInput, 'release'), { recursive: true })
await cp(join(trust, 'recovery-root.spki.base64'), join(imageInput, 'recovery-root.spki.base64'))
await cp(tarball, join(imageInput, 'dsh.tgz'))
await cp(supportedTargetUrl, join(imageInput, 'supported-target.json'))
await cp(environmentDefinitionUrl, join(imageInput, 'environment-definition.json'))
result = spawnSync(process.execPath, [
new URL('../tools/build-seed.mjs', import.meta.url).pathname,
packageRoot, seedOutput, imageInput, 'fixture-revision',
], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
const inventory = parseImageInventory(await readFile(join(seedOutput, 'inventory.json')))
assert.equal(inventory.authority, 'stable')
assert.equal(inventory.targetSequence, 1)
assert.equal(inventory.deployment.dshVersion, supportedDshVersion)
assert.equal((await lstat(join(seedOutput, 'environments', environmentVersion, 'environment.manifest.json'))).mode & 0o777, 0o444)
assert.equal(
await readFile(join(seedOutput, 'trust', 'recovery-root.spki.base64'), 'utf8'),
await readFile(join(trust, 'recovery-root.spki.base64'), 'utf8'),
)
assert.deepEqual(
await readFile(join(seedOutput, 'trust', 'keyring.json')),
await readFile(join(trust, 'keyring.json')),
)
assert.deepEqual(
await readFile(join(seedOutput, 'trust', 'keyring.sig.json')),
await readFile(join(trust, 'keyring.sig.json')),
)
await assert.rejects(lstat(join(seedOutput, 'trust', 'DEVELOPMENT_FIXTURE')), { code: 'ENOENT' })
const environment = parseEnvironmentManifest(await readFile(join(output, 'environment.manifest.json')))
assert.equal(environment.artifacts.every(artifact => !artifact.url.includes('/artifacts/')), true)
const changedBootstrapRelease = join(root, 'changed-bootstrap-release')
await cp(output, changedBootstrapRelease, { recursive: true })
const changedBootstrapManifestPath = join(changedBootstrapRelease, 'bootstrap.manifest.json')
const changedBootstrapManifest = JSON.parse(await readFile(changedBootstrapManifestPath, 'utf8'))
changedBootstrapManifest.artifacts[0].sha256 = 'f'.repeat(64)
await writeFile(changedBootstrapManifestPath, JSON.stringify(changedBootstrapManifest))
await rm(join(changedBootstrapRelease, 'bootstrap.manifest.sig.json'))
result = spawnSync(process.execPath, [
new URL('../tools/sign.mjs', import.meta.url).pathname,
'sign', current.privatePath, changedBootstrapManifestPath,
join(changedBootstrapRelease, 'bootstrap.manifest.sig.json'),
], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
const changedStablePath = join(changedBootstrapRelease, 'stable.json')
const changedStable = JSON.parse(await readFile(changedStablePath, 'utf8'))
for (const [id, name] of [
['bootstrap-manifest', 'bootstrap.manifest.json'],
['bootstrap-signature', 'bootstrap.manifest.sig.json'],
]) {
const bytes = await readFile(join(changedBootstrapRelease, name))
const artifact = changedStable.artifacts.find(candidate => candidate.id === id)
artifact.sha256 = createHash('sha256').update(bytes).digest('hex')
artifact.size = bytes.byteLength
}
await writeFile(changedStablePath, JSON.stringify(changedStable))
await rm(join(changedBootstrapRelease, 'stable.sig.json'))
result = spawnSync(process.execPath, [
new URL('../tools/sign.mjs', import.meta.url).pathname,
'sign', current.privatePath, changedStablePath, join(changedBootstrapRelease, 'stable.sig.json'),
], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
const invalidBootstrapOutput = join(root, 'unchanged-bootstrap-version-release')
result = spawnSync(process.execPath, [
new URL('../tools/prepare-release.mjs', import.meta.url).pathname,
supportedTargetUrl.pathname,
environmentDefinitionUrl.pathname,
new URL('../../../release/official-dsh-policy.json', import.meta.url).pathname,
trust, current.privatePath, tarball, changedBootstrapRelease, '2',
'https://release.example/platform-bootstrap-conflict/', invalidBootstrapOutput,
], { encoding: 'utf8', env: { ...process.env, SOURCE_DATE_EPOCH: '1787068801' } })
assert.notEqual(result.status, 0)
assert.match(result.stderr, /Bootstrap content changed without increasing Bootstrap VERSION/)
await assert.rejects(lstat(invalidBootstrapOutput), error => error?.code === 'ENOENT')
const rollbackOutput = join(root, 'rollback-release')
result = spawnSync(process.execPath, [
new URL('../tools/prepare-release.mjs', import.meta.url).pathname,
supportedTargetUrl.pathname,
new URL('../../environment/definition.json', import.meta.url).pathname,
new URL('../../../release/official-dsh-policy.json', import.meta.url).pathname,
trust, current.privatePath, tarball, output, '1',
'https://release.example/platform-1-repeat/', rollbackOutput,
], { encoding: 'utf8' })
assert.notEqual(result.status, 0)
const futureTrust = join(root, 'future-trust')
result = spawnSync(process.execPath, [
new URL('../tools/keyring.mjs', import.meta.url).pathname,
recovery.privatePath, current.publicPath, next.publicPath, '2', futureTrust, '-',
], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
const futureRelease = join(root, 'future-release')
await cp(output, futureRelease, { recursive: true })
await cp(join(futureTrust, 'keyring.json'), join(futureRelease, 'keyring.json'), { force: true })
await cp(join(futureTrust, 'keyring.sig.json'), join(futureRelease, 'keyring.sig.json'), { force: true })
const futureStable = JSON.parse(await readFile(join(futureRelease, 'stable.json'), 'utf8'))
futureStable.keyringGeneration = 2
await writeFile(join(futureRelease, 'stable.json'), JSON.stringify(futureStable))
await rm(join(futureRelease, 'stable.sig.json'))
result = spawnSync(process.execPath, [
new URL('../tools/sign.mjs', import.meta.url).pathname,
'sign', current.privatePath, join(futureRelease, 'stable.json'), join(futureRelease, 'stable.sig.json'),
], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
result = spawnSync(process.execPath, [
new URL('../tools/prepare-release.mjs', import.meta.url).pathname,
supportedTargetUrl.pathname,
new URL('../../environment/definition.json', import.meta.url).pathname,
new URL('../../../release/official-dsh-policy.json', import.meta.url).pathname,
trust, current.privatePath, tarball, futureRelease, '2',
'https://release.example/platform-2/', join(root, 'generation-rollback-release'),
], { encoding: 'utf8' })
assert.notEqual(result.status, 0)
assert.match(result.stderr, /keyring generation must not roll back/)
const conflictingTrust = join(root, 'conflicting-trust')
const conflictingNext = await pair(root, 'conflicting-next')
result = spawnSync(process.execPath, [
new URL('../tools/keyring.mjs', import.meta.url).pathname,
recovery.privatePath, current.publicPath, conflictingNext.publicPath, '1', conflictingTrust, '-',
], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
result = spawnSync(process.execPath, [
new URL('../tools/prepare-release.mjs', import.meta.url).pathname,
supportedTargetUrl.pathname,
new URL('../../environment/definition.json', import.meta.url).pathname,
new URL('../../../release/official-dsh-policy.json', import.meta.url).pathname,
conflictingTrust, current.privatePath, tarball, output, '2',
'https://release.example/platform-conflict/', join(root, 'conflicting-release'),
], { encoding: 'utf8' })
assert.notEqual(result.status, 0)
assert.match(result.stderr, /same keyring generation must be byte-identical/)
})
@@ -0,0 +1,82 @@
import assert from 'node:assert/strict'
import { createServer, request as httpRequest } from 'node:http'
import { mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { LocalApiClient } from '../../control-plane/modules/updater/lib/client.mjs'
import {
createRestrictedCliServer,
restrictedCliRoute,
} from '../../control-plane/services/management/restricted-cli.mjs'
import { listenManagement } from '../../control-plane/services/management/server.mjs'
test('restricted CLI channel forwards only documented public operations', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-restricted-cli-'))
const managementSocket = join(root, 'management.sock')
const cliSocket = join(root, 'management-cli.sock')
const seen = []
const management = createServer((request, response) => {
seen.push({ method: request.method, url: request.url, headers: request.headers })
request.resume()
response.writeHead(200, { 'content-type': 'application/json' })
response.end('{"ok":true}')
})
const cli = createRestrictedCliServer({ managementSocketPath: managementSocket, token: 'x'.repeat(32) })
try {
await listenManagement(management, managementSocket)
await listenManagement(cli, cliSocket)
const client = new LocalApiClient(cliSocket)
assert.deepEqual(await client.request('GET', '/_dsh_platform/api/v1/status'), { ok: true })
assert.deepEqual(await client.request('GET', '/_dsh_platform/api/v1/logs?limit=10'), { ok: true })
assert.deepEqual(await client.request('POST', '/_dsh_platform/api/v1/restart-dsh'), { ok: true })
await assert.rejects(
client.request('GET', '/_dsh_platform/api/v1/auth-settings'),
error => error.statusCode === 404 && error.code === 'CLI_ROUTE_NOT_AVAILABLE',
)
await assert.rejects(
client.request('GET', '/_dsh_platform/api/v1/files/list?path=%2F'),
error => error.statusCode === 404 && error.code === 'CLI_ROUTE_NOT_AVAILABLE',
)
await new Promise((resolve, reject) => {
const outgoing = httpRequest({
socketPath: cliSocket,
method: 'GET',
path: '/_dsh_platform/api/v1/status',
headers: {
authorization: 'Basic forged',
cookie: 'dsh_management=forged',
origin: 'https://forged.example',
'x-dsh-csrf': 'forged',
'x-dsh-internal-capability': 'forged',
'x-dsh-restricted-cli': 'forged',
},
}, response => {
response.resume()
response.once('end', resolve)
})
outgoing.once('error', reject)
outgoing.end()
})
assert.equal(seen.length, 4)
assert.equal(seen.every(value => value.headers['x-dsh-restricted-cli'] === 'x'.repeat(32)), true)
const hardened = seen.at(-1).headers
for (const name of ['authorization', 'cookie', 'origin', 'x-dsh-csrf', 'x-dsh-internal-capability']) {
assert.equal(hardened[name], undefined)
}
} finally {
await Promise.all([
new Promise(resolve => cli.close(resolve)),
new Promise(resolve => management.close(resolve)),
])
await rm(root, { recursive: true, force: true })
}
})
test('restricted CLI allowlist binds both method and normalized pathname', () => {
assert.equal(restrictedCliRoute('GET', '/_dsh_platform/api/v1/status'), true)
assert.equal(restrictedCliRoute('POST', '/_dsh_platform/api/v1/status'), false)
assert.equal(restrictedCliRoute('GET', '/_dsh_platform/api/v1/logs?source=stage0'), true)
assert.equal(restrictedCliRoute('GET', '/_dsh_platform/api/v1/terminal/sessions'), false)
assert.equal(restrictedCliRoute('GET', '/_dsh_platform/api/v1/status/../auth-settings'), false)
})
@@ -0,0 +1,148 @@
import assert from 'node:assert/strict'
import { mkdtemp } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { UpdateJournal } from '../../control-plane/modules/updater/lib/journal.mjs'
import { CompleteStateRecovery } from '../../control-plane/modules/updater/lib/rollback.mjs'
async function fixture() {
const root = await mkdtemp(join(tmpdir(), 'dsh-complete-rollback-'))
const journal = new UpdateJournal(join(root, 'journal.json'))
const from = {
dsh: '0.1.0-rc.7', environment: 'env-1', runtime: 'runtime-a', dataSnapshot: null,
receiptTokens: ['stable-b', 'stable-a'],
}
const to = { dsh: '0.1.0-rc.8', environment: 'env-1', runtime: 'runtime-b' }
await journal.begin({ transactionId: 'transaction-a', mode: 'experimental', from, to })
await journal.transition('candidate-ready', { receiptTokens: ['experimental'] })
await journal.transition('suspended')
await journal.transition('snapshot-created', { snapshotId: 'snapshot-a' })
await journal.transition('switched')
await journal.transition('probation', { probationUntil: '2026-08-19T00:02:00.000Z' })
await journal.transition('committed')
const calls = []
const current = { ...to, dataSnapshot: null, receiptTokens: ['experimental', 'stable-a', 'stable-b'] }
const activator = {
currentDeployment: async () => current,
rollbackDeployments: async () => ({
current: {
id: current.runtime,
dshVersion: current.dsh,
environmentVersion: current.environment,
receiptTokens: current.receiptTokens,
},
previous: {
id: 'runtime-a-materialized',
dshVersion: from.dsh,
environmentVersion: from.environment,
receiptTokens: from.receiptTokens,
},
}),
suspendDsh: async () => calls.push('suspend'),
restoreDeployment: async (value, options) => calls.push(`restore:${value.runtime}:${String(options.resume)}`),
resumeDsh: async () => calls.push('resume'),
}
const snapshots = {
inspect: async id => ({
id, createdAt: '2026-08-19T00:00:00.000Z', archiveSha256: 'a'.repeat(64), archiveSize: 42,
runtimeId: from.runtime, environmentVersion: from.environment, dshVersion: from.dsh,
}),
restore: async id => calls.push(`data:${id}`),
}
return { journal, from, to, current, activator, snapshots, calls, recovery: new CompleteStateRecovery({ journal, snapshots, activator }) }
}
test('binds a complete rollback plan to current Runtime, receipts, and verified snapshot', async () => {
const value = await fixture()
const plan = await value.recovery.plan()
assert.equal(plan.previous.runtime, 'runtime-a-materialized')
assert.deepEqual(plan.previous.receiptTokens, ['stable-a', 'stable-b'])
value.current.runtime = 'different-runtime'
assert.equal((await value.recovery.plan()).snapshot, null)
})
test('restores the previous complete state only with the current plan ID', async () => {
const value = await fixture()
const plan = await value.recovery.plan()
const progress = []
await assert.rejects(value.recovery.restore('stale'), /stale/)
await assert.rejects(value.recovery.restore(plan.planId, { requireConfirmation: true }), /confirmation/)
const result = await value.recovery.restore(plan.planId, {
requireConfirmation: true,
confirmDataLoss: true,
onProgress: async (phase, percent) => progress.push([phase, percent]),
})
assert.equal(result.status, 'rolled-back')
assert.deepEqual(progress, [['stopping', 15], ['switching', 35], ['restoring-data', 65], ['verifying', 85]])
assert.deepEqual(value.calls, ['suspend', 'restore:runtime-a-materialized:false', 'data:snapshot-a', 'resume'])
assert.equal((await value.journal.read()).phase, 'rolled-back')
assert.equal((await value.recovery.plan()).snapshot, null)
})
test('rejects a valid snapshot archive bound to a different deployment', async () => {
const value = await fixture()
const inspect = value.snapshots.inspect
value.snapshots.inspect = async id => ({ ...await inspect(id), environmentVersion: 'other-env' })
await assert.rejects(value.recovery.plan(), /does not describe/)
})
test('binds Stable rollback to the exact Bootstrap previous slot without a data snapshot', async () => {
const calls = []
const progress = []
const references = {
environment: { sha256: 'a'.repeat(64) },
pristine: { sha256: 'b'.repeat(64) },
runtime: { sha256: 'c'.repeat(64) },
systemPlugins: { sha256: 'd'.repeat(64) },
}
const current = {
id: 'deployment-record-current', authority: 'stable', targetSequence: 2,
dshVersion: '0.1.0-rc.8', environmentVersion: 'env-2', receiptTokens: ['stable-2'], ...references,
}
const previous = {
id: 'deployment-record-previous', authority: 'stable', targetSequence: 1,
dshVersion: '0.1.0-rc.7', environmentVersion: 'env-1', receiptTokens: ['stable-1'], ...references,
}
const recovery = new CompleteStateRecovery({
journal: { read: async () => undefined },
snapshots: {},
activator: {
rollbackDeployments: async () => ({ current, previous }),
rollback: async recordId => calls.push(recordId),
},
})
const plan = await recovery.plan()
assert.equal(plan.mode, 'stable')
assert.equal(plan.snapshot, null)
assert.equal(plan.previous.runtime, previous.id)
await assert.rejects(recovery.restore(plan.planId, {
requireConfirmation: true,
confirmDataLoss: true,
}), /snapshot/)
assert.deepEqual(calls, [])
assert.deepEqual(await recovery.restore(plan.planId, {
onProgress: async (phase, percent) => progress.push([phase, percent]),
}), { status: 'rolled-back', transactionId: null })
assert.deepEqual(calls, [previous.id])
assert.deepEqual(progress, [['switching', 35], ['verifying', 90]])
})
test('does not expose a rollback between Image and Managed Records with identical content', async () => {
const reference = { sha256: 'a'.repeat(64) }
const base = {
authority: 'stable', targetSequence: 1, dshVersion: '0.1.0-rc.7', environmentVersion: 'env-1',
environment: reference, pristine: reference, runtime: reference, systemPlugins: reference, receiptTokens: [],
}
const recovery = new CompleteStateRecovery({
journal: { read: async () => undefined },
snapshots: {},
activator: {
rollbackDeployments: async () => ({
current: { ...base, id: 'deployment-record-image' },
previous: { ...base, id: 'deployment-record-materialized' },
}),
},
})
assert.equal(await recovery.plan(), null)
})
@@ -0,0 +1,245 @@
import assert from 'node:assert/strict'
import { createHash } from 'node:crypto'
import { cp, mkdir, mkdtemp, readFile, readdir, readlink, rm, symlink, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { dirname, join, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import test from 'node:test'
import {
buildRuntime,
RuntimeSlots,
verifyNpmIntegrity,
verifyPatchSet,
verifyRuntimePatches,
} from '../../control-plane/modules/patch-manager/index.mjs'
import { verifyDshCompatibility } from '../lib/dsh-compatibility.mjs'
const containerRoot = dirname(dirname(dirname(fileURLToPath(import.meta.url))))
async function pristine() {
const root = await mkdtemp(join(tmpdir(), 'dsh-pristine-'))
await mkdir(join(root, 'lib'), { recursive: true })
await writeFile(join(root, 'lib', 'bin.js'), `#!/usr/bin/env node
const invocation = parseDshArgs(process.argv.slice(2), readVersion());
switch (invocation.mode) {
case "profile": {
const { runProfile } = await import("./profile-boot-fixture.js");
}
}
`)
await writeFile(join(root, 'lib', 'profile-boot-fixture.js'), 'import { r as runProfile } from "./profile-boot-implementation.js";\nexport { runProfile };\n')
await writeFile(join(root, 'lib', 'profile-boot-implementation.js'), `async function runProfile(options) {
\tprocess.on("SIGTERM", () => {
\t\tinterrupt(0);
\t});
\tprocess.on("SIGINT", () => { interrupt(130); });
\tconst ctx = await boot(NAME, rootConfig, patches, (hostCtx) => {
\t\thostCtx.provide(DSH_LAUNCH_ENVIRONMENT_KEY, options.environment);
\t});
\treturn {
\t\tctx,
\t\tshutdown
\t};
}
export { runProfile as r };
`)
const picker = join(root, 'node_modules/@deepseek-ai/dsh-host-directory-picker-browse/lib')
const connection = join(root, 'node_modules/@deepseek-ai/dsh-client-connection/lib')
const theme = join(root, 'node_modules/@deepseek-ai/dsh-client-ui-theme/lib')
await mkdir(picker, { recursive: true })
await mkdir(connection, { recursive: true })
await mkdir(theme, { recursive: true })
await writeFile(join(picker, 'index.js'), 'const target = resolve(path ?? home);\n')
await writeFile(join(connection, 'client.js'), 'isLoopback: pageLocation === void 0 || isLoopbackHostname(pageLocation.hostname),\n')
await writeFile(join(theme, 'client.js'), 'var corner_shape_css_default = "@supports (corner-shape:superellipse(1.5)){:root{--dsw-corner-shape:superellipse(1.5)}*,:before,:after{corner-shape:var(--dsw-corner-shape)}}";\n')
await mkdir(join(root, 'node_modules/.bin'), { recursive: true })
await symlink('../tool/bin.js', join(root, 'node_modules/.bin/tool'))
return root
}
async function transportOwnedPristine() {
const root = await pristine()
await writeFile(
join(root, 'node_modules/@deepseek-ai/dsh-client-connection/lib/client.js'),
'isLoopback: transport?.ownsHost === true || pageLocation === void 0 || isLoopbackHostname(pageLocation.hostname),\n',
)
return root
}
async function environment(root, patches) {
const environmentRoot = join(root, 'environment')
await mkdir(join(environmentRoot, 'artifacts'), { recursive: true })
const artifacts = []
const references = []
for (const [index, path] of patches.entries()) {
const bytes = await readFile(path)
const sha256 = createHash('sha256').update(bytes).digest('hex')
const id = `patch-${String(index + 1)}`
await cp(path, join(environmentRoot, 'artifacts', id))
artifacts.push({
id,
mediaType: 'text/javascript',
sha256,
size: bytes.byteLength,
url: `https://example.invalid/${id}`,
})
references.push({ id, sha256 })
}
await writeFile(join(environmentRoot, 'environment.manifest.json'), JSON.stringify({
schema: 1,
manifestType: 'environment',
version: '1.0.0',
keyringGeneration: 1,
targetSequence: 1,
issuedAt: '2026-08-20T00:00:00.000Z',
artifacts,
bootstrapApi: 1,
components: [],
patches: references,
systemPlugins: [],
}))
return environmentRoot
}
test('rebuilds each Runtime from unchanged Pristine with the complete ordered Patch set', async () => {
const source = await pristine()
const originalPicker = await readFile(join(source, 'node_modules/@deepseek-ai/dsh-host-directory-picker-browse/lib/index.js'))
const root = await mkdtemp(join(tmpdir(), 'dsh-runtime-'))
const patches = [
join(containerRoot, 'environment/resources/patches/directory-picker.mjs'),
join(containerRoot, 'environment/resources/patches/browser-loopback.mjs'),
join(containerRoot, 'environment/resources/patches/managed-lifecycle.mjs'),
join(containerRoot, 'environment/resources/patches/native-corners.mjs'),
]
const first = await buildRuntime({ pristineRoot: source, versionsRoot: join(root, 'versions'), runtimeId: 'one', patchPaths: patches })
const second = await buildRuntime({ pristineRoot: source, versionsRoot: join(root, 'versions'), runtimeId: 'two', patchPaths: patches })
assert.deepEqual(await readFile(join(source, 'node_modules/@deepseek-ai/dsh-host-directory-picker-browse/lib/index.js')), originalPicker)
for (const runtime of [first, second]) {
assert.match(await readFile(join(runtime, 'package/node_modules/@deepseek-ai/dsh-host-directory-picker-browse/lib/index.js'), 'utf8'), /DSH_DEFAULT_WORKSPACE/)
assert.match(await readFile(join(runtime, 'package/node_modules/@deepseek-ai/dsh-client-connection/lib/client.js'), 'utf8'), /isLoopback: true/)
assert.match(await readFile(join(runtime, 'package/lib/bin.js'), 'utf8'), /prepareManagedInvocation/)
assert.match(await readFile(join(runtime, 'package/lib/profile-boot-implementation.js'), 'utf8'), /managedSigtermHandler/)
assert.match(await readFile(join(runtime, 'package/node_modules/@deepseek-ai/dsh-client-ui-theme/lib/client.js'), 'utf8'), /corner_shape_css_default = ""/)
assert.equal(await readlink(join(runtime, 'package/node_modules/.bin/tool')), '../tool/bin.js')
}
})
test('rejects a Patch mismatch without publishing a partial Runtime', async () => {
const source = await pristine()
await writeFile(join(source, 'node_modules/@deepseek-ai/dsh-client-connection/lib/client.js'), 'changed upstream\n')
const root = await mkdtemp(join(tmpdir(), 'dsh-runtime-failure-'))
await assert.rejects(buildRuntime({
pristineRoot: source,
versionsRoot: join(root, 'versions'),
runtimeId: 'broken',
patchPaths: [join(containerRoot, 'environment/resources/patches/browser-loopback.mjs')],
}), /found 0/)
await assert.rejects(readFile(join(root, 'versions/broken/package/lib/bin.js')), { code: 'ENOENT' })
})
test('builds a Runtime from the transport-owned browser classification', async () => {
const source = await transportOwnedPristine()
const root = await mkdtemp(join(tmpdir(), 'dsh-runtime-transport-loopback-'))
const runtime = await buildRuntime({
pristineRoot: source,
versionsRoot: join(root, 'versions'),
runtimeId: 'transport-loopback',
patchPaths: [join(containerRoot, 'environment/resources/patches/browser-loopback.mjs')],
})
assert.equal(
await readFile(join(runtime, 'package/node_modules/@deepseek-ai/dsh-client-connection/lib/client.js'), 'utf8'),
'isLoopback: true,\n',
)
})
test('verifies mandatory Patch Artifacts and their applied Runtime effects before startup', async () => {
const source = await pristine()
const root = await mkdtemp(join(tmpdir(), 'dsh-runtime-verification-'))
const patches = [
join(containerRoot, 'environment/resources/patches/directory-picker.mjs'),
join(containerRoot, 'environment/resources/patches/browser-loopback.mjs'),
join(containerRoot, 'environment/resources/patches/managed-lifecycle.mjs'),
join(containerRoot, 'environment/resources/patches/native-corners.mjs'),
]
const runtimeRoot = await buildRuntime({
pristineRoot: source,
versionsRoot: join(root, 'versions'),
runtimeId: 'verified',
patchPaths: patches,
})
const environmentRoot = await environment(root, patches)
assert.deepEqual(await verifyRuntimePatches({ runtimeRoot, environmentRoot }), ['patch-1', 'patch-2', 'patch-3', 'patch-4'])
await writeFile(
join(runtimeRoot, 'package/node_modules/@deepseek-ai/dsh-client-connection/lib/client.js'),
'isLoopback: false,\n',
)
await assert.rejects(verifyRuntimePatches({ runtimeRoot, environmentRoot }), /Patch verification failed/)
await writeFile(join(environmentRoot, 'artifacts', 'patch-2'), 'modified artifact\n')
await assert.rejects(verifyRuntimePatches({ runtimeRoot, environmentRoot }), /differs from the Environment Manifest/)
})
test('checks npm SHA-512 integrity and tracks Runtime current/previous slots', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-runtime-slots-'))
const tarball = join(root, 'dsh.tgz')
const bytes = Buffer.from('tarball fixture')
await writeFile(tarball, bytes)
await verifyNpmIntegrity(tarball, `sha512-${createHash('sha512').update(bytes).digest('base64')}`)
await assert.rejects(verifyNpmIntegrity(tarball, `sha512-${Buffer.alloc(64).toString('base64')}`), { code: 'TRUST_ARTIFACT_MISMATCH' })
await mkdir(join(root, 'runtime/versions/one'), { recursive: true })
await mkdir(join(root, 'runtime/versions/two'), { recursive: true })
const slots = new RuntimeSlots(join(root, 'runtime'))
await slots.promote('one')
await slots.promote('two')
assert.deepEqual(await slots.state(), { current: 'two', previous: 'one' })
await slots.rollback()
assert.deepEqual(await slots.state(), { current: 'one', previous: 'two' })
})
test('prunes only Runtime versions outside current and previous slots', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-runtime-prune-'))
const slots = new RuntimeSlots(root)
for (const version of ['one', 'two', 'old']) await mkdir(join(root, 'versions', version), { recursive: true })
const imageSeed = await mkdtemp(join(tmpdir(), 'dsh-runtime-image-seed-'))
await writeFile(join(imageSeed, 'sentinel'), 'keep')
await symlink(imageSeed, join(root, 'versions/image-old'), 'dir')
await slots.promote('one')
await slots.promote('two')
assert.deepEqual(await slots.prune(), ['image-old', 'old'])
assert.deepEqual((await readdir(join(root, 'versions'))).sort(), ['one', 'two'])
assert.equal(await readFile(join(imageSeed, 'sentinel'), 'utf8'), 'keep')
})
test('builds and verifies the real DSH 0.1.5 Runtime when its npm tree is provided', {
skip: process.env.DSH_COMPAT_PACKAGE_ROOT === undefined,
}, async t => {
const source = resolve(process.env.DSH_COMPAT_PACKAGE_ROOT)
const root = await mkdtemp(join(tmpdir(), 'dsh-real-runtime-'))
t.after(() => rm(root, { recursive: true, force: true }))
const patches = [
join(containerRoot, 'environment/resources/patches/directory-picker.mjs'),
join(containerRoot, 'environment/resources/patches/browser-loopback.mjs'),
join(containerRoot, 'environment/resources/patches/managed-lifecycle.mjs'),
join(containerRoot, 'environment/resources/patches/native-corners.mjs'),
]
const runtime = await buildRuntime({
pristineRoot: source,
versionsRoot: join(root, 'versions'),
runtimeId: 'dsh-0.1.5-rc.1',
patchPaths: patches,
})
const packageRoot = join(runtime, 'package')
await verifyPatchSet(packageRoot, patches)
assert.deepEqual(await verifyDshCompatibility({
packageRoot,
expectedVersion: '0.1.5-rc.1',
home: join(root, 'home'),
}), {
name: '@deepseek-ai/dsh',
version: '0.1.5-rc.1',
packages: 10,
remoteMethods: 3,
webRows: 5,
})
})
@@ -0,0 +1,156 @@
import assert from 'node:assert/strict'
import { execFile, spawnSync } from 'node:child_process'
import { lstat, mkdtemp, mkdir, readFile, readlink, symlink, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { fileURLToPath, pathToFileURL } from 'node:url'
import { promisify } from 'node:util'
import test from 'node:test'
import { provisionPlatformSeed } from '../stage0/lib/seed.mjs'
import { TrustLedger } from '../stage0/lib/ledger.mjs'
import { verifyRuntimePatches } from '../../control-plane/modules/patch-manager/index.mjs'
import { writeDshEntrypointFixture } from './fixtures/dsh-package.mjs'
const execute = promisify(execFile)
test('checked-in development seed is Recovery-signed and contains no private key', async () => {
const trust = new URL('../seed/trust/', import.meta.url)
const publicKey = (await readFile(new URL('recovery-root.spki.base64', trust), 'utf8')).trim()
const keyring = await readFile(new URL('keyring.json', trust))
const signature = JSON.parse(await readFile(new URL('keyring.sig.json', trust), 'utf8'))
const root = await mkdtemp(join(tmpdir(), 'dsh-seed-trust-'))
const accepted = await new TrustLedger(root, publicKey).acceptKeyring(keyring, signature)
assert.equal(accepted.generation, 1)
for (const name of ['recovery-root.spki.base64', 'keyring.json', 'keyring.sig.json', 'DEVELOPMENT_FIXTURE']) {
assert.doesNotMatch(await readFile(new URL(name, trust), 'utf8'), /PRIVATE KEY/)
}
})
test('registers immutable image seed trees once and preserves later current links', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-platform-seed-'))
const seed = join(root, 'seed')
const data = join(root, 'data')
for (const [group, version] of [['environment', 'env-one'], ['runtime', 'runtime-one']]) {
await mkdir(join(seed, group, version), { recursive: true })
await writeFile(join(seed, group, 'VERSION'), `${version}\n`)
}
await mkdir(join(seed, 'pristine', 'runtime-one', 'node_modules/.bin'), { recursive: true })
await symlink('../package/bin.js', join(seed, 'pristine', 'runtime-one', 'node_modules/.bin/tool'))
await mkdir(join(seed, 'system-plugins', 'env-one'), { recursive: true })
const first = await provisionPlatformSeed(seed, data)
assert.equal(first.seededLinks.length, 4)
for (const [entry, source] of [
['store/environments/versions/env-one', 'environment/env-one'],
['store/runtimes/versions/runtime-one', 'runtime/runtime-one'],
['store/pristine/runtime-one', 'pristine/runtime-one'],
['store/system-plugins/versions/env-one', 'system-plugins/env-one'],
]) {
assert.equal((await lstat(join(data, entry))).isSymbolicLink(), true)
assert.equal(await readlink(join(data, entry)), join(seed, source))
}
assert.equal(await readlink(join(data, 'store/environments', 'current')), 'versions/env-one')
assert.equal(await readlink(join(data, 'store/pristine/runtime-one/node_modules/.bin/tool')), '../package/bin.js')
assert.equal((await lstat(join(data, 'store/snapshots'))).isDirectory(), true)
await writeFile(join(data, 'state/updater', 'sentinel'), 'keep')
const repeated = await provisionPlatformSeed(seed, data)
assert.deepEqual(repeated.seededLinks, [])
assert.equal(await readFile(join(data, 'state/updater', 'sentinel'), 'utf8'), 'keep')
})
test('preserves an existing materialized version instead of replacing it with an image seed link', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-platform-materialized-'))
const seed = join(root, 'seed')
const data = join(root, 'data')
for (const [group, version] of [['environment', 'env-one'], ['runtime', 'runtime-one']]) {
await mkdir(join(seed, group, version), { recursive: true })
await writeFile(join(seed, group, 'VERSION'), `${version}\n`)
}
await mkdir(join(seed, 'pristine/runtime-one'), { recursive: true })
await mkdir(join(seed, 'system-plugins/env-one'), { recursive: true })
await mkdir(join(data, 'store/runtimes/versions/runtime-one'), { recursive: true })
await writeFile(join(data, 'store/runtimes/versions/runtime-one/sentinel'), 'materialized')
await provisionPlatformSeed(seed, data)
assert.equal((await lstat(join(data, 'store/runtimes/versions/runtime-one'))).isDirectory(), true)
assert.equal(await readFile(join(data, 'store/runtimes/versions/runtime-one/sentinel'), 'utf8'), 'materialized')
})
test('repairs broken image entries and current slots after an image replacement', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-platform-replaced-image-'))
const seed = join(root, 'seed')
const data = join(root, 'data')
for (const [group, version] of [['environment', 'env-one'], ['runtime', 'runtime-one']]) {
await mkdir(join(seed, group, version), { recursive: true })
await writeFile(join(seed, group, 'VERSION'), `${version}\n`)
}
await mkdir(join(seed, 'pristine/runtime-one'), { recursive: true })
await mkdir(join(seed, 'system-plugins/env-one'), { recursive: true })
await mkdir(join(data, 'store/runtimes/versions'), { recursive: true })
await symlink('/missing-old-image/runtime-old', join(data, 'store/runtimes/versions/runtime-old'))
await symlink('versions/runtime-old', join(data, 'store/runtimes/current'))
await provisionPlatformSeed(seed, data)
assert.equal(await readlink(join(data, 'store/runtimes/versions/runtime-one')), join(seed, 'runtime/runtime-one'))
assert.equal(await readlink(join(data, 'store/runtimes/current')), 'versions/runtime-one')
assert.equal((await lstat(join(data, 'store/runtimes/current'))).isSymbolicLink(), true)
})
test('rejects seed IDs that could escape their image roots', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-platform-invalid-seed-'))
const seed = join(root, 'seed')
await mkdir(join(seed, 'environment'), { recursive: true })
await mkdir(join(seed, 'runtime'), { recursive: true })
await writeFile(join(seed, 'environment/VERSION'), '../escape\n')
await writeFile(join(seed, 'runtime/VERSION'), 'runtime-one\n')
await assert.rejects(provisionPlatformSeed(seed, join(root, 'data')), /Environment seed ID is invalid/)
})
test('builds a self-contained Bootstrap seed and preserves npm bin links', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-build-seed-'))
const installed = join(root, 'installed')
const output = join(root, 'output')
await writeDshEntrypointFixture(installed)
await writeFile(join(installed, 'package.json'), JSON.stringify({ name: '@deepseek-ai/dsh', version: '0.1.0-rc.fixture' }))
const picker = join(installed, 'node_modules/@deepseek-ai/dsh-host-directory-picker-browse/lib')
const connection = join(installed, 'node_modules/@deepseek-ai/dsh-client-connection/lib')
const theme = join(installed, 'node_modules/@deepseek-ai/dsh-client-ui-theme/lib')
await mkdir(picker, { recursive: true })
await mkdir(connection, { recursive: true })
await mkdir(theme, { recursive: true })
await writeFile(join(picker, 'index.js'), 'const target = resolve(path ?? home);\n')
await writeFile(join(connection, 'client.js'), 'isLoopback: pageLocation === void 0 || isLoopbackHostname(pageLocation.hostname),\n')
await writeFile(join(theme, 'client.js'), 'var corner_shape_css_default = "@supports (corner-shape:superellipse(1.5)){:root{--dsw-corner-shape:superellipse(1.5)}*,:before,:after{corner-shape:var(--dsw-corner-shape)}}";\n')
await mkdir(join(installed, 'node_modules/.bin'), { recursive: true })
await symlink('../tool/bin.js', join(installed, 'node_modules/.bin/tool'))
await execute(process.execPath, [fileURLToPath(new URL('../tools/build-seed.mjs', import.meta.url)), installed, output])
const bootstrapVersion = (await readFile(new URL('../bootstrap/VERSION', import.meta.url), 'utf8')).trim()
const bootstrapRoot = join(output, 'bootstrap', bootstrapVersion)
const contracts = await import(pathToFileURL(join(bootstrapRoot, 'platform/lib/contracts.mjs')).href)
const { parseImageInventory } = await import('../lib/deployment-contracts.mjs')
assert.equal(typeof contracts.parseStable, 'function')
const inventory = parseImageInventory(await readFile(join(output, 'inventory.json')))
assert.equal(inventory.authority, 'development')
assert.equal(inventory.targetSequence, 0)
assert.equal(inventory.bootstrap.version, bootstrapVersion)
assert.equal(inventory.deployment.dshVersion, '0.1.0-rc.fixture')
assert.equal((await lstat(join(bootstrapRoot, 'control-plane/services/management/node_modules/ws/index.js'))).isFile(), true)
assert.equal((await lstat(join(bootstrapRoot, 'control-plane/services/management/node_modules/@xterm/xterm/css/xterm.css'))).isFile(), true)
assert.equal((await lstat(join(bootstrapRoot, 'control-plane/services/outbound-proxy/index.mjs'))).isFile(), true)
assert.equal((await lstat(join(bootstrapRoot, 'control-plane/services/access-manager/index.mjs'))).isFile(), true)
await assert.rejects(lstat(join(bootstrapRoot, 'control-plane/skills')), { code: 'ENOENT' })
const skillArchive = join(
output, 'environments', inventory.deployment.environment.id, 'artifacts', 'system-skill-catalog',
)
const skillListing = spawnSync('tar', ['-tzf', skillArchive], { encoding: 'utf8' })
assert.equal(skillListing.status, 0, skillListing.stderr)
assert.match(skillListing.stdout, /^skills\/dsh-docker-operations\/SKILL\.md$/m)
assert.equal(await readlink(join(output, 'pristine/0.1.0-rc.fixture/node_modules/.bin/tool')), '../tool/bin.js')
assert.equal(await readlink(join(output, 'runtime/0.1.0-rc.fixture/package/node_modules/.bin/tool')), '../tool/bin.js')
assert.deepEqual(await verifyRuntimePatches({
runtimeRoot: join(output, 'runtimes', inventory.deployment.runtime.id),
environmentRoot: join(output, 'environments', inventory.deployment.environment.id),
}), ['directory-picker', 'browser-loopback', 'managed-lifecycle', 'native-corners'])
})
@@ -0,0 +1,143 @@
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import test from 'node:test'
import { buildSystemPluginClient } from '../tools/build-system-plugin-client.mjs'
const root = new URL('../../environment/resources/plugins/settings-document-editor/package/', import.meta.url)
test('Settings Document Editor is an optional DSH web System Plugin', async () => {
const metadata = JSON.parse(await readFile(new URL('package.json', root)))
const patch = JSON.parse(await readFile(new URL('cordis.patch.json', root)))
assert.equal(metadata.name, '@dsh-docker/settings-document-editor')
assert.equal(metadata.dshDocker.description.zh, '在浏览器中查看和编辑 DSH 配置文件。')
assert.equal(metadata.dsh.client.platform, 'web')
assert.ok(metadata.dsh.client.inject.includes('@deepseek-ai/dsh-api-remotes'))
assert.equal(metadata.dsh.client.inject.includes('@deepseek-ai/dsh-client-runtime'), false)
assert.equal(metadata.peerDependencies['@deepseek-ai/dsh-client-runtime'], undefined)
assert.equal(metadata.exports['./client'], './lib/client.bundle.js')
assert.equal(patch[0].insert[0].id, 'dsh-docker.settings-document-editor.plugin')
})
test('Settings Document Editor checked-in bundle matches its source', async () => {
const bundle = await readFile(new URL('lib/client.bundle.js', root), 'utf8')
const rebuilt = await buildSystemPluginClient({
pluginId: '@dsh-docker/settings-document-editor',
sourcePath: new URL('lib/client.js', root),
stylePath: new URL('lib/style.module.css', root),
})
assert.equal(bundle, rebuilt)
assert.doesNotThrow(() => new Function(bundle))
assert.match(bundle, /^window\.__ModuleLoader__\.load\(/)
})
test('Settings Document Editor replaces and restores both pathless DSH open actions', async () => {
const source = await readFile(new URL('lib/client.js', root), 'utf8')
assert.match(source, /\/_dsh_platform\/plugin-api\/v1\/settings-document/)
assert.doesNotMatch(source, /\/_dsh_platform\/api\/v1\/settings-document/)
assert.match(source, /interceptDocumentOpen\([\s\S]*connection\.api\.settings,[\s\S]*'openDocument'/)
assert.match(source, /interceptDocumentOpen\([\s\S]*child\.remote\.settings,[\s\S]*'openSettingsDocument'/)
assert.match(source, /ctx\.inject\(\['remote\.settings'\]/)
assert.match(source, /\{ ok: true, value: \{ opened: true \} \}/)
assert.match(source, /\{ result: \{ ok: true, value: \{ opened: true \} \} \}/)
assert.doesNotMatch(source, /openDocument\(.*path|body:.*path/s)
assert.match(source, /settings\.dshDocumentEditor/)
assert.match(source, /shell\.overlay/)
assert.match(source, /createPortal\([\s\S]*document\.body\)/)
assert.match(source, /zh: \{[\s\S]*en: \{/)
assert.match(source, /error\.status === 409/)
assert.match(source, /event\.key\.toLowerCase\(\) === 's'/)
assert.match(source, /className: css\.lineNumbers/)
assert.match(source, /lineNumbers\.current\.scrollTop = event\.currentTarget\.scrollTop/)
assert.match(source, /wrap: 'off'/)
})
test('Settings Document Editor intercepts old and getter-backed open methods with their native result shapes', async () => {
const source = await readFile(new URL('lib/client.js', root), 'utf8')
const definition = source.slice(source.indexOf('function interceptDocumentOpen'), source.indexOf('\n\nexport function apply'))
const interceptDocumentOpen = new Function(`${definition}; return interceptDocumentOpen`)()
let opens = 0
const controller = { open: async () => { opens += 1 } }
for (const [method, result, getterBacked] of [
['openDocument', { result: { ok: true, value: { opened: true } } }, false],
['openSettingsDocument', { ok: true, value: { opened: true } }, true],
]) {
const original = async () => ({ original: true })
const target = getterBacked ? {} : { [method]: original }
if (getterBacked) Object.defineProperty(target, method, {
configurable: true,
enumerable: true,
get: () => original,
})
const restore = interceptDocumentOpen(target, method, controller, result)
assert.deepEqual(await target[method](), result)
restore()
assert.equal(target[method], original)
}
assert.equal(opens, 2)
})
test('Settings Document Editor follows DSH tokens and has a mobile layout', async () => {
const style = await readFile(new URL('lib/style.module.css', root), 'utf8')
assert.match(style, /--dsw-alias-label-primary/)
assert.match(style, /--dsw-alias-border-l2/)
assert.match(style, /@media \(max-width: 640px\)/)
assert.match(style, /height: 100dvh/)
assert.match(style, /\.editorFrame \{[\s\S]*grid-template-columns: auto minmax\(0, 1fr\)/)
assert.match(style, /\.lineNumbers \{[\s\S]*text-align: right/)
assert.match(style, /\.editorFrame:focus-within \{ border-color: var\(--dsw-alias-border-l2\); \}/)
assert.match(style, /\.lineNumbers \{[\s\S]*background: color-mix\(in srgb, var\(--dsw-alias-bg-base\) 72%, var\(--dsw-alias-bg-module-platform\)\)/)
assert.match(style, /\.overlay,[\s\S]*?\.overlay \*::after \{[^}]*corner-shape: round;/)
assert.doesNotMatch(style, /--dsw-alias-state-business-primary/)
assert.equal((style.match(/ui-monospace/g) ?? []).length, 2)
assert.doesNotMatch(style, /#[0-9a-f]{3,8}\b/i)
})
test('Settings Document Editor state discards stale loads and rejects stale saves', async () => {
const source = await readFile(new URL('lib/client.js', root), 'utf8')
const definition = source.slice(source.indexOf('class DocumentController'), source.indexOf('function Editor('))
const calls = []
const responses = [
{ content: 'language: zh\n', revision: 'a'.repeat(64), exists: true },
{ content: 'language: en\n', revision: 'b'.repeat(64), exists: true },
]
const DocumentController = new Function('request', `${definition}; return DocumentController`)(async (method, body) => {
calls.push([method, body])
return responses.shift()
})
const controller = new DocumentController()
await controller.open()
assert.equal(controller.state.content, 'language: zh\n')
controller.edit('language: en\n')
await controller.save()
assert.equal(controller.state.content, 'language: en\n')
assert.equal(controller.state.savedContent, 'language: en\n')
assert.deepEqual(calls.map(call => call[0]), ['GET', 'PUT'])
controller.close()
controller.state.content = 'stale'
const conflict = Object.assign(new Error('changed'), { status: 409 })
const FailingController = new Function('request', `${definition}; return DocumentController`)(async () => { throw conflict })
const failing = new FailingController()
failing.state.content = 'old content'
failing.state.savedContent = 'old content'
failing.state.revision = 'c'.repeat(64)
await failing.open()
assert.equal(failing.state.content, '')
assert.equal(failing.state.revision, null)
assert.equal(failing.state.error, 'changed')
let conflictCall = 0
const ConflictController = new Function('request', `${definition}; return DocumentController`)(async () => {
conflictCall += 1
if (conflictCall === 1) return { content: 'one: 1\n', revision: 'd'.repeat(64), exists: true }
throw conflict
})
const stale = new ConflictController()
await stale.open()
stale.edit('one: 2\n')
await stale.save()
assert.equal(stale.state.conflict, true)
assert.equal(stale.state.savedContent, 'one: 1\n')
assert.equal(stale.state.content, 'one: 2\n')
})
@@ -0,0 +1,117 @@
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import test from 'node:test'
import { buildSystemPluginClient } from '../tools/build-system-plugin-client.mjs'
const root = new URL('../../environment/resources/plugins/settings-navigation/package/', import.meta.url)
test('Settings Navigation is a browser-only optional System Plugin', async () => {
const metadata = JSON.parse(await readFile(new URL('package.json', root)))
const patch = JSON.parse(await readFile(new URL('cordis.patch.json', root)))
assert.equal(metadata.name, '@dsh-docker/settings-navigation')
assert.equal(metadata.dshDocker.description.zh, '优化 DSH 设置目录滚动和窄屏分级导航。')
assert.equal(metadata.dsh.client.platform, 'web')
assert.equal(metadata.dsh.client.inject.includes('@deepseek-ai/dsh-client-runtime'), false)
assert.equal(metadata.peerDependencies['@deepseek-ai/dsh-client-runtime'], undefined)
assert.equal(metadata.exports['./client'], './lib/client.bundle.js')
assert.equal(patch[0].insert[0].id, 'dsh-docker.settings-navigation.plugin')
assert.equal(patch[0].insert[0].name, metadata.name)
})
test('Settings Navigation checked-in bundle matches its source', async () => {
const bundle = await readFile(new URL('lib/client.bundle.js', root), 'utf8')
const style = await readFile(new URL('lib/style.module.css', root), 'utf8')
const rebuilt = await buildSystemPluginClient({
pluginId: '@dsh-docker/settings-navigation',
sourcePath: new URL('lib/client.js', root),
stylePath: new URL('lib/style.module.css', root),
})
assert.equal(bundle, rebuilt)
assert.doesNotThrow(() => new Function(bundle))
assert.match(bundle, /^window\.__ModuleLoader__\.load\(/)
assert.doesNotMatch(style, /corner-shape/)
assert.doesNotMatch(style, /\.header button:not\(\[aria-label\]\)/)
})
test('Settings Navigation recognizes the Settings shell without text or generated classes', async () => {
const source = await readFile(new URL('lib/client.js', root), 'utf8')
const matcher = source.slice(source.indexOf('function directChildren'), source.indexOf('\nfunction findSettingsDialog'))
const matchSettingsDialog = new Function(`${matcher}; return matchSettingsDialog`)()
class Element {
constructor(tagName, ownerDocument) {
this.tagName = tagName.toUpperCase()
this.ownerDocument = ownerDocument
this.children = []
this.attributes = new Map()
}
append(...children) { this.children.push(...children) }
contains(target) { return this === target || this.children.some(child => child.contains(target)) }
getAttribute(name) { return this.attributes.get(name) ?? null }
setAttribute(name, value) { this.attributes.set(name, value) }
querySelector(selector) {
return this.querySelectorAll(selector)[0] ?? null
}
querySelectorAll(selector) {
const result = []
for (const child of this.children) {
if (selector === 'button' && child.tagName === 'BUTTON') result.push(child)
result.push(...child.querySelectorAll(selector))
}
return result
}
}
const ids = new Map()
const document = { getElementById: id => ids.get(id) ?? null }
const dialog = new Element('div', document)
dialog.setAttribute('role', 'dialog')
dialog.setAttribute('aria-modal', 'true')
dialog.setAttribute('aria-labelledby', 'settings-title')
const nav = new Element('nav', document)
const title = new Element('div', document)
ids.set('settings-title', title)
const list = new Element('div', document)
const selected = new Element('button', document)
selected.setAttribute('aria-current', 'true')
list.append(selected)
nav.append(title, list)
const content = new Element('div', document)
const header = new Element('div', document)
header.append(new Element('button', document))
content.append(header, new Element('div', document))
dialog.append(nav, content)
assert.equal(matchSettingsDialog(dialog)?.current, selected)
dialog.append(new Element('aside', document))
assert.equal(matchSettingsDialog(dialog), null)
dialog.children.pop()
dialog.setAttribute('aria-labelledby', 'unknown')
assert.equal(matchSettingsDialog(dialog), null)
})
test('Settings Navigation owns compact navigation without moving native nodes', async () => {
const source = await readFile(new URL('lib/client.js', root), 'utf8')
const style = await readFile(new URL('lib/style.module.css', root), 'utf8')
assert.match(source, /const COMPACT_WIDTH = 640/)
assert.match(source, /shell\.overlay/)
assert.match(source, /IconChevronLeftOutline14/)
assert.match(source, /MutationObserverImpl/)
assert.match(source, /ResizeObserverImpl/)
assert.match(source, /attributeFilter: \['aria-current', 'aria-labelledby'\]/)
assert.match(source, /\['dialog', 'nav', 'navTitle', 'navList', 'content', 'header', 'options'\]/)
assert.match(source, /\.every\(key => next\[key\] === this\.match\[key\]\)/)
assert.match(source, /this\.view = match\.buttons\.indexOf\(match\.current\) > 0 \? 'detail' : 'directory'/)
assert.match(source, /this\.modeInitialized && compact && !this\.compact/)
assert.match(source, /this\.directoryScrollTop = this\.match\.navList\.scrollTop/)
assert.match(source, /current\?\.focus\(\{ preventScroll: true \}\)/)
assert.match(source, /this\.requestFrame\(\(\) => \{ navList\.scrollTop = scrollTop \}\)/)
assert.match(source, /delete this\.match\.dialog\.dataset\.dshSettingsNavigationMode/)
assert.match(source, /delete this\.match\.dialog\.dataset\.dshSettingsNavigationView/)
assert.match(source, /node\.classList\.remove\(className\)/)
assert.doesNotMatch(source, /textContent|innerHTML|appendChild\(match\.|replaceChild/)
assert.match(style, /scrollbar-gutter: stable/)
assert.match(style, /data-dsh-settings-navigation-view='directory'/)
assert.match(style, /data-dsh-settings-navigation-view='detail'/)
assert.match(style, /--dsw-alias-label-primary/)
assert.doesNotMatch(style, /#[0-9a-f]{3,8}\b/i)
})
@@ -0,0 +1,111 @@
import assert from 'node:assert/strict'
import { chmod, lstat, mkdir, mkdtemp, readFile, readlink, symlink, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { PersistentStateSnapshots } from '../../control-plane/modules/updater/lib/snapshots.mjs'
import { SnapshotClient } from '../../control-plane/modules/updater/lib/snapshot-client.mjs'
import { UserPluginSnapshots } from '../../control-plane/modules/plugin-manager/user-snapshots.mjs'
import { LocalApiClient } from '../../control-plane/modules/updater/lib/client.mjs'
import { createSnapshotServer, listenSnapshots } from '../stage0/lib/snapshot-server.mjs'
async function fixture(options = {}) {
const root = await mkdtemp(join(tmpdir(), 'dsh-snapshots-'))
const sourceRoot = join(root, 'home')
await mkdir(join(sourceRoot, 'nested'), { recursive: true })
return {
root,
sourceRoot,
snapshots: new PersistentStateSnapshots({ root: join(root, 'snapshots'), sourceRoot, ...options }),
}
}
const state = {
id: 'before-rc8',
runtimeId: '0.1.0-rc.7-1',
environmentVersion: '1.0.0',
dshVersion: '0.1.0-rc.7',
}
test('snapshots and restores complete DSH_HOME contents and permissions', async () => {
const { sourceRoot, snapshots } = await fixture({ now: () => new Date('2026-08-19T00:00:00.000Z') })
await writeFile(join(sourceRoot, '.config'), 'before')
await writeFile(join(sourceRoot, 'nested', 'database'), 'rows')
await chmod(join(sourceRoot, 'nested', 'database'), 0o640)
await symlink('nested/database', join(sourceRoot, 'database-link'))
const createProgress = []
const snapshot = await snapshots.create({ ...state, onProgress: value => { createProgress.push(value) } })
assert.equal(snapshot.createdAt, '2026-08-19T00:00:00.000Z')
assert.equal(createProgress.at(-1).processedItems, createProgress.at(-1).totalItems)
await writeFile(join(sourceRoot, '.config'), 'after')
await writeFile(join(sourceRoot, 'new-data'), 'remove me')
const restoreProgress = []
await snapshots.restore(state.id, { onProgress: value => { restoreProgress.push(value) } })
assert.equal(await readFile(join(sourceRoot, '.config'), 'utf8'), 'before')
assert.equal(await readFile(join(sourceRoot, 'nested', 'database'), 'utf8'), 'rows')
assert.equal((await lstat(join(sourceRoot, 'nested', 'database'))).mode & 0o777, 0o640)
assert.equal(await readlink(join(sourceRoot, 'database-link')), 'nested/database')
await assert.rejects(readFile(join(sourceRoot, 'new-data')), { code: 'ENOENT' })
assert.equal(restoreProgress.at(-1).processedItems, restoreProgress.at(-1).totalItems)
assert.equal((await snapshots.list())[0].id, state.id)
})
test('verifies a snapshot before clearing current DSH_HOME', async () => {
const { sourceRoot, snapshots } = await fixture()
await writeFile(join(sourceRoot, 'database'), 'before')
const snapshot = await snapshots.create(state)
await writeFile(join(snapshot.path, 'data.tar.gz'), 'corrupt')
await writeFile(join(sourceRoot, 'database'), 'current')
await assert.rejects(snapshots.restore(state.id), { code: 'TRUST_ARTIFACT_MISMATCH' })
assert.equal(await readFile(join(sourceRoot, 'database'), 'utf8'), 'current')
})
test('does not publish a partial snapshot when archive creation fails', async () => {
const { snapshots } = await fixture({ runImpl: async () => { throw new Error('no space left') } })
await assert.rejects(snapshots.create(state), /no space/)
assert.deepEqual(await snapshots.list(), [])
})
test('removing an absent snapshot is idempotent while corrupt snapshots remain errors', async () => {
const { snapshots } = await fixture()
assert.equal(await snapshots.remove('already-removed'), false)
const snapshot = await snapshots.create(state)
await writeFile(join(snapshot.path, 'data.tar.gz'), 'corrupt')
await assert.rejects(snapshots.remove(state.id), { code: 'TRUST_ARTIFACT_MISMATCH' })
})
test('Stage-0 serves only fixed DSH and Web Profile snapshot scopes', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-snapshot-server-'))
const dshHome = join(root, 'dsh')
const profileRoot = join(dshHome, 'profiles', 'web')
await mkdir(profileRoot, { recursive: true })
await writeFile(join(dshHome, 'settings.yaml'), 'before')
await writeFile(join(profileRoot, 'package.json'), '{"before":true}')
const server = createSnapshotServer({
dshSnapshots: new PersistentStateSnapshots({ root: join(root, 'snapshots'), sourceRoot: dshHome }),
userPluginSnapshots: new UserPluginSnapshots({ root: join(root, 'user-plugin-snapshots'), profileRoot }),
})
const socket = join(root, 'snapshot.sock')
await listenSnapshots(server, socket)
const api = new LocalApiClient(socket)
const dsh = new SnapshotClient(api, 'dsh')
const userPlugin = new SnapshotClient(api, 'user-plugin')
try {
const created = await dsh.create(state)
assert.equal(created.id, state.id)
assert.equal(created.path, undefined)
await writeFile(join(dshHome, 'settings.yaml'), 'after')
await dsh.restore(state.id)
assert.equal(await readFile(join(dshHome, 'settings.yaml'), 'utf8'), 'before')
const plugin = await userPlugin.create('profile-before-change')
assert.equal(plugin.id, 'profile-before-change')
assert.equal(plugin.path, undefined)
await writeFile(join(profileRoot, 'package.json'), '{"after":true}')
await userPlugin.restore('profile-before-change')
assert.equal(await readFile(join(profileRoot, 'package.json'), 'utf8'), '{"before":true}')
} finally {
await new Promise(resolve => server.close(resolve))
}
})
@@ -0,0 +1,811 @@
import assert from 'node:assert/strict'
import { cp, lstat, mkdtemp, mkdir, readFile, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { request } from 'node:http'
import { spawn, spawnSync } from 'node:child_process'
import test from 'node:test'
import { BootstrapManager } from '../stage0/lib/slots.mjs'
import {
BootstrapSupervisor,
bootstrapLaunchCommand,
bootstrapLaunchEnvironment,
} from '../stage0/lib/supervisor.mjs'
import { createTrustServer, listenUnix } from '../stage0/lib/trust-server.mjs'
import { createRecoveryServer, listenRecovery } from '../stage0/lib/recovery-server.mjs'
import {
ProxyLaunchBroker,
createProxyLaunchServer,
listenProxyLaunch,
proxyLaunchCommand,
proxyLaunchEnvironment,
} from '../stage0/lib/proxy-launch-server.mjs'
import {
AccessLaunchBroker,
accessLaunchCommand,
accessLaunchEnvironment,
createAccessLaunchServer,
listenAccessLaunch,
} from '../stage0/lib/access-launch-server.mjs'
import { TrustLedger } from '../stage0/lib/ledger.mjs'
import { VerifiedObjectStore } from '../stage0/lib/artifacts.mjs'
import { LocalApiClient } from '../../control-plane/modules/updater/lib/client.mjs'
import { keyPair } from './helpers.mjs'
import {
deriveImageBuildId,
deriveRecordId,
parseImageInventory,
recordsFromImageInventory,
} from '../lib/deployment-contracts.mjs'
import { hashTree } from '../lib/tree-hash.mjs'
test('Stage-0 checks the Management status endpoint before managed file mutations', async () => {
const source = await readFile(new URL('../stage0/index.mjs', import.meta.url), 'utf8')
assert.match(source, /management\.request\('GET', '\/_dsh_platform\/api\/v1\/status'\)/)
assert.doesNotMatch(source, /management\.status\(\)/)
})
test('Bootstrap releases its parent IPC channel after shutdown', async () => {
const source = await readFile(new URL('../bootstrap/index.mjs', import.meta.url), 'utf8')
assert.match(source, /process\.off\('message', onRecoveryMessage\)/)
assert.match(source, /if \(process\.connected\) process\.disconnect\(\)/)
})
async function bootstrap(root, version, behavior) {
const directory = join(root, version)
await mkdir(directory, { recursive: true })
await writeFile(join(directory, 'index.mjs'), behavior)
return directory
}
async function imageBootstrap(root, version, sequence, behavior = 'seed', revision = `revision-${String(sequence)}`, authority = 'stable') {
const seedRoot = join(root, `seed-${revision}`)
const bootstrapRoot = await bootstrap(join(seedRoot, 'bootstrap'), version, behavior)
const bootstrapSha256 = await hashTree(bootstrapRoot)
const content = {
schema: 1,
authority,
platformRevision: revision,
targetSequence: sequence,
bootstrapApi: 1,
updateApi: 1,
bootstrap: { version, id: version, sha256: bootstrapSha256 },
deployment: {
id: `deployment-${String(sequence)}`,
dshVersion: `0.1.0-rc.${String(sequence)}`,
environmentVersion: '1.0.0',
environment: { id: 'environment', sha256: '1'.repeat(64) },
pristine: { id: 'pristine', sha256: '2'.repeat(64) },
runtime: { id: 'runtime', sha256: '3'.repeat(64) },
systemPlugins: { id: 'system-plugins', sha256: '4'.repeat(64) },
},
}
const inventory = parseImageInventory({ ...content, imageBuildId: deriveImageBuildId(content) })
return { seedRoot, inventory, record: recordsFromImageInventory(inventory).bootstrap }
}
function bootstrapManager(root, image) {
return new BootstrapManager({
stateRoot: join(root, 'state'),
storeRoot: join(root, 'store'),
seedRoot: image.seedRoot,
inventory: image.inventory,
})
}
async function storeBootstrap(manager, version, sequence, behavior) {
const id = `bootstrap-${version}`
const path = await bootstrap(manager.storeRoot, id, behavior)
const sha256 = await hashTree(path)
const content = {
schema: 1,
version,
bootstrapApi: 1,
targetSequence: sequence,
artifact: { storage: 'store', kind: 'bootstrap', id, sha256 },
}
return manager.writeRecord({ ...content, id: deriveRecordId('bootstrap-record', content) })
}
test('preserves container supplementary groups while lowering Bootstrap privileges', () => {
assert.deepEqual(bootstrapLaunchCommand({
node: '/usr/local/bin/node',
script: '/opt/bootstrap/index.mjs',
}), {
executable: '/usr/local/bin/node',
args: ['/opt/bootstrap/index.mjs'],
})
assert.deepEqual(bootstrapLaunchCommand({
node: '/usr/local/bin/node',
script: '/opt/bootstrap/index.mjs',
uid: 1000,
gid: 1000,
}), {
executable: '/usr/bin/setpriv',
args: [
'--reuid=1000',
'--regid=1000',
'--keep-groups',
'--',
'/usr/local/bin/node',
'/opt/bootstrap/index.mjs',
],
})
assert.throws(() => bootstrapLaunchCommand({
node: '/usr/local/bin/node',
script: '/opt/bootstrap/index.mjs',
uid: 1000,
}), /UID and GID/)
})
test('launches Proxy Manager with a dedicated identity and isolated environment', () => {
assert.deepEqual(proxyLaunchCommand({
node: '/usr/local/bin/node', script: '/opt/proxy/index.mjs', uid: 991, gid: 991, platformGid: 1000,
}), {
executable: '/usr/bin/setpriv',
args: [
'--reuid=991', '--regid=991', '--groups=1000', '--',
'/usr/local/bin/node', '/opt/proxy/index.mjs',
],
})
assert.throws(() => proxyLaunchCommand({
node: 'node', script: 'proxy.mjs', uid: 0, gid: 0, platformGid: 1000,
}), /positive/)
assert.deepEqual(proxyLaunchEnvironment({
environment: { PATH: '/usr/bin', DSH_PROXY_MASTER_KEY: 'secret', HOME: '/root', LEAK: 'value' },
dataRoot: '/data/platform', runRoot: '/run/dsh-platform',
}), {
HOME: '/nonexistent', USER: 'dsh-proxy', LOGNAME: 'dsh-proxy', PATH: '/usr/bin',
DSH_PLATFORM_DATA: '/data/platform', DSH_PLATFORM_RUN: '/run/dsh-platform',
DSH_PROXY_MASTER_KEY: 'secret',
})
})
test('launches Access Manager without platform supplementary groups or inherited secrets', () => {
assert.deepEqual(accessLaunchCommand({
node: '/usr/local/bin/node', script: '/opt/access/index.mjs', uid: 992, gid: 992,
}), {
executable: '/usr/bin/setpriv',
args: [
'--reuid=992', '--regid=992', '--clear-groups', '--',
'/usr/local/bin/node', '/opt/access/index.mjs',
],
})
assert.throws(() => accessLaunchCommand({ node: 'node', script: 'access.mjs', uid: 0, gid: 0 }), /positive/)
assert.deepEqual(accessLaunchEnvironment({
environment: { PATH: '/usr/bin', HOME: '/root', DSH_PROXY_PASSWORD: 'secret', LEAK: 'value' },
dataRoot: '/data/platform', runRoot: '/run/dsh-platform', classificationToken: 'classification-token',
}), {
HOME: '/nonexistent', USER: 'dsh-access', LOGNAME: 'dsh-access', PATH: '/usr/bin',
DSH_PLATFORM_DATA: '/data/platform', DSH_PLATFORM_RUN: '/run/dsh-platform',
DSH_ACCESS_CLASSIFICATION_TOKEN: 'classification-token',
})
})
test('Access Manager launch broker exposes only fixed lifecycle operations', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-access-launch-'))
const worker = join(root, 'worker.mjs')
const socket = join(root, 'launch.sock')
const accessSocket = join(root, 'access.sock')
const recoverySocket = join(root, 'recovery.sock')
await writeFile(worker, `
import { writeFileSync } from 'node:fs'
writeFileSync(${JSON.stringify(accessSocket)}, '')
writeFileSync(${JSON.stringify(recoverySocket)}, '')
process.send({ type: 'ready', componentReady: true })
process.once('SIGTERM', () => process.exit(0))
setInterval(() => {}, 1000)
`)
const broker = new AccessLaunchBroker({
token: 'valid-token', dataRoot: root, runRoot: root, script: worker,
accessSocket, recoverySocket, uid: 992, gid: 992,
platformGid: process.getgid?.() ?? 1000,
spawnImpl: (_executable, _args, options) => spawn(process.execPath, [worker], {
...options, env: process.env,
}),
})
const server = createAccessLaunchServer({ broker, token: 'valid-token' })
await listenAccessLaunch(server, socket)
const client = new LocalApiClient(socket)
try {
assert.deepEqual(await client.request('GET', '/v1/status'), {
running: false, componentReady: false, pid: null,
})
await assert.rejects(client.request('POST', '/v1/start', { token: 'invalid' }), error => error.statusCode === 403)
const started = await client.request('POST', '/v1/start', { token: 'valid-token' })
assert.equal(started.running, true)
assert.equal(started.componentReady, true)
await assert.rejects(client.request('POST', '/v1/arbitrary', { token: 'valid-token' }), error => error.statusCode === 404)
assert.equal((await client.request('POST', '/v1/stop', { token: 'valid-token' })).running, false)
} finally {
await broker.stop()
await new Promise(resolve => server.close(resolve))
}
})
test('Proxy Manager launch broker requires its token and exposes only fixed operations', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-proxy-launch-'))
const worker = join(root, 'worker.mjs')
const socket = join(root, 'launch.sock')
const controlSocket = join(root, 'control.sock')
await writeFile(worker, `
import { writeFileSync } from 'node:fs'
writeFileSync(${JSON.stringify(controlSocket)}, '')
process.send({ type: 'ready', componentReady: true, revision: 'revision', ports: [17891] })
process.once('SIGTERM', () => process.exit(0))
setInterval(() => {}, 1000)
`)
const broker = new ProxyLaunchBroker({
token: 'valid-token', dataRoot: root, runRoot: root, script: worker,
controlSocket, uid: 991, gid: 991,
platformGid: process.getgid?.() ?? 1000,
spawnImpl: (_executable, _args, options) => {
const child = spawn(process.execPath, [worker], {
...options, env: process.env,
})
return child
},
})
const server = createProxyLaunchServer({ broker, token: 'valid-token' })
await listenProxyLaunch(server, socket)
const client = new LocalApiClient(socket)
try {
assert.deepEqual(await client.request('GET', '/v1/status'), {
running: false, componentReady: false, pid: null, revision: null, ports: [],
})
await assert.rejects(client.request('POST', '/v1/start', { token: 'invalid' }), error => error.statusCode === 403)
const started = await client.request('POST', '/v1/start', { token: 'valid-token' })
assert.equal(started.running, true)
assert.equal(started.componentReady, true)
assert.equal((await client.request('GET', '/v1/status')).revision, 'revision')
await assert.rejects(client.request('POST', '/v1/arbitrary', { token: 'valid-token' }), error => error.statusCode === 404)
assert.equal((await client.request('POST', '/v1/stop', { token: 'valid-token' })).running, false)
} finally {
await broker.stop()
await new Promise(resolve => server.close(resolve))
}
})
test('Proxy Manager launch failure returns after an already-exited child without waiting for another exit', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-proxy-launch-failure-'))
const worker = join(root, 'worker.mjs')
await writeFile(worker, 'process.exit(9)')
const broker = new ProxyLaunchBroker({
token: 'valid-token', dataRoot: root, runRoot: root, script: worker,
controlSocket: join(root, 'control.sock'), uid: 991, gid: 991,
platformGid: process.getgid?.() ?? 1000,
spawnImpl: (_executable, _args, options) => spawn(process.execPath, [worker], {
...options, env: process.env,
}),
})
await assert.rejects(Promise.race([
broker.start(),
new Promise((_, reject) => setTimeout(() => reject(new Error('launch failure timed out')), 1_000)),
]), /exited before readiness.*code=9/)
assert.equal(broker.status().running, false)
})
test('replaces the root login environment when lowering Bootstrap privileges', () => {
const accessEvidence = {
dshSettings: false,
webProfile: true,
dshSessions: false,
userSkills: false,
deploymentState: true,
managementState: false,
updaterState: false,
legacyAuthenticationConfigured: true,
}
assert.deepEqual(bootstrapLaunchEnvironment({
environment: {
HOME: '/root', USER: 'root', LOGNAME: 'root', PATH: '/usr/local/bin:/usr/bin',
DSH_PROXY_MASTER_KEY: 'must-not-leak', DSH_PROXY_LAUNCH_TOKEN: 'old-token',
DSH_PROXY_USERNAME: 'legacy-user', DSH_PROXY_PASSWORD: 'legacy-password',
DSH_PLATFORM_PASSWORD: 'legacy-platform-password',
},
dataRoot: '/data/platform',
runRoot: '/run/dsh-platform',
seedRoot: '/opt/dsh-platform/seed',
bootstrapVersion: '1.0.0',
user: 'node',
home: '/home/node',
proxyLaunchToken: 'new-token',
accessEvidence,
}), {
HOME: '/home/node',
USER: 'node',
LOGNAME: 'node',
PATH: '/usr/local/bin:/usr/bin',
XDG_CACHE_HOME: '/home/node/.cache',
XDG_CONFIG_HOME: '/home/node/.config',
XDG_DATA_HOME: '/home/node/.local/share',
DSH_PLATFORM_DATA: '/data/platform',
DSH_PLATFORM_RUN: '/run/dsh-platform',
DSH_PLATFORM_SEED: '/opt/dsh-platform/seed',
DSH_BOOTSTRAP_VERSION: '1.0.0',
DSH_PROXY_LAUNCH_TOKEN: 'new-token',
DSH_LEGACY_INSTALLATION_EVIDENCE: JSON.stringify(accessEvidence),
})
})
test('tracks immutable Bootstrap Records in one generation-based slots file', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-records-'))
const image = await imageBootstrap(root, '1.0.0', 1)
const manager = bootstrapManager(root, image)
await manager.reconcileImage(image.record)
const managed = await storeBootstrap(manager, '2.0.0', 2, 'next')
await manager.promote(managed.id)
let state = await manager.state()
assert.equal(state.current, managed.id)
assert.equal(state.previous, image.record.id)
assert.equal(state.generation, 2)
await manager.rollback()
state = await manager.state()
assert.equal(state.current, image.record.id)
assert.equal(state.previous, managed.id)
assert.equal(state.generation, 3)
})
test('Stage-0 collects only Bootstrap assets outside current and previous', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-gc-'))
const image = await imageBootstrap(root, '1.0.0', 1)
const manager = bootstrapManager(root, image)
await manager.reconcileImage(image.record)
const retained = await storeBootstrap(manager, '2.0.0', 2, 'retained')
await manager.promote(retained.id)
const orphan = await storeBootstrap(manager, '3.0.0', 3, 'orphan')
const removed = await manager.collectGarbage()
assert.deepEqual(removed.records, [orphan.id])
await assert.rejects(lstat(manager.recordPath(orphan.id)), { code: 'ENOENT' })
await assert.rejects(lstat(join(manager.storeRoot, orphan.artifact.id)), { code: 'ENOENT' })
assert.equal((await lstat(join(manager.storeRoot, retained.artifact.id))).isDirectory(), true)
})
test('advances to a newer image without reinterpreting an old Image Reference', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-image-forward-'))
const first = await imageBootstrap(root, '1.0.0', 1)
const initial = bootstrapManager(root, first)
await initial.reconcileImage(first.record)
const second = await imageBootstrap(root, '1.0.0', 2, 'new-seed')
const upgraded = bootstrapManager(root, second)
await upgraded.reconcileImage(second.record)
const state = await upgraded.state()
assert.equal(state.current, second.record.id)
assert.equal(state.previous, first.record.id)
await assert.rejects(upgraded.resolveRecord(first.record.id), /different image/)
})
test('uses an older image Bootstrap after an explicit Docker image rollback', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-image-rollback-'))
const first = await imageBootstrap(root, '1.0.0', 1)
await bootstrapManager(root, first).reconcileImage(first.record)
const second = await imageBootstrap(root, '1.0.0', 2, 'new-seed')
await bootstrapManager(root, second).reconcileImage(second.record)
const rolledBack = bootstrapManager(root, first)
const state = await rolledBack.reconcileImage(first.record)
assert.equal(state.current, first.record.id)
assert.equal(state.previous, second.record.id)
assert.equal((await rolledBack.current()).record.id, first.record.id)
})
test('rejects same-sequence Bootstrap content conflicts', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-image-conflict-'))
const first = await imageBootstrap(root, '1.0.0', 1, 'one', 'revision-one')
await bootstrapManager(root, first).reconcileImage(first.record)
const conflicting = await imageBootstrap(root, '1.0.0', 1, 'two', 'revision-two')
await assert.rejects(bootstrapManager(root, conflicting).reconcileImage(conflicting.record), /conflicts/)
})
test('replaces a rebuilt development Bootstrap without retaining a stale Image Reference', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-development-refresh-'))
const first = await imageBootstrap(root, '1.0.0', 0, 'one', 'development-one', 'development')
await bootstrapManager(root, first).reconcileImage(first.record)
const rebuilt = await imageBootstrap(root, '1.0.0', 0, 'two', 'development-two', 'development')
const manager = bootstrapManager(root, rebuilt)
const state = await manager.reconcileImage(rebuilt.record)
assert.equal(state.current, rebuilt.record.id)
assert.equal(state.previous, null)
assert.equal((await manager.current()).path, join(rebuilt.seedRoot, 'bootstrap', '1.0.0'))
})
test('replaces a stale formal Image Bootstrap when starting a development image', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-development-after-formal-'))
const formal = await imageBootstrap(root, '1.0.0', 11, 'formal', 'formal-eleven', 'stable')
await bootstrapManager(root, formal).reconcileImage(formal.record)
const development = await imageBootstrap(root, '1.0.0', 0, 'development', 'local-rebuild', 'development')
const manager = bootstrapManager(root, development)
const state = await manager.reconcileImage(development.record)
assert.equal(state.current, development.record.id)
assert.equal(state.previous, null)
assert.equal((await manager.current()).path, join(development.seedRoot, 'bootstrap', '1.0.0'))
})
test('replaces a stale Managed Bootstrap when starting a development image', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-development-after-managed-'))
const formal = await imageBootstrap(root, '1.0.0', 11, 'formal', 'formal-eleven', 'stable')
const initial = bootstrapManager(root, formal)
await initial.reconcileImage(formal.record)
const managed = await storeBootstrap(initial, '2.0.0', 12, 'managed')
await initial.promote(managed.id)
const development = await imageBootstrap(root, '1.0.0', 0, 'development', 'local-rebuild', 'development')
const manager = bootstrapManager(root, development)
const state = await manager.reconcileImage(development.record)
assert.equal(state.current, development.record.id)
assert.equal(state.previous, null)
assert.equal((await manager.current()).path, join(development.seedRoot, 'bootstrap', '1.0.0'))
})
test('preserves a higher Managed Bootstrap when the image is behind', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-image-behind-'))
const first = await imageBootstrap(root, '1.0.0', 1)
const initial = bootstrapManager(root, first)
await initial.reconcileImage(first.record)
const managed = await storeBootstrap(initial, '3.0.0', 3, 'managed')
await initial.promote(managed.id)
const second = await imageBootstrap(root, '2.0.0', 2)
const restarted = bootstrapManager(root, second)
const state = await restarted.reconcileImage(second.record)
assert.equal(state.current, managed.id)
assert.equal((await restarted.current()).record.version, '3.0.0')
})
test('prefers the current image when Store and Image content match at one sequence', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-image-preferred-'))
const image = await imageBootstrap(root, '1.0.0', 1)
const manager = bootstrapManager(root, image)
await manager.reconcileImage(image.record)
const storeId = 'bootstrap-identical'
await cp(
join(image.seedRoot, 'bootstrap', '1.0.0'),
join(manager.storeRoot, storeId),
{ recursive: true },
)
const content = {
schema: 1,
version: '1.0.0',
bootstrapApi: 1,
targetSequence: 1,
artifact: { storage: 'store', kind: 'bootstrap', id: storeId, sha256: image.inventory.bootstrap.sha256 },
}
const managed = await manager.writeRecord({ ...content, id: deriveRecordId('bootstrap-record', content) })
await manager.promote(managed.id)
const state = await manager.reconcileImage(image.record)
assert.equal(state.current, image.record.id)
assert.equal(state.previous, managed.id)
})
test('rolls back when the current Bootstrap exits before readiness', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-supervisor-'))
const image = await imageBootstrap(root, '1.0.0', 1, 'process.send({ type: "ready", bootstrapApi: 1 }); setInterval(() => {}, 1000)')
const slots = bootstrapManager(root, image)
await slots.reconcileImage(image.record)
const candidate = await storeBootstrap(slots, '2.0.0', 2, 'process.exit(2)')
await slots.promote(candidate.id)
const reports = []
const supervisor = new BootstrapSupervisor({
slots,
dataRoot: root,
readyTimeoutMs: 1_000,
report: (message, fields) => { reports.push({ message, fields }) },
})
const child = await supervisor.startWithRollback()
assert.equal(child.exitCode, null)
const state = await slots.state()
assert.equal(state.current, image.record.id)
assert.equal(state.previous, candidate.id)
assert.equal(reports.some(entry => entry.message === 'bootstrap.launch.failed' && entry.fields.error instanceof Error), true)
assert.equal(reports.some(entry => entry.message === 'bootstrap.rollback.started'), true)
assert.equal(reports.some(entry => entry.message === 'bootstrap.rollback.completed'), true)
assert.equal(reports.some(entry => entry.message === 'bootstrap.ready' && entry.fields.pid === child.pid), true)
await supervisor.stop()
assert.equal(reports.some(entry => entry.message === 'bootstrap.stop.completed'), true)
})
test('serializes a staged Bootstrap restart behind initial readiness', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-supervisor-transition-'))
const image = await imageBootstrap(root, '1.0.0', 1, `
setTimeout(() => process.send({ type: 'ready', bootstrapApi: 1 }), 100)
setInterval(() => {}, 1000)
`)
const slots = bootstrapManager(root, image)
await slots.reconcileImage(image.record)
const candidate = await storeBootstrap(
slots,
'2.0.0',
2,
'process.send({ type: "ready", bootstrapApi: 1 }); setInterval(() => {}, 1000)',
)
const reports = []
let resolveLaunch
const launchStarted = new Promise(resolve => { resolveLaunch = resolve })
const supervisor = new BootstrapSupervisor({
slots,
dataRoot: root,
readyTimeoutMs: 1_000,
report: (message, fields) => {
reports.push({ message, fields })
if (message === 'bootstrap.launch.started' && fields.bootstrapVersion === '1.0.0') resolveLaunch()
},
})
const initial = supervisor.startWithRollback()
await launchStarted
await slots.promote(candidate.id)
const restart = supervisor.restart()
const initialChild = await initial
const restartedChild = await restart
assert.notEqual(initialChild.pid, restartedChild.pid)
assert.equal((await slots.state()).current, candidate.id)
assert.equal(reports.some(entry => entry.message === 'bootstrap.launch.failed'), false)
const initialReady = reports.findIndex(entry => (
entry.message === 'bootstrap.ready' && entry.fields.bootstrapVersion === '1.0.0'
))
const restartStarted = reports.findIndex(entry => entry.message === 'bootstrap.restart.started')
assert.notEqual(initialReady, -1)
assert.equal(restartStarted > initialReady, true)
assert.equal(reports.some(entry => (
entry.message === 'bootstrap.ready' && entry.fields.bootstrapVersion === '2.0.0'
)), true)
await supervisor.stop()
})
test('rejects an in-flight recovery request when Bootstrap exits', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-recovery-exit-'))
const behavior = `
process.send({ type: 'ready', bootstrapApi: 1 })
process.on('message', () => process.exit(7))
setInterval(() => {}, 1000)
`
const image = await imageBootstrap(root, '1.0.0', 1, behavior)
const slots = bootstrapManager(root, image)
await slots.reconcileImage(image.record)
const reports = []
const supervisor = new BootstrapSupervisor({
slots,
dataRoot: root,
readyTimeoutMs: 1_000,
report: (message, fields) => { reports.push({ message, fields }) },
})
await supervisor.startWithRollback()
await assert.rejects(supervisor.recoverImageBaseline(), /Bootstrap exited unexpectedly/)
assert.equal(supervisor.requests.size, 0)
assert.equal(reports.some(entry => entry.message === 'bootstrap.recovery.started'), true)
assert.equal(reports.some(entry => entry.message === 'bootstrap.recovery.failed' && entry.fields.error instanceof Error), true)
assert.equal(reports.some(entry => entry.message === 'bootstrap.exited' && entry.fields.code === 7), true)
})
function unixRequest(socketPath, method, path, body) {
return new Promise((resolve, reject) => {
const requestBody = body === undefined ? undefined : Buffer.from(JSON.stringify(body))
const req = request({ socketPath, method, path, headers: requestBody === undefined ? {} : { 'content-length': requestBody.length } }, response => {
const chunks = []
response.on('data', chunk => chunks.push(chunk))
response.on('end', () => resolve({ status: response.statusCode, body: JSON.parse(Buffer.concat(chunks)) }))
})
req.once('error', reject)
req.end(requestBody)
})
}
test('root-only recovery server requires the exact current imageBuildId', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-recovery-server-'))
const image = await imageBootstrap(root, '1.0.0', 2)
let recoveries = 0
const reports = []
const server = createRecoveryServer({
inventory: image.inventory,
deployments: async () => ({ current: { recordId: 'broken' } }),
supervisor: { recoverImageBaseline: async () => { recoveries += 1; return { current: image.record.id } } },
report: (message, fields) => { reports.push({ message, fields }) },
})
const socketPath = join(root, 'run', 'recovery.sock')
await listenRecovery(server, socketPath)
try {
const status = await unixRequest(socketPath, 'GET', '/v1/status')
assert.equal(status.status, 200)
assert.equal(status.body.imageBaseline.imageBuildId, image.inventory.imageBuildId)
assert.equal((await unixRequest(socketPath, 'POST', '/v1/recover-image-baseline', { confirm: 'wrong' })).status, 400)
assert.equal((await unixRequest(socketPath, 'POST', '/v1/recover-image-baseline', {
confirm: image.inventory.imageBuildId,
})).status, 200)
assert.equal((await unixRequest(socketPath, 'POST', '/v1/recover-image-baseline', {
confirm: 'x'.repeat(17 * 1024),
})).status, 400)
assert.equal(recoveries, 1)
assert.equal(reports.some(entry => entry.message === 'recovery.request.failed' && entry.fields.error instanceof Error), true)
assert.equal(reports.some(entry => entry.message === 'image-baseline.requested'), true)
assert.equal(reports.some(entry => entry.message === 'image-baseline.recovered'), true)
} finally {
await new Promise(resolve => server.close(resolve))
}
})
test('exposes a bounded local Trust API without trust-root mutation routes', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-trust-server-'))
const recovery = keyPair()
const ledger = new TrustLedger(join(root, 'trust'), recovery.publicKey)
const objects = new VerifiedObjectStore({ root: join(root, 'trust'), untrustedRoot: join(root, 'downloads'), ledger })
const reports = []
const server = createTrustServer({
ledger,
objects,
report: (message, fields) => { reports.push({ message, fields }) },
})
const socketPath = join(root, 'run', 'trust.sock')
await listenUnix(server, socketPath)
try {
assert.deepEqual(await unixRequest(socketPath, 'GET', '/v1/status'), {
status: 200,
body: { keyringGeneration: null, targetSequence: null, officialDshVersion: null },
})
assert.equal((await unixRequest(socketPath, 'POST', '/v1/trust/reset', {})).status, 404)
assert.equal((await unixRequest(socketPath, 'POST', '/v1/dsh/ensure', { version: 'bad', sourcePath: '/tmp/x' })).status, 400)
assert.equal(reports.some(entry => entry.message === 'trust.request.failed'
&& entry.fields.pathname === '/v1/dsh/ensure'
&& entry.fields.error instanceof Error), true)
assert.equal((await readFile(socketPath).catch(error => error.code)), 'ENXIO')
} finally {
await new Promise(resolve => server.close(resolve))
}
})
test('returns structured Trust errors to local clients', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-trust-error-server-'))
const error = Object.assign(new Error('keyring signature key is not trusted'), { code: 'TRUST_UNKNOWN_KEY' })
const server = createTrustServer({
ledger: { acceptKeyring: async () => { throw error } },
objects: {},
})
const socketPath = join(root, 'run', 'trust.sock')
await listenUnix(server, socketPath)
try {
assert.deepEqual(await unixRequest(socketPath, 'POST', '/v1/keyring', { document: '', signature: {} }), {
status: 400,
body: { error: error.message, code: error.code },
})
await assert.rejects(new LocalApiClient(socketPath).acceptKeyring(Buffer.alloc(0), {}), failure => {
assert.equal(failure.code, error.code)
assert.equal(failure.localApiPath, '/v1/keyring')
return true
})
} finally {
await new Promise(resolve => server.close(resolve))
}
})
test('exposes only an injected Stage-0 Bootstrap staging operation', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-stage-bootstrap-api-'))
const recovery = keyPair()
const ledger = new TrustLedger(join(root, 'trust'), recovery.publicKey)
const objects = new VerifiedObjectStore({ root: join(root, 'trust'), untrustedRoot: join(root, 'downloads'), ledger })
let staged
const server = createTrustServer({
ledger,
objects,
stageBootstrap: async (receipt, version) => {
staged = { receipt, version }
return { recordId: 'bootstrap-record-one', targetSequence: 2, recordChanged: false, restartRequired: false }
},
})
const socketPath = join(root, 'run', 'trust.sock')
await listenUnix(server, socketPath)
try {
assert.deepEqual(await unixRequest(socketPath, 'POST', '/v1/bootstrap/stage', {
receipt: 'receipt-token', version: '2.0.0',
}), {
status: 202,
body: {
status: 'staged',
version: '2.0.0',
recordId: 'bootstrap-record-one',
targetSequence: 2,
recordChanged: false,
restartRequired: false,
},
})
assert.deepEqual(staged, { receipt: 'receipt-token', version: '2.0.0' })
} finally {
await new Promise(resolve => server.close(resolve))
}
})
test('official DSH Trust API accepts only a version and two untrusted source paths', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-official-api-'))
const recovery = keyPair()
const ledger = new TrustLedger(join(root, 'trust'), recovery.publicKey)
const calls = []
const objects = {
ensureOfficialDsh: async (version, metadataPath, tarballPath) => {
calls.push({ version, metadataPath, tarballPath })
return { authorityType: 'official-dsh', authorityVersion: version }
},
reconcileRevocations: async () => [],
activeReceipts: async () => [],
}
const server = createTrustServer({ ledger, objects })
const socketPath = join(root, 'run', 'trust.sock')
await listenUnix(server, socketPath)
try {
const request = {
version: '0.1.0-rc.8',
metadataPath: '/data/platform/cache/downloads/official.json',
tarballPath: '/data/platform/cache/downloads/official.tgz',
}
const accepted = await unixRequest(socketPath, 'POST', '/v1/dsh/ensure', request)
assert.equal(accepted.status, 200)
assert.deepEqual(calls, [request])
for (const extra of [
{ ...request, url: 'https://mirror.example/dsh.tgz' },
{ ...request, expectedHash: '0'.repeat(64) },
{ ...request, candidate: {} },
{ version: request.version, metadataPath: request.metadataPath },
]) {
assert.equal((await unixRequest(socketPath, 'POST', '/v1/dsh/ensure', extra)).status, 400)
}
assert.equal((await unixRequest(socketPath, 'POST', '/v1/artifacts/import-experimental', {
candidate: {}, sourcePath: '/data/platform/downloads/untrusted/dsh.tgz',
})).status, 404)
assert.deepEqual(calls, [request])
} finally {
await new Promise(resolve => server.close(resolve))
}
})
test('Bootstrap archive installation rejects traversal before extraction', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-archive-'))
const image = await imageBootstrap(root, '1.0.0', 1)
const slots = bootstrapManager(root, image)
await slots.reconcileImage(image.record)
const source = join(root, 'source')
await mkdir(join(source, 'platform', 'bootstrap'), { recursive: true })
await writeFile(join(source, 'platform', 'bootstrap', 'index.mjs'), 'process.exit(0)')
const valid = join(root, 'valid.tgz')
assert.equal(spawnSync('tar', ['-czf', valid, '-C', source, 'platform']).status, 0)
const installed = await slots.installArchive(valid, { version: '2.0.0', targetSequence: 2 })
const resolved = await slots.resolveRecord(installed.id)
assert.match(await readFile(join(resolved.path, 'platform', 'bootstrap', 'index.mjs'), 'utf8'), /exit/)
const unsafe = join(root, 'unsafe.tgz')
assert.equal(spawnSync('tar', ['-czf', unsafe, '--transform=s,^,../,', '-C', source, 'platform']).status, 0)
await assert.rejects(slots.installArchive(unsafe, { version: '3.0.0', targetSequence: 3 }), /unsafe path/)
})
test('stages Bootstrap by content identity even when its version does not change', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-bootstrap-content-stage-'))
const image = await imageBootstrap(root, '1.0.0', 1)
const slots = bootstrapManager(root, image)
await slots.reconcileImage(image.record)
const archive = async (name, behavior) => {
const source = join(root, `source-${name}`)
await mkdir(join(source, 'platform', 'bootstrap'), { recursive: true })
await writeFile(join(source, 'platform', 'bootstrap', 'index.mjs'), behavior)
const destination = join(root, `${name}.tgz`)
assert.equal(spawnSync('tar', ['-czf', destination, '-C', source, 'platform']).status, 0)
return destination
}
const first = await slots.stageArchive(await archive('first', 'console.log("first")'), {
version: '1.0.0', targetSequence: 2,
})
assert.equal(first.recordChanged, true)
assert.equal(first.restartRequired, true)
const identical = await slots.stageArchive(join(root, 'first.tgz'), {
version: '1.0.0', targetSequence: 3,
})
assert.equal(identical.recordChanged, true)
assert.equal(identical.restartRequired, false)
assert.equal((await slots.state()).current, identical.record.id)
const changed = await slots.stageArchive(await archive('changed', 'console.log("changed")'), {
version: '1.0.0', targetSequence: 4,
})
assert.equal(changed.recordChanged, true)
assert.equal(changed.restartRequired, true)
assert.equal((await slots.state()).previous, identical.record.id)
})
@@ -0,0 +1,64 @@
import assert from 'node:assert/strict'
import { mkdtemp, readFile, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { spawnSync } from 'node:child_process'
import test from 'node:test'
import {
advanceSupportedDsh,
compareDshVersions,
parseSupportedTarget,
validateSupportedTarget,
} from '../lib/supported-target.mjs'
const target = {
schema: 1,
latestSupportedDsh: '0.1.0-rc.7',
environment: '1.0.0-dev',
}
const bytes = value => Buffer.from(`${JSON.stringify(value)}\n`)
test('parses the exact Supported Target contract and matching Environment', () => {
assert.deepEqual(parseSupportedTarget(bytes(target)), target)
assert.equal(validateSupportedTarget(bytes(target), bytes({ version: target.environment })).environment, target.environment)
assert.throws(() => parseSupportedTarget(bytes({ ...target, extra: true })), /exactly/)
assert.throws(() => validateSupportedTarget(bytes(target), bytes({ version: 'different' })), /does not match/)
})
test('compares release and prerelease DSH semantic versions without downgrade ambiguity', () => {
assert.equal(compareDshVersions('0.1.0-rc.21', '0.1.0-rc.7'), 1)
assert.equal(compareDshVersions('0.1.0', '0.1.0-rc.21'), 1)
assert.equal(compareDshVersions('0.1.0-rc.7', '0.1.0-rc.7+rebuilt'), 0)
assert.equal(compareDshVersions('0.1.0-2', '0.1.0-beta'), -1)
assert.equal(compareDshVersions('999999999999999999999.0.0', '999999999999999999998.0.0'), 1)
assert.throws(() => compareDshVersions('0.1.0-rc.07', '0.1.0-rc.7'), /numeric prerelease/)
})
test('advances only DSH and rejects an upstream rollback', () => {
const advanced = advanceSupportedDsh(target, '0.1.0-rc.8')
assert.equal(advanced.changed, true)
assert.deepEqual(advanced.target, { ...target, latestSupportedDsh: '0.1.0-rc.8' })
assert.equal(advanceSupportedDsh(target, target.latestSupportedDsh).changed, false)
assert.throws(() => advanceSupportedDsh(target, '0.1.0-rc.6'), /roll back/)
})
test('CLI validates and atomically updates the candidate target', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-supported-target-'))
const targetPath = join(root, 'supported-target.json')
const definitionPath = join(root, 'definition.json')
await writeFile(targetPath, bytes(target))
await writeFile(definitionPath, bytes({ version: target.environment }))
const tool = new URL('../tools/supported-target.mjs', import.meta.url).pathname
const result = spawnSync(process.execPath, [tool, 'advance', targetPath, definitionPath, '0.1.0-rc.8'], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
assert.equal(JSON.parse(result.stdout).changed, true)
assert.equal(JSON.parse(await readFile(targetPath, 'utf8')).latestSupportedDsh, '0.1.0-rc.8')
const rollback = spawnSync(process.execPath, [tool, 'advance', targetPath, definitionPath, '0.1.0-rc.7'], { encoding: 'utf8' })
assert.notEqual(rollback.status, 0)
assert.equal(JSON.parse(await readFile(targetPath, 'utf8')).latestSupportedDsh, '0.1.0-rc.8')
const extraArgument = spawnSync(process.execPath, [tool, 'validate', targetPath, definitionPath, 'ignored'], { encoding: 'utf8' })
assert.equal(extraArgument.status, 64)
})
@@ -0,0 +1,329 @@
import assert from 'node:assert/strict'
import { cp, lstat, mkdtemp, mkdir, readFile, readlink, rm, symlink, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { spawnSync } from 'node:child_process'
import test from 'node:test'
import {
discardSystemPluginSelection,
linkSystemPluginScope,
listBundledSystemPlugins,
listManagedSystemPlugins,
materializeSystemPluginSelection,
rebuildBundledSystemPluginView,
reconcileSystemPlugins,
SystemPluginSelectionStore,
} from '../../control-plane/modules/plugin-manager/system.mjs'
import { createHash } from 'node:crypto'
import { hashTree } from '../lib/tree-hash.mjs'
test('Bootstrap lists System Plugins from the selected runtime view without rehashing the Deployment', async () => {
const source = await readFile(new URL('../bootstrap/index.mjs', import.meta.url), 'utf8')
const list = source.slice(source.indexOf('const systemPlugins = {'), source.indexOf(' mutate:', source.indexOf('const systemPlugins = {')))
assert.match(list, /realpath\(paths\.deploymentView\)/)
assert.match(list, /environmentRoot: join\(deploymentRoot, 'environment'\)/)
assert.match(list, /sourceRoot: join\(deploymentRoot, 'system-plugins'\)/)
assert.doesNotMatch(list, /deployments\.selected\(\)/)
})
async function archive(root, id, patch) {
const source = join(root, `source-${id}`, 'package')
await mkdir(source, { recursive: true })
await writeFile(join(source, 'package.json'), JSON.stringify({
name: `@dsh-docker/${id}`,
type: 'module',
main: 'plugin.mjs',
dshDocker: { description: { zh: `${id} 功能`, en: `${id} feature` } },
}))
await writeFile(join(source, 'cordis.patch.json'), JSON.stringify(patch))
await writeFile(join(source, 'plugin.mjs'), `export const id = ${JSON.stringify(id)}\n`)
const path = join(root, `${id}.tgz`)
const result = spawnSync('tar', ['-czf', path, '-C', dirname(source), 'package'], { encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
return path
}
function dirname(path) {
return path.slice(0, path.lastIndexOf('/'))
}
test('reconciles only declared System Plugins into a separate immutable overlay', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-system-plugins-'))
const userHome = join(root, 'user-home')
await mkdir(join(userHome, 'profiles/web'), { recursive: true })
await writeFile(join(userHome, 'cordis.patch.yml'), '[{"id":"user-owned"}]\n')
await writeFile(join(userHome, 'profiles/web/package.json'), '{"dependencies":{"third-party":"1.0.0"}}\n')
const first = await archive(root, 'update-ui', [{ insert: [{ id: 'dsh-docker.update-ui.host', name: '@dsh-docker/update-ui' }] }])
const second = await archive(root, 'diagnostics', [{ insert: [{ id: 'dsh-docker.diagnostics.host', name: '@dsh-docker/diagnostics' }] }])
const artifacts = new Map([['update-ui', first], ['diagnostics', second]])
await reconcileSystemPlugins({
root: join(root, 'managed'),
environmentVersion: '1.0.0',
plugins: [
{ id: 'update-ui', sha256: '1'.repeat(64) },
{ id: 'diagnostics', sha256: '2'.repeat(64) },
],
artifactPath: reference => artifacts.get(reference.id),
})
const overlay = JSON.parse(await readFile(join(root, 'managed/current/cordis.patch.yml'), 'utf8'))
assert.deepEqual(overlay.flatMap(entry => entry.insert).map(entry => entry.id), ['dsh-docker.update-ui.host', 'dsh-docker.diagnostics.host'])
assert.deepEqual(overlay.flatMap(entry => entry.insert).map(entry => entry.name), [
'@dsh-docker/update-ui',
'@dsh-docker/diagnostics',
])
assert.equal(await readFile(join(userHome, 'cordis.patch.yml'), 'utf8'), '[{"id":"user-owned"}]\n')
assert.match(await readFile(join(userHome, 'profiles/web/package.json'), 'utf8'), /third-party/)
})
test('rejects a System Plugin which claims another namespace', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-system-plugin-invalid-'))
const artifact = await archive(root, 'update-ui', [{ insert: [{ id: 'unowned-row', name: '@dsh-docker/update-ui' }] }])
await assert.rejects(reconcileSystemPlugins({
root: join(root, 'managed'),
environmentVersion: 'invalid',
plugins: [{ id: 'update-ui', sha256: '1'.repeat(64) }],
artifactPath: () => artifact,
}), /namespace/)
})
test('rejects a System Plugin which patches an existing DSH row', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-system-plugin-mutation-'))
const artifact = await archive(root, 'update-ui', [{
id: 'dsh-docker.update-ui.host',
name: '@dsh-docker/update-ui',
}])
await assert.rejects(reconcileSystemPlugins({
root: join(root, 'managed'),
environmentVersion: 'invalid',
plugins: [{ id: 'update-ui', sha256: '1'.repeat(64) }],
artifactPath: () => artifact,
}), /only a non-empty insert list/)
})
test('retargets only the reserved System Plugin scope without changing persistent Profile data', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-system-plugin-scope-'))
const dshHome = join(root, 'dsh')
const profileRoot = join(dshHome, 'profiles', 'web')
const modulesRoot = join(dshHome, 'profiles', 'node_modules')
const userPackageRoot = join(modulesRoot, '@example', 'persistent-plugin')
const firstView = join(root, 'run', 'system-plugins-v1')
const secondView = join(root, 'run', 'system-plugins-v2')
await mkdir(join(profileRoot, 'node_modules'), { recursive: true })
await mkdir(userPackageRoot, { recursive: true })
await mkdir(join(firstView, 'packages'), { recursive: true })
await mkdir(join(secondView, 'packages'), { recursive: true })
await writeFile(join(profileRoot, 'package.json'), '{"dependencies":{"persistent-plugin":"1.0.0"}}\n')
await writeFile(join(profileRoot, 'pnpm-lock.yaml'), 'lockfileVersion: 9\n')
await writeFile(join(userPackageRoot, 'package.json'), '{"name":"@example/persistent-plugin"}\n')
await symlink(userPackageRoot, join(profileRoot, 'node_modules', 'persistent-plugin'), 'dir')
const profileBefore = await hashTree(profileRoot)
const link = await linkSystemPluginScope({ dshHome, viewRoot: firstView })
assert.equal(await readlink(link), join(firstView, 'packages'))
assert.equal(await linkSystemPluginScope({ dshHome, viewRoot: firstView }), link)
assert.equal(await linkSystemPluginScope({ dshHome, viewRoot: secondView }), link)
assert.equal(await readlink(link), join(secondView, 'packages'))
assert.equal(await hashTree(profileRoot), profileBefore)
assert.equal(await readFile(join(userPackageRoot, 'package.json'), 'utf8'), '{"name":"@example/persistent-plugin"}\n')
})
test('rebuilds a bundled System Plugin view only from the current Environment artifacts', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-system-plugin-repair-'))
const environment = join(root, 'environment')
const artifact = await archive(root, 'platform-management', [{
insert: [{ id: 'dsh-docker.platform-management.plugin', name: '@dsh-docker/platform-management' }],
}])
const bytes = await readFile(artifact)
const sha256 = createHash('sha256').update(bytes).digest('hex')
await mkdir(join(environment, 'artifacts'), { recursive: true })
await cp(artifact, join(environment, 'artifacts', 'system-plugin-platform-management'))
await writeFile(join(environment, 'environment.manifest.json'), JSON.stringify({
schema: 1,
manifestType: 'environment',
version: '1.0.0',
keyringGeneration: 1,
targetSequence: 1,
issuedAt: '2026-08-20T00:00:00.000Z',
artifacts: [{
id: 'system-plugin-platform-management',
mediaType: 'application/vnd.dsh-platform.system-plugin.v1+tar+gzip',
sha256,
size: bytes.byteLength,
url: 'https://example.invalid/system-plugin-platform-management',
}],
bootstrapApi: 1,
components: [],
patches: [],
systemPlugins: [{ id: 'platform-management', sha256 }],
}))
const initialRoot = join(root, 'initial')
const initial = await reconcileSystemPlugins({
root: initialRoot,
environmentVersion: 'fixture',
plugins: [{ id: 'platform-management', sha256 }],
artifactPath: () => artifact,
})
const expectedSha256 = await hashTree(initial)
const rebuilt = await rebuildBundledSystemPluginView({
environmentRoot: environment,
outputRoot: join(root, 'views'),
expectedSha256,
requestedPluginId: 'platform-management',
})
assert.equal(await hashTree(rebuilt), expectedSha256)
assert.deepEqual(await listBundledSystemPlugins({ environmentRoot: environment, viewRoot: rebuilt }), [{
id: 'platform-management',
artifactId: 'system-plugin-platform-management',
sha256,
installed: true,
reason: null,
}])
await rm(join(rebuilt, 'packages', 'platform-management'), { recursive: true })
const repairedAgain = await rebuildBundledSystemPluginView({
environmentRoot: environment,
outputRoot: join(root, 'views'),
expectedSha256,
requestedPluginId: 'platform-management',
})
assert.equal(await hashTree(repairedAgain), expectedSha256)
await assert.rejects(rebuildBundledSystemPluginView({
environmentRoot: environment,
outputRoot: join(root, 'views'),
expectedSha256,
requestedPluginId: 'unknown',
}), /not bundled/)
})
test('persists install and enable selection while protecting Platform Management', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-system-plugin-selection-'))
const environment = join(root, 'environment')
const definitions = [
['platform-management', '1'],
['diagnostics', '2'],
]
const artifacts = []
await mkdir(join(environment, 'artifacts'), { recursive: true })
for (const [id] of definitions) {
const artifact = await archive(root, id, [{
insert: [{ id: `dsh-docker.${id}.plugin`, name: `@dsh-docker/${id}` }],
}])
const bytes = await readFile(artifact)
const sha256 = createHash('sha256').update(bytes).digest('hex')
const artifactId = `system-plugin-${id}`
await cp(artifact, join(environment, 'artifacts', artifactId))
artifacts.push({ id: artifactId, sha256, size: bytes.byteLength, pluginId: id, path: artifact })
}
await writeFile(join(environment, 'environment.manifest.json'), JSON.stringify({
schema: 1,
manifestType: 'environment',
version: '1.0.0',
keyringGeneration: 1,
targetSequence: 1,
issuedAt: '2026-08-20T00:00:00.000Z',
artifacts: artifacts.map(artifact => ({
id: artifact.id,
mediaType: 'application/vnd.dsh-platform.system-plugin.v1+tar+gzip',
sha256: artifact.sha256,
size: artifact.size,
url: `https://example.invalid/${artifact.id}`,
})),
bootstrapApi: 1,
components: [],
patches: [],
systemPlugins: artifacts.map(artifact => ({ id: artifact.pluginId, sha256: artifact.sha256 })),
}))
const source = await reconcileSystemPlugins({
root: join(root, 'source'),
environmentVersion: 'fixture',
plugins: artifacts.map(artifact => ({ id: artifact.pluginId, sha256: artifact.sha256 })),
artifactPath: reference => artifacts.find(artifact => artifact.pluginId === reference.id).path,
})
const store = new SystemPluginSelectionStore(join(root, 'state', 'system-plugins.json'))
const pluginIds = artifacts.map(artifact => artifact.pluginId)
const initial = await listManagedSystemPlugins({ environmentRoot: environment, sourceRoot: source, selectionStore: store })
assert.deepEqual(initial.map(plugin => [plugin.id, plugin.description, plugin.installed, plugin.enabled, plugin.protected]), [
['platform-management', { zh: 'platform-management 功能', en: 'platform-management feature' }, true, true, true],
['diagnostics', { zh: 'diagnostics 功能', en: 'diagnostics feature' }, true, true, false],
])
const active = await materializeSystemPluginSelection({
environmentRoot: environment,
sourceRoot: source,
outputRoot: join(root, 'views'),
selectionStore: store,
})
await assert.rejects(store.configure(pluginIds, 'platform-management', 'uninstall'), /managed by the platform/)
await assert.rejects(store.recover(pluginIds, 'diagnostics', 'uninstall'), /not a platform recovery target/)
await store.recover(pluginIds, 'platform-management', 'uninstall')
assert.deepEqual(await store.read(pluginIds).then(value => value['platform-management']), {
installed: false, enabled: false, protected: true,
})
await store.recover(pluginIds, 'platform-management', 'install')
await store.configure(pluginIds, 'diagnostics', 'disable')
const pending = await listManagedSystemPlugins({
environmentRoot: environment,
sourceRoot: source,
selectionStore: store,
activeRoot: active.path,
})
assert.deepEqual(pending.find(plugin => plugin.id === 'diagnostics'), {
id: 'diagnostics',
artifactId: 'system-plugin-diagnostics',
sha256: artifacts.find(artifact => artifact.pluginId === 'diagnostics').sha256,
description: { zh: 'diagnostics 功能', en: 'diagnostics feature' },
installed: true,
enabled: false,
activeInstalled: true,
activeEnabled: true,
pendingRestart: true,
protected: false,
reason: null,
})
const discarded = await discardSystemPluginSelection({
environmentRoot: environment,
sourceRoot: source,
selectionStore: store,
activeRoot: active.path,
})
assert.equal(discarded.find(plugin => plugin.id === 'diagnostics').pendingRestart, false)
assert.deepEqual((await store.read(pluginIds)).diagnostics, {
installed: true, enabled: true, protected: false,
})
await store.configure(pluginIds, 'diagnostics', 'disable')
const disabled = await materializeSystemPluginSelection({
environmentRoot: environment,
sourceRoot: source,
outputRoot: join(root, 'views'),
selectionStore: store,
})
assert.equal((await lstat(join(disabled.path, 'packages', 'diagnostics'))).isSymbolicLink(), true)
assert.deepEqual(JSON.parse(await readFile(join(disabled.path, 'cordis.patch.yml'), 'utf8'))
.flatMap(entry => entry.insert).map(row => row.name), ['@dsh-docker/platform-management'])
assert.equal((await listManagedSystemPlugins({
environmentRoot: environment,
sourceRoot: source,
selectionStore: store,
activeRoot: disabled.path,
})).find(plugin => plugin.id === 'diagnostics').pendingRestart, false)
await store.configure(pluginIds, 'diagnostics', 'uninstall')
const deleted = await materializeSystemPluginSelection({
environmentRoot: environment,
sourceRoot: source,
outputRoot: join(root, 'views'),
selectionStore: store,
})
await assert.rejects(lstat(join(deleted.path, 'packages', 'diagnostics')), error => error.code === 'ENOENT')
assert.deepEqual((await listManagedSystemPlugins({ environmentRoot: environment, sourceRoot: source, selectionStore: store }))
.map(plugin => [plugin.id, plugin.installed, plugin.enabled]), [
['platform-management', true, true],
['diagnostics', false, false],
])
await store.configure(pluginIds, 'diagnostics', 'install')
assert.deepEqual((await new SystemPluginSelectionStore(join(root, 'state', 'system-plugins.json')).read(pluginIds)).diagnostics, {
installed: true,
enabled: true,
protected: false,
})
})
@@ -0,0 +1,113 @@
import assert from 'node:assert/strict'
import { lstat, mkdir, readFile, readlink, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { mkdtemp } from 'node:fs/promises'
import test from 'node:test'
import {
readSystemSkillCatalog,
SystemSkillManager,
} from '../../control-plane/modules/skill-manager/index.mjs'
async function fixture() {
const root = await mkdtemp(join(tmpdir(), 'dsh-system-skills-'))
const sourceRoot = join(root, 'source')
const bundle = join(sourceRoot, 'operations')
await mkdir(join(bundle, 'references'), { recursive: true })
await writeFile(join(sourceRoot, 'catalog.json'), JSON.stringify({
schema: 1,
skills: [{
id: 'operations',
source: 'operations',
description: { en: 'Operate DSH.', zh: '操作 DSH。' },
}],
}))
await writeFile(join(bundle, 'SKILL.md'), '---\nname: operations\ndescription: Operate DSH.\n---\n\n# Operations\n')
await writeFile(join(bundle, 'references', 'guide.md'), '# Guide\n')
return {
root,
sourceRoot,
viewRoot: join(root, 'run', 'skills'),
statePath: join(root, 'state', 'skills.json'),
}
}
test('System Skills are identified by signed catalog ID and content hash', async () => {
const { sourceRoot } = await fixture()
const catalog = await readSystemSkillCatalog(sourceRoot)
assert.equal(catalog.length, 1)
assert.equal(catalog[0].id, 'operations')
assert.match(catalog[0].sha256, /^[0-9a-f]{64}$/)
assert.deepEqual(catalog[0].description, { en: 'Operate DSH.', zh: '操作 DSH。' })
})
test('System Skill selection hot-updates one stable view and persists install state', async () => {
const paths = await fixture()
const manager = new SystemSkillManager(paths)
assert.deepEqual(await manager.initialize(), [{
id: 'operations',
sha256: (await manager.list())[0].sha256,
description: { en: 'Operate DSH.', zh: '操作 DSH。' },
installed: true,
enabled: true,
}])
assert.equal(await readlink(join(paths.viewRoot, 'operations')), join(paths.sourceRoot, 'operations'))
assert.equal((await manager.configure('operations', 'disable'))[0].enabled, false)
await assert.rejects(lstat(join(paths.viewRoot, 'operations')), { code: 'ENOENT' })
assert.equal((await manager.configure('operations', 'enable'))[0].enabled, true)
assert.equal(await readlink(join(paths.viewRoot, 'operations')), join(paths.sourceRoot, 'operations'))
const uninstalled = (await manager.configure('operations', 'uninstall'))[0]
assert.deepEqual({ installed: uninstalled.installed, enabled: uninstalled.enabled }, { installed: false, enabled: false })
await assert.rejects(manager.configure('operations', 'enable'), /must be installed/)
assert.equal((await new SystemSkillManager(paths).initialize())[0].installed, false)
assert.equal((await manager.configure('operations', 'install'))[0].enabled, true)
})
test('System Skill state ignores removed entries and defaults newly signed skills on', async () => {
const paths = await fixture()
const manager = new SystemSkillManager(paths)
await manager.initialize()
await manager.configure('operations', 'uninstall')
const second = join(paths.sourceRoot, 'second')
await mkdir(second)
await writeFile(join(second, 'SKILL.md'), '---\nname: second\ndescription: Second.\n---\n\n# Second\n')
await writeFile(join(paths.sourceRoot, 'catalog.json'), JSON.stringify({
schema: 1,
skills: [{ id: 'second', source: 'second', description: { en: 'Second.', zh: '第二个。' } }],
}))
const listed = await manager.initialize()
assert.deepEqual(listed.map(skill => ({ id: skill.id, installed: skill.installed, enabled: skill.enabled })), [
{ id: 'second', installed: true, enabled: true },
])
const saved = JSON.parse(await readFile(paths.statePath, 'utf8'))
assert.deepEqual(Object.keys(saved.skills), ['second'])
})
test('System Skill manager switches to a newly selected Environment catalog', async () => {
const paths = await fixture()
const manager = new SystemSkillManager(paths)
await manager.initialize()
const replacementRoot = join(paths.root, 'replacement')
const replacement = join(replacementRoot, 'replacement')
await mkdir(replacement, { recursive: true })
await writeFile(join(replacement, 'SKILL.md'), '---\nname: replacement\ndescription: Replacement.\n---\n')
await writeFile(join(replacementRoot, 'catalog.json'), JSON.stringify({
schema: 1,
skills: [{ id: 'replacement', source: 'replacement', description: { en: 'Replacement.', zh: '替代。' } }],
}))
assert.deepEqual((await manager.useSourceRoot(replacementRoot)).map(skill => skill.id), ['replacement'])
await assert.rejects(lstat(join(paths.viewRoot, 'operations')), { code: 'ENOENT' })
assert.equal(await readlink(join(paths.viewRoot, 'replacement')), replacement)
await assert.rejects(manager.configure('operations', 'enable'), /current Environment/)
})
test('System Skill catalog rejects source aliases and mismatched frontmatter', async () => {
const paths = await fixture()
const document = JSON.parse(await readFile(join(paths.sourceRoot, 'catalog.json'), 'utf8'))
document.skills[0].source = '../operations'
await writeFile(join(paths.sourceRoot, 'catalog.json'), JSON.stringify(document))
await assert.rejects(readSystemSkillCatalog(paths.sourceRoot), /source is invalid/)
})
@@ -0,0 +1,248 @@
import assert from 'node:assert/strict'
import { request as httpRequest } from 'node:http'
import { mkdir, mkdtemp } from 'node:fs/promises'
import { tmpdir, userInfo } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import WebSocket from '../../control-plane/services/management/node_modules/ws/wrapper.mjs'
import { createManagementServer } from '../../control-plane/services/management/server.mjs'
import { TerminalSessionManager, terminalSessionInternals } from '../../control-plane/services/management/terminal/sessions.mjs'
const API = '/_dsh_platform/api/v1/'
function request(port, method, path, body) {
return new Promise((resolve, reject) => {
const encoded = body === undefined ? undefined : Buffer.from(JSON.stringify(body))
const outgoing = httpRequest({
host: '127.0.0.1', port, method, path,
headers: encoded === undefined ? undefined : { 'content-type': 'application/json', 'content-length': encoded.byteLength },
}, response => {
const chunks = []
response.on('data', chunk => chunks.push(chunk))
response.once('end', () => {
const text = Buffer.concat(chunks).toString('utf8')
resolve({ status: response.statusCode, body: text === '' ? undefined : JSON.parse(text) })
})
})
outgoing.once('error', reject)
outgoing.end(encoded)
})
}
function collector(url) {
const socket = new WebSocket(url)
let output = ''
let exit
const waiters = new Set()
socket.on('message', bytes => {
const value = JSON.parse(bytes.toString())
if (value.type === 'output') output += value.data
if (value.type === 'exit') exit = value
for (const waiter of waiters) waiter()
})
const waitFor = async predicate => {
for (let attempt = 0; attempt < 400; attempt += 1) {
if (predicate({ output, exit })) return { output, exit }
await new Promise(resolve => {
const timer = setTimeout(() => { waiters.delete(done); resolve() }, 25)
const done = () => { clearTimeout(timer); waiters.delete(done); resolve() }
waiters.add(done)
})
}
throw new Error(`terminal output did not reach expected state: ${output}`)
}
return { socket, waitFor, output: () => output }
}
async function fixture({ reconnectMs = 30_000 } = {}) {
const root = await mkdtemp(join(tmpdir(), 'dsh-terminal-'))
const workspace = join(root, 'workspace')
const dshHome = join(root, 'dsh')
await mkdir(workspace)
await mkdir(dshHome)
const reports = []
const terminalSessions = new TerminalSessionManager({
cwd: workspace,
dshHome,
reconnectMs,
report: async (message, fields) => { reports.push({ message, fields }) },
})
const logs = {
diagnostic: async () => {}, query: async () => [], on: () => {}, off: () => {},
}
const state = { read: async () => ({}), on: () => {}, off: () => {} }
const coordinator = { publicStatus: async () => ({}), hasActiveTask: () => false, state, on: () => {}, off: () => {} }
const server = createManagementServer({ coordinator, logs, terminalSessions })
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(0, '127.0.0.1', resolve)
})
const port = server.address().port
return {
root, workspace, dshHome, reports, terminalSessions, server, port,
close: async () => {
await terminalSessions.shutdown()
await new Promise(resolve => server.close(resolve))
},
}
}
test('terminal HTTP and WebSocket lifecycle provides a real bounded PTY', async () => {
const value = await fixture()
try {
const created = await request(value.port, 'POST', `${API}terminal/sessions`, { cols: 92, rows: 31 })
assert.equal(created.status, 201)
assert.match(created.body.sessionId, terminalSessionInternals.SESSION_PATTERN)
const path = `${API}terminal/sessions/${created.body.sessionId}`
assert.equal((await request(value.port, 'GET', path)).body.status, 'running')
const first = collector(`ws://127.0.0.1:${value.port}${path}/stream`)
await new Promise((resolve, reject) => {
first.socket.once('open', resolve)
first.socket.once('error', reject)
})
first.socket.send(JSON.stringify({ type: 'resize', cols: 101, rows: 37 }))
first.socket.send(JSON.stringify({
type: 'input',
data: "printf '\\033[31mred\\033[0m\\n'; printf '中文✓\\n'; printf 'cwd=%s\\n' \"$PWD\"; printf 'home=%s\\n' \"$HOME\"; printf 'dsh=%s\\n' \"$DSH_HOME\"; printf 'uid=%s\\n' \"$(id -u)\"; printf 'gids=%s\\n' \"$(id -G)\"; printf 'path=%s\\n' \"$PATH\"; printf 'umask=%s\\n' \"$(umask)\"; stty size; printf 'command-complete\\n'\n",
}))
let initial
try {
initial = await first.waitFor(({ output }) => output.includes('\x1b[31mred\x1b[0m') && output.match(/command-complete/g)?.length >= 3)
} catch (error) {
error.message += `; status=${JSON.stringify((await request(value.port, 'GET', path)).body)} reports=${JSON.stringify(value.reports)}`
throw error
}
assert.match(initial.output, /\x1b\[31mred\x1b\[0m/)
assert.match(initial.output, /中文✓/)
assert.match(initial.output, new RegExp(`cwd=${value.workspace.replaceAll('/', '\\/')}`))
assert.match(initial.output, new RegExp(`home=${userInfo().homedir.replaceAll('/', '\\/')}`))
assert.match(initial.output, new RegExp(`dsh=${value.dshHome.replaceAll('/', '\\/')}`))
assert.match(initial.output, new RegExp(`uid=${String(process.getuid())}`))
for (const group of process.getgroups()) assert.match(initial.output, new RegExp(`gids=[^\\r\\n]*\\b${String(group)}\\b`))
assert.match(initial.output, /path=\S+/)
assert.match(initial.output, /umask=0002/)
assert.match(initial.output, /37 101/)
first.socket.send(JSON.stringify({ type: 'input', data: "read -p 'answer: ' value; printf 'interactive=%s\\n' \"$value\"\n" }))
await first.waitFor(({ output }) => output.includes('answer:'))
first.socket.send(JSON.stringify({ type: 'input', data: 'works\n' }))
await first.waitFor(({ output }) => output.includes('interactive=works'))
first.socket.send(JSON.stringify({ type: 'input', data: "printf 'long-start\\n'; head -c 300000 /dev/zero | tr '\\0' x; printf '\\nlong-tail\\n'\n" }))
await first.waitFor(({ output }) => output.length > 300_000 && output.match(/long-tail/g)?.length >= 2)
first.socket.send(JSON.stringify({ type: 'input', data: "printf 'cached-marker\\n'\n" }))
await first.waitFor(({ output }) => output.includes('cached-marker'))
first.socket.close()
await new Promise(resolve => first.socket.once('close', resolve))
const second = collector(`ws://127.0.0.1:${value.port}${path}/stream`)
await new Promise((resolve, reject) => {
second.socket.once('open', resolve)
second.socket.once('error', reject)
})
const replay = (await second.waitFor(({ output }) => output.includes('cached-marker'))).output
assert.match(replay, /long-tail/)
assert.match(replay, /cached-marker/)
assert.doesNotMatch(replay, /long-start/)
assert.ok(Buffer.byteLength(replay) <= 256 * 1024 + 1024)
second.socket.send(JSON.stringify({ type: 'input', data: 'exit 7\n' }))
const ended = await second.waitFor(result => result.exit !== undefined)
assert.deepEqual({ code: ended.exit.code, signal: ended.exit.signal }, { code: 7, signal: null })
assert.equal((await request(value.port, 'GET', path)).body.status, 'exited')
assert.equal((await request(value.port, 'DELETE', path)).body.closed, true)
second.socket.close()
const messages = value.reports.map(entry => entry.message)
for (const event of [
'terminal.session.created', 'terminal.session.connected', 'terminal.session.disconnected',
'terminal.session.reconnected', 'terminal.session.exited', 'terminal.session.closed',
]) assert.ok(messages.includes(event), `${event} was not reported`)
assert.doesNotMatch(JSON.stringify(value.reports), /cached-marker|interactive=works|command-complete/)
} finally {
await value.close()
}
})
test('terminal sessions accept a trusted per-session network environment', async () => {
const value = await fixture()
try {
const created = value.terminalSessions.create({}, {
HTTP_PROXY: 'http://127.0.0.1:17896',
NO_PROXY: 'localhost,.example.com',
})
const path = `${API}terminal/sessions/${created.sessionId}`
const connection = collector(`ws://127.0.0.1:${value.port}${path}/stream`)
await new Promise((resolve, reject) => {
connection.socket.once('open', resolve)
connection.socket.once('error', reject)
})
connection.socket.send(JSON.stringify({
type: 'input',
data: "printf 'proxy=%s\\nno_proxy=%s\\n' \"$HTTP_PROXY\" \"$NO_PROXY\"\n",
}))
const result = await connection.waitFor(({ output }) => output.includes('no_proxy=localhost,.example.com'))
assert.match(result.output, /proxy=http:\/\/127\.0\.0\.1:17896/)
connection.socket.close()
} finally {
await value.close()
}
})
test('terminal sessions enforce dimensions, message size, expiry, and exact IDs', async () => {
const value = await fixture({ reconnectMs: 80 })
try {
for (const body of [{ cols: 1, rows: 24 }, { cols: 501, rows: 24 }, { cols: 80, rows: 0 }, { cols: 80, rows: 201 }]) {
assert.equal((await request(value.port, 'POST', `${API}terminal/sessions`, body)).status, 400)
}
const created = (await request(value.port, 'POST', `${API}terminal/sessions`, {})).body
const path = `${API}terminal/sessions/${created.sessionId}`
const connection = collector(`ws://127.0.0.1:${value.port}${path}/stream`)
await new Promise((resolve, reject) => {
connection.socket.once('open', resolve)
connection.socket.once('error', reject)
})
connection.socket.send(JSON.stringify({ type: 'resize', cols: 1, rows: 20 }))
const closeCode = await new Promise(resolve => connection.socket.once('close', resolve))
assert.equal(closeCode, 1008)
let expiredStatus
for (let attempt = 0; attempt < 100; attempt += 1) {
expiredStatus = (await request(value.port, 'GET', path)).status
if (expiredStatus === 404) break
await new Promise(resolve => setTimeout(resolve, 20))
}
assert.equal(expiredStatus, 404)
const oversized = (await request(value.port, 'POST', `${API}terminal/sessions`, {})).body
const oversizedConnection = collector(`ws://127.0.0.1:${value.port}${API}terminal/sessions/${oversized.sessionId}/stream`)
await new Promise((resolve, reject) => {
oversizedConnection.socket.once('open', resolve)
oversizedConnection.socket.once('error', reject)
})
oversizedConnection.socket.send(JSON.stringify({ type: 'input', data: 'x'.repeat(64 * 1024 + 1) }))
const oversizedClose = await new Promise(resolve => oversizedConnection.socket.once('close', resolve))
assert.ok([1008, 1009].includes(oversizedClose))
assert.equal((await request(value.port, 'GET', `${API}terminal/sessions/not-a-uuid`)).status, 404)
} finally {
await value.close()
}
})
test('terminal sessions default to the Root maintenance home', () => {
const terminalSessions = new TerminalSessionManager()
assert.equal(terminalSessions.cwd, '/root')
})
test('terminal shutdown waits for every helper process to close', async () => {
const root = await mkdtemp(join(tmpdir(), 'dsh-terminal-shutdown-'))
const workspace = join(root, 'workspace')
const dshHome = join(root, 'dsh')
await mkdir(workspace)
await mkdir(dshHome)
const terminalSessions = new TerminalSessionManager({ cwd: workspace, dshHome })
const created = terminalSessions.create()
const session = terminalSessions.sessions.get(created.sessionId)
assert.equal(session.child.exitCode, null)
await terminalSessions.shutdown()
assert.equal(terminalSessions.sessions.size, 0)
assert.equal(session.child.exitCode !== null || session.child.signalCode !== null, true)
})
@@ -0,0 +1,105 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import {
TotpFlowStore,
TotpRetryLimiter,
generateTotpSecret,
totpCode,
totpUri,
validTotpSecret,
verifyTotpCode,
} from '../../control-plane/services/access-manager/lib/totp.mjs'
test('generates interoperable TOTP secrets, codes, and enrollment URIs', () => {
const secret = generateTotpSecret(size => Buffer.alloc(size, 7))
assert.equal(secret.length, 32)
assert.equal(validTotpSecret(secret), true)
const code = totpCode(secret, { now: 59_000 })
assert.equal(code.length, 6)
assert.equal(verifyTotpCode(code, secret, { now: 59_000 }), true)
assert.equal(verifyTotpCode(code, secret, { now: 89_000 }), true)
assert.equal(verifyTotpCode('not-six-digits', secret, { now: 59_000 }), false)
assert.match(totpUri({ secret, username: '管理 员' }), /^otpauth:\/\/totp\/DSH%20Docker%3A%E7%AE%A1%E7%90%86%20%E5%91%98\?secret=/)
})
test('binds one-time TOTP enrollment and login challenges to account state and sessions', () => {
let now = 1_000
let fill = 1
const flows = new TotpFlowStore({
now: () => now,
random: size => Buffer.alloc(size, fill++),
enrollmentTtlMs: 100,
loginTtlMs: 100,
})
const account = { accountId: 'account-a', revision: 'revision-a' }
const enrollment = flows.createEnrollment(account, 'management-a')
assert.equal(flows.enrollment(enrollment.token, account, 'management-a')?.value.secret, enrollment.secret)
assert.equal(flows.enrollment(enrollment.token, account, 'management-a')?.value.confirmed, false)
const boundEnrollment = flows.enrollment(enrollment.token, account, 'management-a')
assert.equal(flows.confirmEnrollment(boundEnrollment.key), true)
assert.equal(flows.enrollment(enrollment.token, account, 'management-a')?.value.confirmed, true)
assert.equal(flows.enrollment(enrollment.token, account, 'management-b'), undefined)
assert.equal(flows.enrollment(enrollment.token, { ...account, revision: 'revision-b' }, 'management-a'), undefined)
assert.equal(flows.cancelEnrollment(enrollment.token, account, 'management-a'), true)
assert.equal(flows.confirmEnrollment(boundEnrollment.key), false)
assert.equal(flows.enrollment(enrollment.token, account, 'management-a'), undefined)
const disabling = flows.createDisableConfirmation(account, 'management-a')
assert.match(disabling.token, /^dshtd_/)
const boundDisable = flows.disableConfirmation(disabling.token, account, 'management-a')
assert.equal(boundDisable?.value.confirmed, false)
assert.equal(flows.confirmDisable(boundDisable.key), true)
assert.equal(flows.disableConfirmation(disabling.token, account, 'management-a')?.value.confirmed, true)
assert.equal(flows.disableConfirmation(disabling.token, account, 'management-b'), undefined)
assert.equal(flows.disableConfirmation(disabling.token, { ...account, revision: 'revision-b' }, 'management-a'), undefined)
assert.equal(flows.cancelDisableConfirmation(disabling.token, account, 'management-a'), true)
assert.equal(flows.disableConfirmation(disabling.token, account, 'management-a'), undefined)
const login = flows.createLogin(account, { origin: 'https://dsh.example', authenticationSource: 'browser:a' })
assert.equal(flows.login(login.token, account)?.value.origin, 'https://dsh.example')
now += 101
assert.equal(flows.login(login.token, account), undefined)
})
test('applies fixed TOTP retry and separate source and global daily windows', () => {
let now = 1_000
assert.equal(new TotpRetryLimiter().sourceDailyLimit, 25)
assert.equal(new TotpRetryLimiter().globalDailyLimit, 50)
const limiter = new TotpRetryLimiter({
now: () => now,
threshold: 3,
retryMs: 10_000,
sourceDailyLimit: 4,
globalDailyLimit: 6,
dailyWindowMs: 100_000,
})
assert.equal(limiter.fail('account', 'browser:a').kind, null)
assert.equal(limiter.fail('account', 'browser:a').kind, null)
assert.deepEqual(limiter.fail('account', 'browser:a'), { kind: 'retry', retryAfterSeconds: 10 })
assert.equal(limiter.clearDailyLimits('account').cleared, 2)
assert.deepEqual(limiter.retry('account', 'browser:a'), { kind: 'retry', retryAfterSeconds: 10 })
now += 10_000
assert.deepEqual(limiter.fail('account', 'browser:a'), { kind: 'retry', retryAfterSeconds: 10 })
now += 10_000
limiter.succeed('account', 'browser:a')
assert.deepEqual(limiter.retry('account', 'browser:a'), { kind: null, retryAfterSeconds: 0 })
for (let index = 0; index < 3; index += 1) limiter.fail('account', 'browser:b')
now += 10_000
assert.equal(limiter.fail('account', 'browser:b').kind, 'rate')
limiter.clearDailyLimits('account')
assert.equal(limiter.retry('account', 'browser:b').kind, 'retry')
now += 10_000
assert.equal(limiter.retry('account', 'browser:b').kind, null)
for (let index = 0; index < 3; index += 1) {
limiter.succeed('account', 'browser:a')
limiter.fail('account', 'browser:a')
limiter.succeed('account', 'browser:b')
limiter.fail('account', 'browser:b')
}
assert.equal(limiter.retry('account', 'browser:a').kind, 'rate')
assert.equal(limiter.clearDailyLimits('account', { globalOnly: true }).cleared, 1)
assert.equal(limiter.retry('account', 'browser:a').kind, null)
})
File diff suppressed because it is too large. Load diff
Loaded 100 of 302 files, more files were not shown because too many files have changed in this diff. Show more