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,195 @@
import { createHash, randomBytes, randomUUID } from 'node:crypto'
import { chmod, mkdir, open, readFile, readdir, rename, rm, stat } from 'node:fs/promises'
import { join } from 'node:path'
import { canonicalJson } from '../../../../platform/lib/canonical-json.mjs'
import { durableReplace } from '../../../../platform/lib/atomic.mjs'
import { defaultProxyConfiguration, validateProxyConfiguration } from './contracts.mjs'
import { ProxyConfigurationError } from './errors.mjs'
function hash(bytes) {
return createHash('sha256').update(bytes).digest('hex')
}
async function syncDirectory(path) {
const handle = await open(path, 'r')
try { await handle.sync() } finally { await handle.close() }
}
async function writeSynced(path, bytes, mode) {
const handle = await open(path, 'wx', mode)
try {
await handle.writeFile(bytes)
await handle.sync()
} finally {
await handle.close()
}
}
function revision() {
return randomBytes(32).toString('base64url')
}
function parse(bytes, label) {
try { return JSON.parse(bytes.toString('utf8')) } catch { throw new ProxyConfigurationError(`${label} is corrupt`, { code: 'PROXY_STATE_CORRUPT', stage: 'load' }) }
}
export class ProxyConfigurationStore {
constructor(root, { now = () => new Date() } = {}) {
this.root = root
this.revisionsRoot = join(root, 'revisions')
this.currentPath = join(root, 'current.json')
this.now = now
this.queue = Promise.resolve()
}
exclusive(operation) {
const result = this.queue.then(operation, operation)
this.queue = result.then(() => undefined, () => undefined)
return result
}
async prepare() {
await mkdir(this.revisionsRoot, { recursive: true, mode: 0o700 })
await chmod(this.root, 0o700)
await chmod(this.revisionsRoot, 0o700)
}
async readPointer() {
try {
const pointer = parse(await readFile(this.currentPath), 'proxy current pointer')
if (pointer?.schema !== 1 || typeof pointer.revision !== 'string' || !/^[A-Za-z0-9_-]{43}$/.test(pointer.revision)) {
throw new ProxyConfigurationError('proxy current pointer is corrupt', { code: 'PROXY_STATE_CORRUPT', stage: 'load' })
}
return pointer.revision
} catch (error) {
if (error?.code === 'ENOENT') return null
throw error
}
}
async readRevision(id) {
const root = join(this.revisionsRoot, id)
const [configBytes, credentialsBytes, manifestBytes] = await Promise.all([
readFile(join(root, 'config.json')),
readFile(join(root, 'credentials.json')),
readFile(join(root, 'manifest.json')),
])
const manifest = parse(manifestBytes, 'proxy revision manifest')
if (
manifest?.schema !== 1 || manifest.revision !== id
|| manifest.files?.config !== hash(configBytes)
|| manifest.files?.credentials !== hash(credentialsBytes)
) throw new ProxyConfigurationError('proxy revision manifest does not match its files', { code: 'PROXY_STATE_CORRUPT', stage: 'load' })
const configuration = parse(configBytes, 'proxy configuration')
const credentials = parse(credentialsBytes, 'proxy credentials')
if (credentials?.schema !== 1 || credentials.username !== configuration?.proxy?.username || !('password' in credentials)) {
throw new ProxyConfigurationError('proxy credentials do not match configuration', { code: 'PROXY_STATE_CORRUPT', stage: 'load' })
}
const validated = validateProxyConfiguration({
...configuration,
proxy: { ...configuration.proxy, password: credentials.password },
noProxy: { user: configuration.noProxy.user },
})
if (validated.configuration.proxy.passwordConfigured !== configuration.proxy.passwordConfigured) {
throw new ProxyConfigurationError('proxy password state is inconsistent', { code: 'PROXY_STATE_CORRUPT', stage: 'load' })
}
return Object.freeze({ revision: id, configuration: validated.configuration, credentials: validated.credentials, createdAt: manifest.createdAt })
}
async recoveryCandidates() {
const entries = await readdir(this.revisionsRoot, { withFileTypes: true })
const candidates = []
for (const entry of entries) {
if (!entry.isDirectory() || entry.name.startsWith('.')) continue
try {
const loaded = await this.readRevision(entry.name)
candidates.push(loaded)
} catch {}
}
return candidates.sort((left, right) => right.createdAt.localeCompare(left.createdAt))
}
async load() {
await this.prepare()
let current = null
let corruptPointer = false
try {
current = await this.readPointer()
} catch (error) {
if (error?.code !== 'PROXY_STATE_CORRUPT') throw error
corruptPointer = true
}
if (current !== null) {
try { return Object.freeze({ ...(await this.readRevision(current)), recovery: 'none' }) } catch (error) {
if (error?.code !== 'PROXY_STATE_CORRUPT' && error?.code !== 'ENOENT') throw error
}
}
const recovered = (await this.recoveryCandidates())[0]
if (recovered !== undefined) {
await durableReplace(this.currentPath, canonicalJson({ schema: 1, revision: recovered.revision }), 0o640)
return Object.freeze({ ...recovered, recovery: 'previous' })
}
const entries = await readdir(this.revisionsRoot, { withFileTypes: true })
const hadPublishedState = corruptPointer || entries.some(entry => entry.isDirectory() && !entry.name.startsWith('.'))
if (corruptPointer) {
await rename(this.currentPath, join(this.root, `current.corrupt.${randomUUID()}.json`))
await syncDirectory(this.root)
}
const initialized = await this.commit({ baseRevision: null, value: defaultProxyConfiguration() })
return Object.freeze({ ...initialized, recovery: hadPublishedState ? 'reset-disabled' : 'none' })
}
commit(request) {
return this.exclusive(() => this.commitUnlocked(request))
}
async commitUnlocked({ baseRevision, value }) {
await this.prepare()
let current = null
try { current = await this.loadCurrentWithoutRecovery() } catch (error) {
if (error?.code !== 'ENOENT') throw error
}
if ((current?.revision ?? null) !== baseRevision) {
throw new ProxyConfigurationError('proxy configuration changed', {
code: 'REVISION_CONFLICT', statusCode: 409, stage: 'activate',
})
}
const validated = validateProxyConfiguration(value, { existingPassword: current?.credentials.password ?? null })
const id = revision()
const staging = join(this.revisionsRoot, `.${id}.${randomUUID()}.tmp`)
const published = join(this.revisionsRoot, id)
await mkdir(staging, { mode: 0o700 })
try {
const configBytes = canonicalJson(validated.configuration)
const credentialsBytes = canonicalJson({ schema: 1, ...validated.credentials })
const manifestBytes = canonicalJson({
schema: 1,
revision: id,
createdAt: this.now().toISOString(),
files: { config: hash(configBytes), credentials: hash(credentialsBytes) },
})
await writeSynced(join(staging, 'config.json'), configBytes, 0o640)
await writeSynced(join(staging, 'credentials.json'), credentialsBytes, 0o600)
await writeSynced(join(staging, 'manifest.json'), manifestBytes, 0o640)
await syncDirectory(staging)
await rename(staging, published)
await syncDirectory(this.revisionsRoot)
const candidate = await this.readRevision(id)
await durableReplace(this.currentPath, canonicalJson({ schema: 1, revision: id }), 0o640)
return candidate
} catch (error) {
await rm(staging, { recursive: true, force: true })
throw error
}
}
async loadCurrentWithoutRecovery() {
const current = await this.readPointer()
if (current === null) {
const error = new Error('proxy configuration is not initialized')
error.code = 'ENOENT'
throw error
}
return this.readRevision(current)
}
}