import test from 'brittle' import b4a from 'b4a' import crypto from 'hypercore-crypto' import { topicKey, buildMbr, parseMbr, BLOCK_SIZE, TOPIC_STRING, PROTOCOL_APP_CHANNEL_NAME, PROTOCOL_CAP_CHANNEL_NAME, PROTOCOL_CHAT_CHANNEL_NAME, PROTOCOL_MESHDROP_CHANNEL_NAME } from './constants.js' import { BARE_OS_SEED_RPC_METHOD_SHORT_NAMES, BARE_OS_SEED_RPC_METHODS, bareOsIsAllowedSeedRpcMethodShort } from './lib/seed-rpc-methods.js' import { BARE_OS_KERNEL_FEATURES_STOCK_WORD_PRIMARY, BARE_OS_KERNEL_FEATURES_STOCK_WORD_EXTENDED_SEEDING_PLATFORM, BARE_OS_KERNEL_FEATURES_STOCK_WORD_RLIMITS_DELEGATES_SHELL, BARE_OS_KERNEL_FEATURES_STOCK_WORD_OFFLINE_NET_EXTENSIONS, BARE_OS_KERNEL_FEATURES_STOCK_WORD_HOST_TRANSPORT_DELEGATES, BARE_OS_KERNEL_FEATURES_STOCK_WORD_REPLICATION_OPERATOR_SURFACE, BARE_OS_KERNEL_FEATURES_STOCK_WORD_PEAR_CORESTORE_HRPC, BARE_OS_KERNEL_FEATURES_STOCK_WORD_BARE_RUNTIME_PROTO_MUX, BARE_OS_KERNEL_FEATURES_STOCK_WORD_BARE_MODULE_CRYPTO_STAGING, BARE_OS_KERNEL_FEATURES_STOCK_WORD_PEAR_INSPECT_LOGGER_TLS, BARE_OS_KERNEL_FEATURES_STOCK_WORD_HYPERCORE_PACK_HRPC_LIFECYCLE } from './lib/kernel-feature-bits.js' import { BARE_OS_KERNEL_CAPABILITY_WORDS_JSON_KEY, BARE_OS_KERNEL_CAPABILITY_WORD_KEYS, buildKernelCapabilityWordsObject, getKernelCapabilityWords, readKernelCapabilityWord } from './lib/kernel-capability-wire.js' import { msgDataEncoding, msgSearchReqEncoding } from './lib/messages.js' import { msgChatHelloEncoding, msgChatEventEncoding, msgChatJoinEncoding, BARE_OS_CHAT_WIRE_SCHEMA_VERSION } from './lib/chat-messages.js' import { pearMultisigShapeOk } from './lib/bare-os-pear-multisig-shape.js' import { BARE_OS_PROTOMUX_CHANNEL_SCHEMA_VERSION } from './lib/protomux-channel-schema.js' import { BARE_OS_PROTOMUX_CHANNEL_SCHEMA_VERSION as protomuxSchemaFromChannel } from './lib/channel.js' import { setupSeedChannel } from './lib/channel.js' import { bareOsProtMuxChatChannelEnabled } from './lib/chat-env.js' import { setupBareOsChatChannel } from './lib/chat-channel.js' test('topicKey is deterministic 32-byte hash', (t) => { const a = topicKey() const b = topicKey() t.is(a.byteLength, 32) t.alike(a, b) }) test('topicKey differs from other string', (t) => { const k1 = topicKey() const k2 = crypto.hash(b4a.from('other-topic')) t.unlike(k1, k2) }) test('topicKey golden hash fixture for bare-os-v1', (t) => { const k = topicKey() t.is( b4a.toString(k, 'hex'), '9863e44075709205829ba35d40dccf5d8d07e3924bb71a1c941bb5149a0edc4b' ) }) test('buildMbr + parseMbr roundtrip', (t) => { const key = crypto.hash(b4a.from('primary-drive')) const mbr = buildMbr(key) t.is(mbr.byteLength, BLOCK_SIZE) const { keys } = parseMbr(mbr) t.is(keys.length, 1) t.alike(keys[0], key) }) test('buildMbr + parseMbr preserves deterministic failover order', (t) => { const primary = crypto.hash(b4a.from('primary-drive-failover')) const failoverA = crypto.hash(b4a.from('failover-a')) const failoverB = crypto.hash(b4a.from('failover-b')) const mbr = buildMbr(primary, [failoverA, failoverB]) const parsed = parseMbr(mbr) t.is(parsed.keys.length, 3) t.alike(parsed.keys[0], primary) t.alike(parsed.keys[1], failoverA) t.alike(parsed.keys[2], failoverB) const parsed2 = parseMbr(buildMbr(primary, [failoverA, failoverB])) t.alike(parsed2.keys, parsed.keys) }) test('msgDataEncoding roundtrip', (t) => { const m = { index: 0, data: b4a.from('hello') } const pre = { buffer: null, start: 0, end: 0 } msgDataEncoding.preencode(pre, m) const buf = b4a.alloc(pre.end) const state = { buffer: buf, start: 0, end: 0 } msgDataEncoding.encode(state, m) const decState = { buffer: buf, start: 0, end: buf.byteLength } const dec = msgDataEncoding.decode(decState) t.is(dec.index, 0) t.alike(dec.data, m.data) }) test('msgSearchReqEncoding roundtrip', (t) => { const m = { id: 7, query: 'foo' } const pre = { buffer: null, start: 0, end: 0 } msgSearchReqEncoding.preencode(pre, m) const buf = b4a.alloc(pre.end) const state = { buffer: buf, start: 0, end: 0 } msgSearchReqEncoding.encode(state, m) const decState = { buffer: buf, start: 0, end: buf.byteLength } const dec = msgSearchReqEncoding.decode(decState) t.is(dec.id, 7) t.is(dec.query, 'foo') }) test('PROTOCOL_APP_CHANNEL_NAME is bare-os-app-v1', (t) => { t.is(PROTOCOL_APP_CHANNEL_NAME, 'bare-os-app-v1') }) test('PROTOCOL_CAP_CHANNEL_NAME is bare-os-cap-v1', (t) => { t.is(PROTOCOL_CAP_CHANNEL_NAME, 'bare-os-cap-v1') }) test('PROTOCOL_CHAT_CHANNEL_NAME is bare-os-chat-v1', (t) => { t.is(PROTOCOL_CHAT_CHANNEL_NAME, 'bare-os-chat-v1') }) test('protocol channel compatibility fixture (app/cap/chat/meshdrop)', (t) => { const fixture = { app: PROTOCOL_APP_CHANNEL_NAME, cap: PROTOCOL_CAP_CHANNEL_NAME, chat: PROTOCOL_CHAT_CHANNEL_NAME, meshdrop: PROTOCOL_MESHDROP_CHANNEL_NAME } t.alike(fixture, { app: 'bare-os-app-v1', cap: 'bare-os-cap-v1', chat: 'bare-os-chat-v1', meshdrop: 'bare-os-meshdrop-v1' }) t.ok(Object.values(fixture).every((name) => name.startsWith('bare-os-'))) t.ok(Object.values(fixture).every((name) => name.endsWith('-v1'))) }) test('setupSeedChannel batches initial sends with stream cork/uncork when available', (t) => { let corkCalls = 0 let uncorkCalls = 0 let replicateCalls = 0 const primarySends = [] const pairedOpens = [] function makeChannelRecorder() { const msgs = [] return { messages: msgs, addMessage() { const slot = { send: (msg) => primarySends.push(msg) } msgs.push(slot) }, open() { pairedOpens.push('open') } } } const mux = { stream: { cork() { corkCalls++ }, uncork() { uncorkCalls++ } }, createChannel() { return makeChannelRecorder() }, pair(_opts, onpair) { const achan = { messages: [], addMessage() { this.messages.push({ send() {} }) }, open() { pairedOpens.push('app-open') } } onpair(achan) return achan } } const localRAM = new Map([[0, b4a.from('mbr')]]) setupSeedChannel( /** @type {any} */ (mux), localRAM, () => { replicateCalls++ }, { enableBareOsAppChannel: true, enableBareOsChatChannel: false } ) t.is(corkCalls, 1) t.is(uncorkCalls, 1) t.ok(pairedOpens.includes('open')) t.ok(pairedOpens.includes('app-open')) t.is(replicateCalls, 1) t.ok(primarySends.some((v) => v instanceof Uint8Array), 'bitfield sent') }) function roundtrip(enc, m, t) { const pre = { buffer: null, start: 0, end: 0 } enc.preencode(pre, m) const buf = b4a.alloc(pre.end) const state = { buffer: buf, start: 0, end: 0 } enc.encode(state, m) const decState = { buffer: buf, start: 0, end: buf.byteLength } return enc.decode(decState) } test('msgChatHelloEncoding roundtrip', (t) => { const m = { chatSchemaVersion: 1, swarmChatCapability: true } const dec = roundtrip(msgChatHelloEncoding, m, t) t.is(dec.chatSchemaVersion, 1) t.is(dec.swarmChatCapability, true) }) test('msgChatJoinEncoding roundtrip', (t) => { const m = { roomId: 'general', displayName: 'alice', senderPk: b4a.alloc(32, 3), clientInstanceId: b4a.alloc(16, 7) } const dec = roundtrip(msgChatJoinEncoding, m, t) t.is(dec.roomId, 'general') t.is(dec.displayName, 'alice') t.alike(dec.senderPk, m.senderPk) t.alike(dec.clientInstanceId, m.clientInstanceId) }) test('msgChatEventEncoding roundtrip', (t) => { const m = { schemaVersion: BARE_OS_CHAT_WIRE_SCHEMA_VERSION, evtKind: 1, roomId: 'general', evtId: b4a.alloc(16, 9), tsMs: Date.now(), senderPk: b4a.alloc(32, 2), displayName: 'bob', body: 'hello swarm', ttl: 4, sigDetached: null } const dec = roundtrip(msgChatEventEncoding, m, t) t.is(dec.schemaVersion, m.schemaVersion) t.is(dec.body, 'hello swarm') t.is(dec.ttl, 4) t.is(dec.sigDetached, null) }) test('TOPIC_STRING is bare-os-v1', (t) => { t.is(TOPIC_STRING, 'bare-os-v1') }) test('seed RPC method list matches kernel_info surface', (t) => { t.ok(BARE_OS_SEED_RPC_METHOD_SHORT_NAMES.includes('capabilities')) t.ok(BARE_OS_SEED_RPC_METHOD_SHORT_NAMES.includes('replication_queue')) t.is(BARE_OS_SEED_RPC_METHODS.length, BARE_OS_SEED_RPC_METHOD_SHORT_NAMES.length) t.ok(BARE_OS_SEED_RPC_METHODS.every((m) => m.startsWith('bare_os.'))) }) test('stock primary capability word is non-zero', (t) => { t.ok((BARE_OS_KERNEL_FEATURES_STOCK_WORD_PRIMARY >>> 0) > 0) }) test('stock extended seeding platform word is non-zero', (t) => { t.ok((BARE_OS_KERNEL_FEATURES_STOCK_WORD_EXTENDED_SEEDING_PLATFORM >>> 0) > 0) }) test('stock host-transport-delegates word is non-zero', (t) => { t.ok((BARE_OS_KERNEL_FEATURES_STOCK_WORD_HOST_TRANSPORT_DELEGATES >>> 0) > 0) }) test('stock replication-operator-surface word is non-zero', (t) => { t.ok((BARE_OS_KERNEL_FEATURES_STOCK_WORD_REPLICATION_OPERATOR_SURFACE >>> 0) > 0) }) test('stock pear-corestore-hrpc word is non-zero', (t) => { t.ok((BARE_OS_KERNEL_FEATURES_STOCK_WORD_PEAR_CORESTORE_HRPC >>> 0) > 0) }) test('stock bare-runtime-proto-mux word is non-zero', (t) => { t.ok((BARE_OS_KERNEL_FEATURES_STOCK_WORD_BARE_RUNTIME_PROTO_MUX >>> 0) > 0) }) test('stock bare-module-crypto-staging word is non-zero', (t) => { t.ok((BARE_OS_KERNEL_FEATURES_STOCK_WORD_BARE_MODULE_CRYPTO_STAGING >>> 0) > 0) }) test('stock pear-inspect-logger-tls word is non-zero', (t) => { t.ok((BARE_OS_KERNEL_FEATURES_STOCK_WORD_PEAR_INSPECT_LOGGER_TLS >>> 0) > 0) }) test('stock hypercore-pack-hrpc-lifecycle word is non-zero', (t) => { t.ok((BARE_OS_KERNEL_FEATURES_STOCK_WORD_HYPERCORE_PACK_HRPC_LIFECYCLE >>> 0) > 0) }) test('kernelCapabilityWords round-trip fixture covers all 11 words', (t) => { const wordsObj = buildKernelCapabilityWordsObject( BARE_OS_KERNEL_FEATURES_STOCK_WORD_PRIMARY, BARE_OS_KERNEL_FEATURES_STOCK_WORD_EXTENDED_SEEDING_PLATFORM, BARE_OS_KERNEL_FEATURES_STOCK_WORD_RLIMITS_DELEGATES_SHELL, BARE_OS_KERNEL_FEATURES_STOCK_WORD_OFFLINE_NET_EXTENSIONS, BARE_OS_KERNEL_FEATURES_STOCK_WORD_HOST_TRANSPORT_DELEGATES, BARE_OS_KERNEL_FEATURES_STOCK_WORD_REPLICATION_OPERATOR_SURFACE, BARE_OS_KERNEL_FEATURES_STOCK_WORD_PEAR_CORESTORE_HRPC, BARE_OS_KERNEL_FEATURES_STOCK_WORD_BARE_RUNTIME_PROTO_MUX, BARE_OS_KERNEL_FEATURES_STOCK_WORD_BARE_MODULE_CRYPTO_STAGING, BARE_OS_KERNEL_FEATURES_STOCK_WORD_PEAR_INSPECT_LOGGER_TLS, BARE_OS_KERNEL_FEATURES_STOCK_WORD_HYPERCORE_PACK_HRPC_LIFECYCLE ) const cap = { [BARE_OS_KERNEL_CAPABILITY_WORDS_JSON_KEY]: wordsObj } const roundTrip = getKernelCapabilityWords(cap) t.ok(roundTrip) t.is(BARE_OS_KERNEL_CAPABILITY_WORD_KEYS.length, 11) t.alike(Object.keys(roundTrip).sort(), [...BARE_OS_KERNEL_CAPABILITY_WORD_KEYS].sort()) for (const key of BARE_OS_KERNEL_CAPABILITY_WORD_KEYS) { t.is(readKernelCapabilityWord(roundTrip, key), readKernelCapabilityWord(wordsObj, key)) } }) test('seed RPC registry includes HyperDHT / blind-peer operator hints', (t) => { t.ok(BARE_OS_SEED_RPC_METHOD_SHORT_NAMES.includes('dht_bootstrap_hint')) t.ok(BARE_OS_SEED_RPC_METHOD_SHORT_NAMES.includes('blind_peer_topology_v2')) }) test('bareOsIsAllowedSeedRpcMethodShort matches registry gate', (t) => { t.ok(bareOsIsAllowedSeedRpcMethodShort('version')) t.ok(bareOsIsAllowedSeedRpcMethodShort('capabilities')) t.absent(bareOsIsAllowedSeedRpcMethodShort('not_a_registered_bare_os_method')) t.absent(bareOsIsAllowedSeedRpcMethodShort('')) }) test('pearMultisigShapeOk accepts valid multisig metadata', (t) => { t.ok(pearMultisigShapeOk({ signers: ['a', 'b'], quorum: 1 })) t.ok(pearMultisigShapeOk({ signers: ['x'], quorum: 1 })) }) test('pearMultisigShapeOk rejects tampered shapes', (t) => { t.absent(pearMultisigShapeOk(null)) t.absent(pearMultisigShapeOk({ signers: 'x', quorum: 1 })) t.absent(pearMultisigShapeOk({ signers: ['a'], quorum: 2 })) t.absent(pearMultisigShapeOk({ signers: [], quorum: 0 })) }) test('BARE_OS_PROTOMUX_CHANNEL_SCHEMA_VERSION matches channel re-export', (t) => { t.is(BARE_OS_PROTOMUX_CHANNEL_SCHEMA_VERSION, protomuxSchemaFromChannel) t.ok(BARE_OS_PROTOMUX_CHANNEL_SCHEMA_VERSION >= 1) }) test('bareOsProtMuxChatChannelEnabled defaults on; explicit opt-out tokens', (t) => { t.ok(bareOsProtMuxChatChannelEnabled(undefined)) t.ok(bareOsProtMuxChatChannelEnabled({})) t.ok(bareOsProtMuxChatChannelEnabled({ BARE_OS_PROTOMUX_CHAT_CHANNEL: '' })) t.ok(bareOsProtMuxChatChannelEnabled({ BARE_OS_PROTOMUX_CHAT_CHANNEL: 'TRUE' })) t.absent(bareOsProtMuxChatChannelEnabled({ BARE_OS_PROTOMUX_CHAT_CHANNEL: '0' })) t.absent(bareOsProtMuxChatChannelEnabled({ BARE_OS_PROTOMUX_CHAT_CHANNEL: 'false' })) t.absent(bareOsProtMuxChatChannelEnabled({ BARE_OS_PROTOMUX_CHAT_CHANNEL: 'OFF' })) t.absent(bareOsProtMuxChatChannelEnabled({ BARE_OS_PROTOMUX_CHAT_CHANNEL: 'no' })) }) test('setupBareOsChatChannel defers onChannelOpened until fullyOpened resolves', async (t) => { /** @type {((v: boolean) => void) | null} */ let resolveOpened = null const chan = { addMessage() {}, open() {}, fullyOpened() { return new Promise((resolve) => { resolveOpened = resolve }) } } const mux = { createChannel() { return chan } } let called = false setupBareOsChatChannel(/** @type {any} */ (mux), { onChannelOpened() { called = true } }) t.absent(called) resolveOpened(true) await Promise.resolve() await Promise.resolve() t.ok(called) })