/** * Autopass payload for HDMS drive sharing (invite → pair → mount). * Kept separate from hdms-manager.js for small, Node-testable surface. */ import b4a from 'b4a' import idEnc from 'hypercore-id-encoding' /** Autopass record key: read-only replica offer (public key only). */ export const HDMS_AUTOPASS_SHARE_KEY = 'bare-os-hdms/pending-share' /** Autopass record key: read/write replica (public key + writer secret). Legacy peers ignore this key. */ export const HDMS_AUTOPASS_SHARE_RW_KEY = 'bare-os-hdms/pending-share-rw' /** Expected hex length for a Hypercore Ed25519 secret key (64 bytes). */ export const WRITER_SECRET_HEX_LEN = 128 /** * @param {string} hex * @returns {string} lowercase hex */ export function normalizeWriterSecretHex(hex) { if (typeof hex !== 'string') throw new Error('Invalid writer secret') const h = hex.trim().toLowerCase() if (!/^[0-9a-f]{128}$/.test(h)) { throw new Error('writerSecretHex must be 128 hex chars (64 bytes)') } return h } /** * @param {string} hex * @returns {Uint8Array} */ export function writerSecretBytesFromHex(hex) { return b4a.from(normalizeWriterSecretHex(hex), 'hex') } /** @param {{ remove: (k: string) => Promise, member?: { flushed: () => Promise }, base?: { update?: () => Promise } }} ap */ export async function removeBothHdmsShareKeys(ap) { for (const k of [HDMS_AUTOPASS_SHARE_KEY, HDMS_AUTOPASS_SHARE_RW_KEY]) { try { await ap.remove(k) if (ap.member) await ap.member.flushed() try { await ap.base?.update?.() } catch (_) {} } catch { /* no prior offer */ } } } /** @param {{ value?: unknown } | null | undefined} offer */ export function decodeShareOffer(offer) { if (!offer || offer.value == null) return null const v = offer.value const raw = typeof v === 'string' ? v : b4a.toString(/** @type {Uint8Array} */ (v)) try { const j = JSON.parse(raw) if ( j && typeof j.key === 'string' && typeof j.label === 'string' && j.key.length > 0 ) { return { label: j.label, key: j.key } } } catch { /* ignore */ } return null } /** * RW payload uses `key` = Hyperdrive public key (manifest id, z32) for discovery/open, * and `signerKey` = metadata writer Ed25519 public key (z32) matching `writerSecretHex`. * @param {{ value?: unknown } | null | undefined} offer * @returns {{ label: string, key: string, signerKey: string, writerSecretHex: string } | null} */ export function decodeRwShareOffer(offer) { if (!offer || offer.value == null) return null const v = offer.value const raw = typeof v === 'string' ? v : b4a.toString(/** @type {Uint8Array} */ (v)) try { const j = JSON.parse(raw) if ( j && typeof j.key === 'string' && typeof j.label === 'string' && typeof j.signerKey === 'string' && typeof j.writerSecretHex === 'string' && j.key.length > 0 && j.signerKey.length > 0 ) { idEnc.decode(j.signerKey) idEnc.decode(j.key) return { label: j.label, key: j.key, signerKey: j.signerKey, writerSecretHex: normalizeWriterSecretHex(j.writerSecretHex) } } } catch { /* ignore */ } return null }