OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / packages / core / pear-entry.js

102 lines · javascript ref:
/**
 * p2p-hiverelay — Pear/Bare runtime entry point.
 *
 * Launch with:
 *   pear run .                               (dev, from this directory)
 *   pear run -d pear://<key>                 (prod, after publish)
 *
 * Differences from the Node CLI:
 *   - Uses Pear.config.storage for persistence (app-scoped by Pear)
 *   - No HTTP server, no TUI, no Lightning, no vm sandbox
 *   - Lifecycle is managed by Pear (teardown on app exit, etc.)
 *
 * Flags (parsed with paparam, the Pear-ecosystem CLI lib):
 *   --region|-r <region>     relay region            (default NA)
 *   --port|-p <port>         HTTP API port           (default 9100)
 *   --max-storage <size>     cap, e.g. 50GB          (default 50GB)
 *   --no-updates             disable P2P OTA updates (default: enabled)
 *
 * Parsing is sloppy() so an unexpected Pear-injected arg can never stop the
 * relay from booting. Full pear-runtime OTA is gated on the storage-stack
 * migration (see docs/PEAR-ALIGNMENT.md); the --no-updates switch is wired
 * now so the opt-out is in place when OTA lands.
 */

/* global Pear */

import { command, flag, sloppy } from 'paparam'
import { BareRelay } from './core/relay-node/bare-relay.js'
import b4a from 'b4a'

const DEFAULT_MAX_STORAGE = 50 * 1024 * 1024 * 1024

const argv = Pear.config.args || []
const cmd = command(
  'p2p-hiverelay',
  sloppy({ flags: true, args: true }),
  flag('--region|-r [region]', 'relay region (default NA)'),
  flag('--port|-p [port]', 'HTTP API port (default 9100)'),
  flag('--max-storage [size]', 'max storage, e.g. 50GB (default 50GB)'),
  flag('--no-updates', 'disable P2P over-the-air updates')
)
const program = cmd.parse(argv)
const flags = (program && program.flags) || {}
// paparam collapses "not passed" and "--no-updates" to the same falsy value,
// so detect the explicit opt-out from argv to keep updates enabled by default.
const updatesEnabled = !argv.includes('--no-updates')

console.log()
console.log('  ╱═══════════════════════════════════════════════════════════════════╲')
console.log('      p2p-hiverelay  ·  pear/bare runtime  ·  always-on p2p relay')
console.log('  ╲═══════════════════════════════════════════════════════════════════╱')
console.log()
console.log('  [storage]', Pear.config.storage)
console.log()

const relay = new BareRelay({
  storage: Pear.config.storage,
  regions: [flags.region || 'NA'],
  maxStorageBytes: flags.maxStorage ? parseBytes(flags.maxStorage) : DEFAULT_MAX_STORAGE,
  httpPort: flags.port ? parseInt(flags.port, 10) : 9100
})

relay.on('started', ({ publicKey }) => {
  console.log()
  console.log('  ⬢ public key:', b4a.toString(publicKey, 'hex'))
  console.log()
})

await relay.start()

Pear.teardown(async () => {
  console.log()
  console.log('  [pear] teardown — stopping relay cleanly…')
  await relay.stop()
})

// P2P over-the-air updates (opt-out via --no-updates). The pear-updates module
// is optional and not yet a declared dependency — full OTA is gated on the
// storage-stack migration (Tier 3). Guarded so its absence is a no-op.
if (updatesEnabled) {
  try {
    const { default: updates } = await import('pear-updates')
    updates(() => {
      console.log('  [pear] update available — will apply on next restart')
    })
  } catch (_) { /* pear-updates not available; skip */ }
} else {
  console.log('  [pear] OTA updates disabled (--no-updates)')
}

// ─── Helpers ────────────────────────────────────────────────────────

function parseBytes (str) {
  const n = parseFloat(str)
  const s = String(str).toUpperCase()
  if (s.endsWith('TB')) return n * 1024 ** 4
  if (s.endsWith('GB')) return n * 1024 ** 3
  if (s.endsWith('MB')) return n * 1024 ** 2
  if (s.endsWith('KB')) return n * 1024
  return n
}