Files
peardock/test/session.integration.test.js
T
Raven Scott c818edac9d
Release rolling / release (push) Successful in 12m24s
Secure connections with AutoPass invites, HMAC capabilities, and viewer default.
Default peers are read-only; admin requires seed proof and operators redeem AutoPass packages with signed grants. ACL UI and docs match the new trust model.
2026-07-14 19:38:53 -04:00

117 lines
3.5 KiB
JavaScript

/**
* Full PeerSession + handler registration over HyperDHT.
*/
import test from 'brittle'
import DHT from 'hyperdht'
import b4a from 'b4a'
import createTestnet from 'hyperdht/testnet.js'
import { PeerSession } from '../server/rpc/session.js'
import { registerAllHandlers } from '../server/rpc/register.js'
import { Methods, Roles } from '../shared/protocol.js'
import { initAuthKeys } from '../server/core/auth-keys.js'
import { createAdminProof } from '../shared/crypto-auth.js'
import { getClientIdentity } from '../client/identity.js'
// Allow browse in tests (production default is deny without PEARDOCK_BROWSE_ROOTS)
process.env.PEARDOCK_BROWSE_ROOTS = process.env.PEARDOCK_BROWSE_ROOTS || '/'
const SEED = 'ab'.repeat(32)
test('PearDockConnection + PeerSession ping round-trip', async (t) => {
const testnet = await createTestnet()
const { bootstrap } = testnet
const serverDht = new DHT({ bootstrap })
// Deterministic seed for admin proof verification
const seedBuf = b4a.from(SEED, 'hex')
const keyPair = DHT.keyPair(seedBuf)
initAuthKeys({
seedHex: SEED,
publicKeyHex: b4a.toString(keyPair.publicKey, 'hex'),
})
const server = serverDht.createServer()
server.on('connection', (socket) => {
const session = new PeerSession(socket, {
serverPublicKey: keyPair.publicKey,
})
registerAllHandlers(session)
})
await server.listen(keyPair)
const publicKeyHex = Buffer.from(keyPair.publicKey).toString('hex')
const clientIdentity = getClientIdentity()
const clientDht = new DHT({ bootstrap, keyPair: clientIdentity.keyPair })
const socket = clientDht.connect(keyPair.publicKey)
await new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error('timeout')), 15000)
const done = () => {
clearTimeout(timer)
resolve()
}
socket.once('connect', done)
socket.once('open', done)
socket.once('error', (e) => {
clearTimeout(timer)
reject(e)
})
})
const { default: ProtomuxRPC } = await import('protomux-rpc')
const { PROTOCOL } = await import('../shared/protocol.js')
const { encodings } = await import('../shared/encodings.js')
const rpc = new ProtomuxRPC(socket, {
id: keyPair.publicKey,
protocol: PROTOCOL,
...encodings,
})
// Viewer without proof: ping ok, mutate denied
const hsViewer = await rpc.request(
Methods.handshake,
{ clientName: 'test', clientVersion: '0' },
encodings
)
t.is(hsViewer.role, Roles.viewer)
const pong = await rpc.request(Methods.ping, {}, encodings)
t.ok(pong.success)
t.ok(pong.pong)
try {
await rpc.request(Methods.browseDirectory, { path: '/' }, encodings)
t.fail('viewer should not browse')
} catch (err) {
t.ok(
err.code === 'PERMISSION_DENIED' ||
err.cause?.code === 'PERMISSION_DENIED' ||
/Permission denied/i.test(err.message + (err.cause?.message || ''))
)
}
// Admin via seed HMAC proof
const adminProof = createAdminProof(SEED, {
peerId: clientIdentity.publicKeyHex,
serverPublicKeyHex: publicKeyHex,
})
const hsAdmin = await rpc.request(
Methods.handshake,
{ clientName: 'test', clientVersion: '0', adminProof },
encodings
)
t.is(hsAdmin.role, Roles.admin)
t.is(hsAdmin.auth?.mode, 'seed')
const browse = await rpc.request(Methods.browseDirectory, { path: '/' }, encodings)
t.ok(browse.success)
t.ok(Array.isArray(browse.contents))
socket.destroy()
await server.close()
await serverDht.destroy()
await clientDht.destroy()
await testnet.destroy()
})