Resolve .bso domain names via bitsocial TXT records.
npm install @bitsocial/bso-resolverIf you're wiring this into pkc-js, create resolver instances per provider:
import Pkc from "@pkcprotocol/pkc-js";
import { BsoResolver } from "@bitsocial/bso-resolver";
const chainProviderUrls = [
"https://eth.drpc.org", // see "Recommended chain providers" below
"https://mainnet.infura.io/v3/YOUR_KEY",
"wss://mainnet.infura.io/ws/v3/YOUR_KEY",
];
const resolvers = chainProviderUrls.map((url) => new BsoResolver({
key: `bso-${new URL(url).origin}`,
provider: url,
}));
const pkc = await Pkc({ nameResolvers: resolvers });
// Access a resolver instance later, it should not be needed in general:
const resolver = pkc.clients.nameResolvers["bso-https://eth.drpc.org"].resolver;
// Later, when shutting down:
await pkc.destroy(); // should cascade to resolver.destroy() for each resolverimport { BsoResolver } from "@bitsocial/bso-resolver";
// Create a resolver instance
const resolver = new BsoResolver({ key: "bso-viem", provider: "viem" });
// Check if a name can be resolved
resolver.canResolve({ name: "example.bso" }); // true
resolver.canResolve({ name: "example.com" }); // false
// Resolve a name
const record = await resolver.resolve({ name: "example.bso" });
// BsoResolveResult | undefined
// Resolve using a custom RPC URL
const resolver2 = new BsoResolver({
key: "bso-infura",
provider: "https://mainnet.infura.io/v3/YOUR_KEY",
});
// Optional cancellation with AbortController
const controller = new AbortController();
const record2 = await resolver.resolve({
name: "example.bso",
abortSignal: controller.signal,
});
// Clean up when done
await resolver.destroy();
await resolver2.destroy();The following free public mainnet RPCs have been verified to resolve .bso names reliably (verified 2026-04-19):
https://eth.drpc.orghttps://ethereum.publicnode.comhttps://ethereum-rpc.publicnode.comhttps://rpc.mevblocker.iohttps://1rpc.io/ethhttps://eth-pokt.nodies.app
Creates a resolver instance with a viem client. The client is lazily initialized on the first resolve() call.
key- Unique identifier for this resolver instance (e.g.`bso-${new URL(chainProviderUrl).origin}`—originkeeps the scheme sohttps://…andwss://…to the same host don't collide)provider- Either"viem"for the default public transport, or an HTTP(S) RPC URL or a Websocket RPC URL
const resolver = new BsoResolver({
key: "bso-viem",
provider: "viem",
});Caching is not handled here. This module is a thin network wrapper. Callers (such as
pkc-js) are responsible for caching resolution results. Every call toresolve()makes a fresh RPC request.
Resolves a .bso name by looking up the bitsocial TXT record.
name- The domain name to resolve (e.g."example.bso")abortSignal(optional) - Abort signal used to cancel an in-flight resolve
Returns a BsoResolveResult, or undefined if not found.
undefined specifically means the lookup completed successfully but no bitsocial TXT record exists for name — either the name itself does not exist, or it exists but has no bitsocial text record set. Network/RPC failures and malformed TXT records throw rather than return undefined, so callers that need to distinguish "not found" from "lookup failed" should both try/catch and check for result === undefined.
TXT value format: <ipnsPublicKey>[;key=value;other=value] -> { publicKey, key, other }
interface BsoResolveResult {
/** Required. The IPNS public key from the first segment of the
* `bitsocial` TXT record. */
publicKey: string;
/** Custom metadata from key=value segments in the TXT record.
* Reserved key: publicKey. */
[key: string]: string;
}Note: Each
bitsocialTXT record value points to a single identity — either a community or an author. A future revision of the format may allow both in the same record.
Returns true if the name ends with .bso (case-insensitive).
Aborts in-flight resolves, closes the WebSocket connection (if any), and releases the viem client. Idempotent — safe to call multiple times.
After destroy(), calling resolve() will throw.
This module does not cache. Every call to resolve() makes a fresh RPC request to the configured provider. Caching is the consumer's responsibility — pkc-js (the primary consumer) maintains a persistent name-resolution cache with per-call freshness control and is the right layer for that policy.
Run the full test suite with:
npm testInstall the Playwright browser binaries used by the browser suite with:
npm run test:browser:installRun the browser suite on Playwright's Chromium and Firefox engines with:
npm run test:browserOn Linux CI or fresh machines, Playwright may also require:
npx playwright install --with-deps chromium firefoxThe package publishes separate Node and browser entry points.
- Browser-aware bundlers should resolve the root package import to the browser build automatically.
- Explicit subpaths are also available:
@bitsocial/bso-resolver/browser@bitsocial/bso-resolver/node
Publishing is automated via .github/workflows/publish.yml. When release-it creates a GitHub release (triggered by CI on main), the publish workflow builds and publishes to npm with --provenance.
- Create the
@bitsocialorganization on npmjs.com - Do an initial manual publish:
npm login && npm run build && npm publish --access public - On npmjs.com, go to the package settings → Publishing access → Configure trusted publishing
- Add: owner=
bitsocialhq, repo=bso-resolver, workflow=publish.yml
GPL-2.0-only