OverviewFilesCommitsIssuesDocsReleasesPeers

pearbrowser-desktop / scripts / reseed-drive.js

101 lines · javascript ref:
/**
 * Re-seed an EXISTING Hyperdrive on HiveRelay — no source dir, no re-import.
 *
 * Opens the drive straight from its original publisher corestore, so the
 * 64-hex key (and every hyper:// link to it) is preserved and the drive
 * version is untouched. Then serves it on the swarm and broadcasts a fresh
 * signed seed-request to all connected relays so the fleet replicates +
 * serves it 24/7 again.
 *
 * Why this exists alongside publish-and-pin.js:
 *   - publish-and-pin needs a <source-dir> and re-imports content. When the
 *     only surviving copy of a site is the drive itself, you don't have the
 *     source — this re-seeds from the corestore as-is.
 *   - publish() only forwards {replicas, timeout} to seed(), so its
 *     `ttlDays` is silently dropped and the pin defaults to a 30-DAY TTL.
 *     This passes ttlDays straight to seed() so a 365-day pin actually
 *     sticks. (A 30-day default is the likeliest cause of "my pinned site
 *     vanished after a month".)
 *
 * Usage:
 *   node scripts/reseed-drive.js <publisher-storage-dir> <64-hex-key> \
 *     [--name <app>] [--replicas 5] [--ttlDays 365] [--archive] [--hold 60]
 */
import { HiveRelayClient } from 'p2p-hiverelay-client'
import Hyperdrive from 'hyperdrive'
import b4a from 'b4a'
import { resolve } from 'node:path'
import { existsSync } from 'node:fs'

function parseArgs (argv) {
  const positional = []
  const flags = {}
  for (let i = 0; i < argv.length; i++) {
    const a = argv[i]
    if (a.startsWith('--')) {
      const k = a.slice(2)
      const v = (argv[i + 1] && !argv[i + 1].startsWith('--')) ? argv[++i] : true
      flags[k] = v
    } else positional.push(a)
  }
  return { positional, flags }
}

const { positional, flags } = parseArgs(process.argv.slice(2))
const storage = positional[0]
const keyHex = positional[1]
if (!storage || !keyHex || !/^[0-9a-f]{64}$/i.test(keyHex)) {
  console.error('usage: node scripts/reseed-drive.js <publisher-storage-dir> <64-hex-key> [--name x] [--replicas 5] [--ttlDays 365] [--archive] [--hold 60]')
  process.exit(2)
}
const storageAbs = resolve(storage)
if (!existsSync(storageAbs)) { console.error('storage not found:', storageAbs); process.exit(2) }

const name = flags.name || 'reseed'
const replicas = Number(flags.replicas) || 5
const ttlDays = Number(flags.ttlDays) || 365
const durability = flags.archive ? 'archive' : 0
const holdSecs = Number(flags.hold) || 60

console.log('🍐 Re-seeding existing drive on HiveRelay')
console.log('   storage :', storageAbs)
console.log('   key     :', keyHex)
console.log('   name    :', name, '| replicas:', replicas, '| ttlDays:', ttlDays, '| durability:', durability || 'standard')
console.log()

const client = new HiveRelayClient(storageAbs)
client.on('seeded', ({ key, acceptances }) => console.log('  ✔ seeded', String(key).slice(0, 12) + '…', '—', acceptances, 'acceptances'))
client.on('seed-error', ({ key, error }) => console.error('  ✗ seed-error', String(key).slice(0, 12) + '…', '—', error && error.message))
client.on('seed-cap-warning', (w) => console.warn('  ! cap warning:', w.hint))

await client.start()
console.log('  · client started, discovering relays…')
await new Promise((res) => {
  let n = 0
  client.on('relay-connected', () => { if (++n >= 1) setTimeout(res, 3000) })
  setTimeout(() => { if (n === 0) { console.error('  ✗ no relays within 15s — DHT issue?'); process.exit(2) } res() }, 15000)
})
console.log('  · relays connected:', client.relays?.size ?? '?')

// Open the EXISTING drive from the publisher corestore — no put(), so the
// content and version are preserved exactly. Readying it registers its
// cores with the corestore so swarm replication serves them to relays.
const drive = new Hyperdrive(client.store, b4a.from(keyHex, 'hex'))
await drive.ready()
console.log('  · drive opened — version', drive.version, '· byteLength', drive.byteLength)
client.drives.set(keyHex, drive)
client.swarm.join(drive.discoveryKey, { server: true, client: true })
client.swarm.flush().catch(() => {})

const acceptances = await client.seed(drive.key, { replicas, ttlDays, durability, discoveryKey: drive.discoveryKey })
const n = Array.isArray(acceptances) ? acceptances.length : (acceptances || 0)
console.log()
console.log('✅ seed-request broadcast — relay acceptances:', n)
console.log('   hyper://' + keyHex + '/')
console.log()
console.log('   holding online ' + holdSecs + 's so relays pull the content from us…')
await new Promise((r) => setTimeout(r, holdSecs * 1000))
try { await client.destroy() } catch {}
console.log('  · done.')
process.exit(0)