/**
* Best-effort fetch of the relay host's /.well-known/ap2p descriptor so the
* operator can rotate the relay's PeerId or TURN endpoint without a client
* release. Baked-in defaults win when the fetch fails (static IPFS hosting
* must keep working offline).
*/
export interface MailboxConfig {
baseUrl: string;
credsUrl: string;
maxMessageKb: number;
}
export interface RelayConfig {
relayMultiaddr: string;
turnCredsUrl: string;
discoveryTopic: string;
/** Offline store-and-forward mailbox; absent ⇒ feature off (PROTOCOL.md §9). */
mailbox?: MailboxConfig;
}
interface Ap2pDescriptor {
relay?: { multiaddrs?: string[]; pubsub_discovery_topic?: string };
endpoints?: { turn_credentials?: string };
mailbox?: { endpoint?: string; mint_endpoint?: string; max_message_kb?: number };
}
export async function resolveRelayConfig(defaults: RelayConfig, timeoutMs = 2000): Promise<RelayConfig> {
const host = defaults.relayMultiaddr.match(/^\/dns4\/([^/]+)\//)?.[1];
if (!host) return defaults; // dev/self-host multiaddr (ip4 etc.) — no descriptor
try {
const res = await fetch(`https://${host}/.well-known/ap2p`, {
signal: AbortSignal.timeout(timeoutMs),
});
if (!res.ok) return defaults;
const d = (await res.json()) as Ap2pDescriptor;
const mailbox: MailboxConfig | undefined =
d.mailbox?.endpoint && d.mailbox?.mint_endpoint
? {
baseUrl: d.mailbox.endpoint,
credsUrl: d.mailbox.mint_endpoint,
maxMessageKb: d.mailbox.max_message_kb ?? 64,
}
: defaults.mailbox;
return {
relayMultiaddr: d.relay?.multiaddrs?.[0] ?? defaults.relayMultiaddr,
turnCredsUrl: d.endpoints?.turn_credentials ?? defaults.turnCredsUrl,
discoveryTopic: d.relay?.pubsub_discovery_topic ?? defaults.discoveryTopic,
...(mailbox ? { mailbox } : {}),
};
} catch {
return defaults;
}
}