import fs from 'node:fs/promises'
import path from 'node:path'
import b4a from 'b4a'
import test from 'brittle'
import {
releaseExclusiveFileLock,
tryExclusiveFileLock
} from '@hiverelay/blind-peercred'
import {
BLIND_STORE_ROOT_CLASSIFICATION,
BLIND_STORE_SESSION_INTEGRATION_STATUS,
BlindStoreSession,
acquireBlindStoreSessionTransactionLease,
classifyBlindStoreRoot,
transferBlindStoreSessionTransactionLease,
verifyBlindStoreSessionTransactionLease
} from '../store-session.js'
import {
createBlindBoundaryScratch,
removeBlindBoundaryScratch
} from '../../../test/blind-boundary-scratch.js'
async function temporaryRoot (t, name = 'hiverelay-blind-store-session-') {
const root = await createBlindBoundaryScratch(name)
await fs.chmod(root, 0o700)
t.teardown(() => removeBlindBoundaryScratch(root))
return root
}
async function createManifestedLayout (t) {
const root = await temporaryRoot(t)
const control = path.join(root, 'control')
await fs.mkdir(control, { mode: 0o700 })
await fs.writeFile(path.join(control, 'writer.lock.v1'), b4a.alloc(0), { mode: 0o600 })
await fs.writeFile(path.join(control, 'wal.v2'), b4a.alloc(0), { mode: 0o600 })
await fs.writeFile(path.join(control, 'manifest-a.v1'), b4a.alloc(64, 0x31), { mode: 0o600 })
return { root, control, lock: path.join(control, 'writer.lock.v1') }
}
async function treeSnapshot (root, relative = '') {
const directory = path.join(root, relative)
const output = []
for (const name of (await fs.readdir(directory)).sort()) {
const childRelative = relative ? path.join(relative, name) : name
const child = path.join(root, childRelative)
const stat = await fs.lstat(child)
const record = {
path: childRelative,
mode: stat.mode & 0o777,
nlink: stat.nlink,
type: stat.isDirectory() ? 'directory' : stat.isFile() ? 'file' : stat.isSymbolicLink() ? 'symlink' : 'other'
}
if (stat.isFile()) record.bytes = (await fs.readFile(child)).toString('hex')
if (stat.isSymbolicLink()) record.target = await fs.readlink(child)
output.push(record)
if (stat.isDirectory()) output.push(...await treeSnapshot(root, childRelative))
}
return output
}
async function rejectsCode (t, promise, code) {
try {
await promise
t.fail(`expected ${code}`)
} catch (error) {
t.is(error.code, code)
}
}
test('store classifier accepts only exact runtime binding placement and still refuses ambiguous legacy roots', async t => {
const root = await temporaryRoot(t)
await fs.writeFile(path.join(root, 'runtime-binding.v1'), b4a.alloc(213, 0x42), { mode: 0o600 })
const before = await treeSnapshot(root)
const classified = await classifyBlindStoreRoot(root)
t.is(classified.kind, BLIND_STORE_ROOT_CLASSIFICATION.PRISTINE)
const session = new BlindStoreSession({ root })
await t.exception(session.open(), /not explicitly authorized/)
t.alike(await treeSnapshot(root), before)
t.is(BLIND_STORE_SESSION_INTEGRATION_STATUS.transactionStoreLockOwnership, 'STORE_SESSION_TRANSACTION_LEASE')
t.is(BLIND_STORE_SESSION_INTEGRATION_STATUS.blocker,
'TWO_SLOT_MANIFEST_RUNTIME_INTEGRATION_UNASSEMBLED')
const walV1Root = await temporaryRoot(t, 'hiverelay-blind-wal-v1-')
const control = path.join(walV1Root, 'control')
await fs.mkdir(control, { mode: 0o700 })
await fs.writeFile(path.join(control, 'writer.lock.v1'), b4a.alloc(0), { mode: 0o600 })
await fs.writeFile(path.join(control, 'manifest-a.v1'), b4a.alloc(64, 0x51), { mode: 0o600 })
await fs.writeFile(path.join(control, 'wal.v1'), b4a.alloc(0), { mode: 0o600 })
const walV1Before = await treeSnapshot(walV1Root)
const walV1 = await classifyBlindStoreRoot(walV1Root)
t.is(walV1.kind, BLIND_STORE_ROOT_CLASSIFICATION.LEGACY_AMBIGUOUS)
t.ok(walV1.reason.includes('wal.v1'))
await t.exception(new BlindStoreSession({ root: walV1Root }).open(), /legacy or ambiguous/)
t.alike(await treeSnapshot(walV1Root), walV1Before)
const mixed = await createManifestedLayout(t)
await fs.writeFile(path.join(mixed.root, 'runtime-binding.v1'), b4a.alloc(213, 0x61), { mode: 0o600 })
const mixedBefore = await treeSnapshot(mixed.root)
const mixedClassification = await classifyBlindStoreRoot(mixed.root)
t.is(mixedClassification.kind, BLIND_STORE_ROOT_CLASSIFICATION.CURRENT_MANIFESTED)
const mixedSession = new BlindStoreSession({ root: mixed.root })
await mixedSession.open()
await mixedSession.close()
t.alike(await treeSnapshot(mixed.root), mixedBefore)
})
test('StoreSession opens only an existing manifested lock and owns it until close', async t => {
const state = await createManifestedLayout(t)
const classified = await classifyBlindStoreRoot(state.root)
t.is(classified.kind, BLIND_STORE_ROOT_CLASSIFICATION.CURRENT_MANIFESTED)
const competing = await fs.open(state.lock, 'r+')
t.teardown(() => competing.close())
t.is(tryExclusiveFileLock(competing), true)
const blocked = new BlindStoreSession({ root: state.root })
await t.exception(blocked.open(), /active writer/)
releaseExclusiveFileLock(competing)
const session = new BlindStoreSession({ root: state.root })
await session.open()
const context = session.lockContext()
t.is(context.classification, BLIND_STORE_ROOT_CLASSIFICATION.CURRENT_MANIFESTED)
t.is(context.ownsWriterLock, true)
t.is(context.transactionStoreLockOwnership, 'STORE_SESSION_TRANSACTION_LEASE')
t.is(tryExclusiveFileLock(competing), false)
await session.close()
t.is(tryExclusiveFileLock(competing), true)
releaseExclusiveFileLock(competing)
})
test('StoreSession detects a parent-directory path swap after opening the old lock inode', async t => {
const state = await createManifestedLayout(t)
const displaced = path.join(state.root, 'control-displaced')
let swapped = false
const session = new BlindStoreSession({
root: state.root,
faultInjector: async point => {
if (!swapped && point === 'store-session:after-classification') {
swapped = true
await fs.rename(state.control, displaced)
await fs.symlink(displaced, state.control)
}
}
})
await t.exception(session.open(), /layout changed/)
t.is(swapped, true)
const oldLock = await fs.open(path.join(displaced, 'writer.lock.v1'), 'r+')
t.is(tryExclusiveFileLock(oldLock), true)
releaseExclusiveFileLock(oldLock)
await oldLock.close()
})
test('root symlinks and symlinked manifest recovery artifacts fail without cleanup', async t => {
const state = await createManifestedLayout(t)
const linkedRoot = `${state.root}-link`
await fs.symlink(state.root, linkedRoot)
t.teardown(() => fs.unlink(linkedRoot).catch(() => {}))
await t.exception(classifyBlindStoreRoot(linkedRoot), /canonical daemon-owned private directory/)
await fs.unlink(path.join(state.control, 'manifest-a.v1'))
await fs.symlink('writer.lock.v1', path.join(state.control, 'manifest-a.v1'))
const temporary = path.join(state.control, `.manifest-b.v1.${'4'.repeat(32)}.tmp`)
await fs.symlink('writer.lock.v1', temporary)
const before = await treeSnapshot(state.root)
const classified = await classifyBlindStoreRoot(state.root)
t.is(classified.kind, BLIND_STORE_ROOT_CLASSIFICATION.LEGACY_AMBIGUOUS)
const session = new BlindStoreSession({ root: state.root })
await t.exception(session.open(), /legacy or ambiguous/)
t.alike(await treeSnapshot(state.root), before)
})
test('pristine lock creation requires explicit authority and a stable empty-root recheck', async t => {
const root = await temporaryRoot(t)
const refused = new BlindStoreSession({ root })
await t.exception(refused.open(), /not explicitly authorized/)
t.alike(await fs.readdir(root), [])
let injected = false
const raced = new BlindStoreSession({
root,
allowPristineBootstrap: true,
faultInjector: async point => {
if (!injected && point === 'store-session:after-classification') {
injected = true
await fs.writeFile(path.join(root, 'legacy-state'), b4a.from('legacy'), { mode: 0o600 })
}
}
})
await t.exception(raced.open(), /changed before bootstrap lock creation/)
t.alike(await treeSnapshot(root), [{
path: 'legacy-state',
mode: 0o600,
nlink: 1,
type: 'file',
bytes: b4a.from('legacy').toString('hex')
}])
const fresh = await temporaryRoot(t, 'hiverelay-blind-store-bootstrap-')
const session = new BlindStoreSession({ root: fresh, allowPristineBootstrap: true })
await session.open()
const context = session.lockContext()
t.is(context.classification, BLIND_STORE_ROOT_CLASSIFICATION.PRISTINE)
t.is(context.bootstrapCreated, true)
const competing = await fs.open(context.writerLockPath, 'r+')
t.is(tryExclusiveFileLock(competing), false)
await session.close()
t.is(tryExclusiveFileLock(competing), true)
releaseExclusiveFileLock(competing)
await competing.close()
})
test('close racing open cannot leave a StoreSession writer lock behind', async t => {
const state = await createManifestedLayout(t)
let unblock
const blocked = new Promise(resolve => { unblock = resolve })
let markReached
const reached = new Promise(resolve => { markReached = resolve })
const session = new BlindStoreSession({
root: state.root,
faultInjector: async point => {
if (point === 'store-session:after-classification') {
markReached()
await blocked
}
}
})
const opening = session.open()
await reached
const closing = session.close()
unblock()
await opening
await closing
const competing = await fs.open(state.lock, 'r+')
t.is(tryExclusiveFileLock(competing), true)
releaseExclusiveFileLock(competing)
await competing.close()
await t.exception(session.open(), /closed/)
})
test('transaction leases are branded, root-bound, singular, and keep the OS lock until release', async t => {
const unopenedState = await createManifestedLayout(t)
const unopened = new BlindStoreSession({ root: unopenedState.root })
await t.exception(Promise.resolve().then(() => unopened.lockContext()), /does not hold its writer lock/)
await unopened.close()
const state = await createManifestedLayout(t)
const session = new BlindStoreSession({ root: state.root })
await session.open()
const context = session.lockContext()
t.is(Object.isFrozen(context), true)
await rejectsCode(t,
acquireBlindStoreSessionTransactionLease(Object.freeze({ ...context }), state.root),
'BLIND_STORE_SESSION_CONTEXT_INVALID')
await rejectsCode(t,
acquireBlindStoreSessionTransactionLease(context, `${state.root}-wrong`),
'BLIND_STORE_SESSION_ROOT_MISMATCH')
const lease = await acquireBlindStoreSessionTransactionLease(context, state.root)
t.is(Object.isFrozen(lease), true)
await rejectsCode(t,
acquireBlindStoreSessionTransactionLease(context, state.root),
'BLIND_STORE_TRANSACTION_LEASE_ACTIVE')
const competing = await fs.open(state.lock, 'r+')
t.teardown(() => competing.close())
let closed = false
const closing = session.close().then(() => { closed = true })
await Promise.resolve()
t.is(closed, false)
t.is(tryExclusiveFileLock(competing), false)
lease.release()
await closing
t.is(closed, true)
t.is(tryExclusiveFileLock(competing), true)
releaseExclusiveFileLock(competing)
await rejectsCode(t,
acquireBlindStoreSessionTransactionLease(context, state.root),
'BLIND_STORE_SESSION_CLOSING')
})
test('transaction lease acquisition rejects a substituted writer-lock inode without mutation', async t => {
const state = await createManifestedLayout(t)
const session = new BlindStoreSession({ root: state.root })
await session.open()
const context = session.lockContext()
const displaced = `${state.lock}.displaced`
await fs.rename(state.lock, displaced)
await fs.writeFile(state.lock, b4a.alloc(0), { mode: 0o600 })
const before = await treeSnapshot(state.root)
await rejectsCode(t,
acquireBlindStoreSessionTransactionLease(context, state.root),
'BLIND_STORE_LOCK_INVALID')
t.alike(await treeSnapshot(state.root), before)
await session.close()
})
test('transaction lease transfer is singular and invalidates caller release authority', async t => {
const state = await createManifestedLayout(t)
const session = new BlindStoreSession({ root: state.root })
await session.open()
const original = await acquireBlindStoreSessionTransactionLease(session.lockContext(), state.root)
const transferred = await transferBlindStoreSessionTransactionLease(original, state.root)
await rejectsCode(t,
verifyBlindStoreSessionTransactionLease(original, state.root),
'BLIND_STORE_TRANSACTION_LEASE_INVALID')
original.release()
t.is(await verifyBlindStoreSessionTransactionLease(transferred, state.root), true)
await rejectsCode(t,
transferBlindStoreSessionTransactionLease(original, state.root),
'BLIND_STORE_TRANSACTION_LEASE_INVALID')
await rejectsCode(t,
transferBlindStoreSessionTransactionLease(transferred, state.root),
'BLIND_STORE_TRANSACTION_LEASE_TRANSFER_INVALID')
const competing = await fs.open(state.lock, 'r+')
t.teardown(() => competing.close())
t.is(tryExclusiveFileLock(competing), false)
let closed = false
const closing = session.close().then(() => { closed = true })
await Promise.resolve()
t.is(closed, false)
t.is(tryExclusiveFileLock(competing), false)
transferred.release()
await closing
t.is(closed, true)
t.is(tryExclusiveFileLock(competing), true)
releaseExclusiveFileLock(competing)
})
test('classifier recognizes checkpoint names but rejects every hard-linked store artifact', async t => {
const paired = await createManifestedLayout(t)
const hash = 'a'.repeat(64)
const checkpoint = path.join(paired.control, `checkpoint-${hash}.v1`)
const temporary = path.join(paired.control, `.checkpoint-${hash}.v1.${'b'.repeat(32)}.tmp`)
await fs.writeFile(checkpoint, b4a.alloc(256, 0x71), { mode: 0o600 })
await fs.link(checkpoint, temporary)
t.is((await fs.lstat(checkpoint)).nlink, 2)
const pairedClassification = await classifyBlindStoreRoot(paired.root)
t.is(pairedClassification.kind, BLIND_STORE_ROOT_CLASSIFICATION.LEGACY_AMBIGUOUS)
await t.exception(new BlindStoreSession({ root: paired.root }).open(), /legacy or ambiguous/)
const wrongHash = await createManifestedLayout(t)
const first = path.join(wrongHash.control, `snapshot-${'c'.repeat(64)}.v1`)
const mismatched = path.join(wrongHash.control, `.snapshot-${'d'.repeat(64)}.v1.${'e'.repeat(32)}.tmp`)
await fs.writeFile(first, b4a.alloc(256, 0x72), { mode: 0o600 })
await fs.link(first, mismatched)
const rejected = await classifyBlindStoreRoot(wrongHash.root)
t.is(rejected.kind, BLIND_STORE_ROOT_CLASSIFICATION.LEGACY_AMBIGUOUS)
t.ok(rejected.reason.includes('single-link'))
const external = await createManifestedLayout(t)
const final = path.join(external.control, `checkpoint-${'f'.repeat(64)}.v1`)
const outside = path.join(external.root, 'outside-hardlink')
await fs.writeFile(final, b4a.alloc(256, 0x73), { mode: 0o600 })
await fs.link(final, outside)
const externallyLinked = await classifyBlindStoreRoot(external.root)
t.is(externallyLinked.kind, BLIND_STORE_ROOT_CLASSIFICATION.LEGACY_AMBIGUOUS)
})