/**
* Boot a real room log and read its OrbitDB address — shared by
* test/vectors/generate-orbit-address.mjs (against the TypeScript build) and
* test/orbit-address.test.mjs (against the ClojureScript build), so both
* implementations are driven through the identical sequence.
*
* THE interop invariant (PROTOCOL.md §5): the address is the CID of the manifest
* {name, type, accessController} — a pure function of the secret-derived dbName
* and the hardened access controller's own address. Nothing negotiates it and
* nothing fails loudly if it diverges; a diverged build silently replicates a
* database nobody else is in.
*
* What is real here: @orbitdb/core with entry+payload encryption and the sueta
* identity provider, the hardened IPFS access controller, Helia on the
* IndexedDB blockstore the browser build uses (fake-indexeddb standing in for
* the browser's), and a real libp2p — helia gates on `isLibp2p()`, so a stub is
* not an option.
*
* What is not: the libp2p node is built HERE with websockets only, instead of
* through lib/net's `Net.create`. Net's browser stack adds @libp2p/webrtc and
* window listeners, neither of which exists in node; and the address depends on
* the transport list not at all. Net's own behaviour is pinned separately by the
* membership transcripts in test/machines.test.mjs.
*/
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
/** 32 × 0x01, base64url — the seed rooms-kit's fixtures use, so `myAuthorId`
* is directly comparable across the two repos. */
export const SEED = "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE";
/**
* Case 1 and 2 are the same room opened by an identity-carrying and an
* identity-less member: the manifest hashes only {name, type, accessController},
* never the creator, so they MUST land on one address. Case 3 is the same
* secret under a different app salt: a different room.
*
* Cases 1–3 duplicate rooms-kit/test/vectors/orbit-address.json exactly (same
* secrets, same salts, same seed) — chat's crypto/access derivations are
* byte-identical to that repo's TypeScript ancestor, so the two fixtures must
* agree, and test/orbit-address.test.mjs asserts they do.
*/
export const addressCases = [
{ secret: "A".repeat(43), appSalt: "chat.ardegazu.ro/v2", withIdentity: true },
{ secret: "A".repeat(43), appSalt: "chat.ardegazu.ro/v2", withIdentity: false },
{ secret: "B".repeat(43), appSalt: "board.ardegazu.ro/v1", withIdentity: true },
];
/** A real libp2p with node-capable transports only. */
async function nodeLibp2p() {
const { createLibp2p } = await import("libp2p");
const { webSockets } = await import("@libp2p/websockets");
const { noise } = await import("@chainsafe/libp2p-noise");
const { yamux } = await import("@chainsafe/libp2p-yamux");
const { identify } = await import("@libp2p/identify");
const { gossipsub } = await import("@chainsafe/libp2p-gossipsub");
return createLibp2p({
addresses: { listen: [] },
transports: [webSockets()],
connectionEncrypters: [noise()],
streamMuxers: [yamux()],
services: { identify: identify(), pubsub: gossipsub({ allowPublishToZeroTopicPeers: true }) },
});
}
/**
* @param mods {{RoomCrypto, openHelia, RoomLog}} the implementation under test
* @param c one entry of `addressCases`
*/
export async function openAddressCase(mods, c) {
// RoomLog.open defaults its OrbitDB directory base to the relative literal
// "sueta" (no caller here passes a `directory` hook), so it lands under the
// CWD in node — run from a temp dir so nothing is written into the repo.
const cwd = process.cwd();
process.chdir(mkdtempSync(join(tmpdir(), "sueta-addr-")));
const libp2p = await nodeLibp2p();
try {
const { Identity } = await import("ardegazu-id-kit");
const rc = await mods.RoomCrypto.create(c.secret, c.appSalt, "12D3KooWFixed");
// unique store names per case: two rooms must never share a blockstore
const tag = `${c.appSalt}-${c.withIdentity}`.replace(/[^a-z0-9]+/gi, "-");
const helia = await mods.openHelia(libp2p, { blocks: `b-${tag}`, data: `d-${tag}` });
const identity = c.withIdentity ? await Identity.fromSeed(SEED) : null;
const log = await mods.RoomLog.open(helia, rc, identity, { onEntry() {}, onError() {} });
const result = {
...c,
seed: c.withIdentity ? SEED : null,
dbName: await rc.dbName(),
address: log.address,
// the durable author id: the identity pub for a sueta identity, OrbitDB's
// own per-device id otherwise (non-deterministic, so only its type is kept)
myAuthorId: c.withIdentity ? log.myAuthorId : typeof log.myAuthorId,
};
await log.close();
return result;
} finally {
await libp2p.stop().catch(() => {});
process.chdir(cwd);
}
}