OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / packages / core / config / default.js

354 lines · javascript ref:
/**
 * Default configuration for HiveRelay nodes
 */

export default {
  productProfile: 'relay-core',

  // Node identity
  storage: './hiverelay-storage',

  // Network
  // When null, uses HyperDHT defaults (node1-3.hyperdht.org:49737).
  // The bootstrap cache merges cached peers with these nodes so that
  // new nodes can still join the network if the hardcoded bootstrap
  // nodes are unreachable.
  bootstrapNodes: null,
  // Bound the initial DHT announcement flush so the local API/dashboard can
  // come up even when public bootstrap is slow; the joined swarm continues
  // discovery after startup.
  dhtFlushTimeoutMs: 1000,
  maxConnections: 256,

  // Bootstrap cache — persists DHT peers to disk so nodes can rejoin
  // the network even when the default bootstrap servers are down.
  bootstrapCacheEnabled: true,
  bootstrapCachePeers: 50,

  // Seeding
  enableSeeding: true,
  // Legacy ceiling for an unset cap. CLI startup resolves an unset value
  // against exact-filesystem available bytes + reserve; explicit values keep
  // their exact operator designation even when equal to 50 GiB.
  maxStorageBytes: 50 * 1024 * 1024 * 1024,
  announceInterval: 15 * 60 * 1000, // 15 minutes

  // Circuit relay
  enableRelay: true,
  maxRelayBandwidthMbps: 100,
  maxCircuitDuration: 10 * 60 * 1000, // 10 minutes
  maxCircuitBytes: 64 * 1024 * 1024, // 64 MB per circuit
  maxCircuitsPerPeer: 5,
  maxCircuitRateBytesPerSecond: 1024 * 1024, // 1 MiB/s per circuit
  reservationTTL: 60 * 60 * 1000, // 1 hour

  // Proof of relay
  proofMaxLatencyMs: 5000,
  proofChallengeInterval: 5 * 60 * 1000, // 5 minutes
  proofMaxPendingChallenges: 2048,
  proofMaxBatchSize: 64,

  // Reputation
  reputationDecayRate: 0.995, // Daily
  minChallengesForRanking: 10,

  // Metrics & API
  enableMetrics: true,
  enableAPI: true,
  apiPort: 9100,
  apiHost: '0.0.0.0',
  corsOrigins: [],
  strictSeedingPrivacy: true,
  enableDistributedDriveBridge: false,

  // Operator fleet multi-node view (dashboard data-layer v3).
  // Read-only HTTP scrape of public /health + /status on peer base URLs.
  // Never pulls management/Tor-private detail. Empty peers → self card only.
  fleet: {
    includeSelf: true,
    timeoutMs: 4000,
    maxPeers: 32,
    peers: [
      // { id: 'bern', baseUrl: 'http://45.59.123.112:9100', region: 'EU', label: 'bern' }
    ]
  },

  // Management UI authentication.
  //
  // When `exposeToken` is true, the relay embeds its management token
  // into the dashboard/wizard HTML it serves, and the browser UI sends
  // it back as `Authorization: Bearer <token>`. This lets the UI
  // authenticate when it is reached through a trusted, authenticating
  // reverse proxy (e.g. Umbrel's app_proxy) — where the localhost check
  // can never apply, because the proxy connects from its own address.
  //
  // SECURITY: only enable this when the API port is reachable *solely*
  // through such a proxy and is NEVER published to the host/LAN. Any
  // client that can load the page can read the token. Off by default so
  // direct/localhost and reverse-proxy-without-this-mode deployments are
  // entirely unaffected.
  ui: {
    exposeToken: false,
    // Simple mode serves a single-page, appliance-style dashboard
    // (dashboard/blindspark.html) instead of the full multi-tab operator
    // UI — no Docs/GitHub, no payments/calculator/leaderboard tabs, just
    // identity + live status. Enabled for the Blindspark Umbrel packaging
    // via HIVERELAY_UI_SIMPLE; off by default so PC node operators and
    // devs get the full dashboard.
    simple: false
  },

  // Operator subsidy (Phase 1 — accrual + signed claims only; no money
  // moves through the relay). When enabled, the node accrues a capped
  // sats ESTIMATE for blind-peer work and exports an Ed25519-signed
  // claim the subsidy coordinator independently verifies and pays to
  // `payoutDestination` (lightning address, BOLT12 offer, or on-chain
  // address — the operator's own; the relay never holds funds). See
  // incentive/subsidy/index.js + docs/OPERATOR-INCENTIVES-Y1.md Prong 2.
  subsidy: {
    enabled: false,
    rateSatsPerDay: 500, // cap ≈ $180/yr at $100k/BTC; coordinator-gated
    epochMs: 10 * 60 * 1000,
    payoutDestination: null
  },

  // Paid pin-lease (demand side). Off by default — self-host never charges.
  // When enabled, publisher-signed seeds (POST /api/v1/seed, NOT operator
  // POST /seed) must pay a byte-days lease; funds settle to the operator's
  // OWN LN node (zero-custody) and the lease is enforced by the existing
  // custody-expiry sweep. Custody/social-recovery seeds are exempt. See
  // incentive/lease/index.js + the payment-for-seeding design.
  lease: {
    enabled: false,
    satsPerGiBDay: 10, // price = ceil(maxStorageBytes/GiB) * leaseDays * this
    maxSatsPerGiBDay: 1_000_000, // ceiling for the runtime-settable rate
    quoteTtlMs: 60 * 60 * 1000, // 1h to pay; aligned with invoice expiry
    minLeaseDays: 1,
    maxLeaseDays: 3650,
    provider: 'mock' // 'mock' (test/demo) | 'lightning' (operator's own node)
  },

  // Blind custody is the default relay posture. Relays may mirror public
  // content, but custody receipts/proofs must be for encrypted material unless
  // an operator explicitly enables transparent custody.
  custody: {
    enabled: true,
    defaultMode: 'blind',
    allowTransparent: false,
    requireEncryptedPayload: true,
    metadataVisibility: 'redacted',
    redactedCatalog: true,
    proofTarget: 'ciphertext',
    defaultRetainMs: 30 * 24 * 60 * 60 * 1000
  },
  custodyExpiryInterval: 60_000,
  custodyExpiryGraceMs: 0,

  // Storage accounting — always-on paced measurement of real per-drive
  // on-disk bytes (the input for the repair storage guard, eviction
  // ranking, and the dashboard Storage panel). ~batchSize drives are
  // stat()ed every tickMs; defaults walk ~1,200 drives in ~4 minutes.
  storageAccounting: {
    tickMs: 5000,
    batchSize: 25
  },

  // Over-replication eviction (Phase A). OFF by default. Under disk
  // pressure, sheds entries the network holds ≥ floorMargin replicas
  // above their target — never archive-tier (durability ≥ 1), never
  // custody-bound, never younger than minAgeMs, never below the floor
  // (deterministic stagger prevents simultaneous shedding). Bytes are
  // actually reclaimed via drive purge; tombstones stop boot-replay and
  // repair from resurrecting shed entries unless the network falls under
  // floor. See core/relay-node/eviction.js.
  eviction: {
    enabled: false,
    diskPressurePct: 80,
    resumePct: 70,
    floorMargin: 1,
    // 6h (was 3 days). 3 days was too protective: a box ingesting content
    // faster than that could never shed old-enough over-replicated entries and
    // would fill the disk to 100% before disk-pressure eviction could act. The
    // floor + floorMargin still protect availability regardless of age.
    minAgeMs: 6 * 60 * 60 * 1000,
    sweepIntervalMs: 10 * 60 * 1000,
    maxEvictionsPerSweep: 20
  },

  // Seeding registry
  registryKey: null, // null = create new autobase
  registryScanInterval: 60_000, // 1 minute
  registryAutoAccept: true, // Auto-accept matching seed requests (false = approval mode)
  targetReplicaFloor: 2,
  replicationCheckInterval: 60_000,
  replicationRepairEnabled: true,

  // Catalog and gateway trust policy
  gatewayPublicOnlyPrivacyTier: true,
  gatewayPort: null,
  // A public HTTPS edge terminates TLS and talks to this loopback-only data
  // plane. Never expose the plaintext compatibility listener directly.
  gatewayHost: '127.0.0.1',
  gatewayTrustProxy: false,
  gatewayTrustedProxyAddresses: ['127.0.0.1', '::1', '::ffff:127.0.0.1'],
  gatewayRequireForwardedSNI: false,
  gatewayCompatibilityHosts: ['127.0.0.1', 'localhost', '[::1]'],
  gatewayMaxInFlight: 256,
  gatewayMaxInFlightPerApp: 32,
  // V-GW1 finite production policy. Public HTTPS preflight additionally
  // requires these exact values to be present explicitly in config.json so a
  // deployment never relies on constructor fallback behavior.
  gatewayMaxResponseBytes: 64 * 1024 * 1024,
  gatewayMaxTransformBytes: 4 * 1024 * 1024,
  gatewayEgressBytesPerWindow: 256 * 1024 * 1024,
  gatewayEgressWindowMs: 60 * 1000,
  gatewayMaxResponseLifetimeMs: 15 * 60 * 1000,
  // Optional DNS suffix for origin-isolated public app hosts on the dedicated
  // data-plane gateway, e.g. `hive.relay.example`. A 52-char z-base-32 app key
  // becomes `<key>.hive.relay.example`. Disabled until explicitly configured.
  hiveAppHostSuffix: null,
  // Transitional local T1 provenance gate. Only exact 64-hex app keys listed
  // here can use key-derived HTTPS hosts; registry defaults never grant access.
  // The blind-substrate role predicate replaces this scaffold allowlist.
  hiveAppPublicKeys: [],
  // Optional immutable Hyperdrive version per admitted app key. Production
  // public-gateway preflight requires an exact pin for every public origin.
  hiveAppPublicVersions: {},
  requireSignedCatalog: false,
  catalogSignatureMaxAgeMs: 5 * 60 * 1000,
  catalogMaxAppAgeMs: 30 * 24 * 60 * 60 * 1000,

  // Discovery / access mode controls
  discovery: {
    dht: true,
    announce: true,
    mdns: false
  },
  access: {
    open: true,
    allowlist: []
  },
  pairing: {
    enabled: false
  },
  serviceDefaultPeerRole: 'authenticated-user',
  serviceAdminAllowlist: [],
  enableServices: false,
  plugins: [],
  serviceSupervision: {
    enabled: true,
    intervalMs: 30_000,
    maxRestarts: 3
  },
  signedDirectory: {
    enabled: false,
    maxEntryBytes: 8 * 1024,
    ttlSeconds: 24 * 60 * 60,
    maxEntriesPerAuthor: 1,
    publishRatePerMinute: 5,
    maxTotalEntries: 10_000,
    clockSkewToleranceSeconds: 60
  },
  federation: {
    enabled: false,
    followInterval: 5 * 60 * 1000,
    followed: [],
    mirrored: []
  },

  // Metadata-privacy controls. The relay is already blind to CONTENT; these
  // shrink the remaining CONNECTION-metadata surface. All default to the
  // privacy-preserving-but-non-degrading setting.
  privacy: {
    // Redact peer pubkeys in the public /api/peers payload to a per-process
    // salted digest. Authenticated operator views can still request raw keys.
    redactPeerIdentifiers: true,
    // Epoch-rotating discovery topics so a passive DHT observer can't
    // enumerate the relay set indefinitely from one vantage point.
    //   false      — static topic only (unchanged behaviour)
    //   'additive' — announce on BOTH static + rotating topics (back-compat +
    //                partial privacy; recommended transitional setting)
    //   'strict'   — rotating topics only (max unlinkability; legacy peers that
    //                only know the static topic won't discover this relay)
    rotateDiscoveryTopic: false
  },

  // Regions
  regions: [], // Empty = accept from all regions

  // Transports
  transports: {
    udp: true, // Always on (HyperDHT default)
    tor: false,
    i2p: false,
    websocket: false,
    holesail: false
  },
  wsPort: 8765,

  // Holesail API tunnel (for relays behind NAT)
  holesail: {
    host: '127.0.0.1'
  },

  // Tor hidden service
  tor: {
    socksHost: '127.0.0.1',
    socksPort: 9050,
    controlHost: '127.0.0.1',
    controlPort: 9051,
    controlPassword: null,
    cookieAuthFile: '/var/lib/tor/control_auth_cookie',
    // hiverelay.onion/1 (RA-01b) — all default to legacy ephemeral behavior.
    // keyFile enables a persistent v3 identity (e.g. '<storage>/tor/hs-key.blob',
    // chmod 0600, include in the encrypted operator backup).
    keyFile: null,
    // Fail-closed daemon floor, e.g. '0.4.9.5' (0.4.8 leaves the network 2026-09-01).
    minDaemonVersion: null,
    // Multi-port forwarding, e.g. [{ vport: 80, targetHost: '127.0.0.1', targetPort: 9100 }].
    // When null, localPort (apiPort) is exposed on vport 80 as before.
    vports: null,
    // Peer protocol plane (Noise/Protomux: custody, replication, RPC): a
    // loopback-bound listener the onion peer vport forwards to, next to the
    // read plane. host MUST stay loopback — the onion service is the only
    // ingress (location hiding). An explicit `vports` entry for the same
    // vport overrides this mapping.
    peer: { enabled: true, vport: 19737, host: '127.0.0.1', port: 19737 },
    // Restricted discovery: base32 x25519 client pubkeys bound at service
    // creation (Flags=V3Auth). Roster changes rebuild the service in place
    // (same address). Empty = open service. rosterFile persists the roster
    // across restarts (operator-private; never publish or log its contents).
    clientAuthKeys: [],
    rosterFile: null,
    // Signed-advertisement key id for the onion endpoint (rotation overlaps
    // use fresh ids). exposure: 'dual' (public + onion) | 'hidden-only'.
    endpointKeyId: null,
    exposure: 'dual',
    maxStreams: null,
    // PoW defense is daemon-wide SETCONF and requires a pow-enabled tor build
    // plus a HiddenServiceDir-configured service (M0 finding, 2026-07-17).
    pow: { enabled: false, queueRate: 250, queueBurst: 1000 },
    // Health-gated advertisement: descriptor uploads + optional SOCKS self-probe.
    health: { probeIntervalMs: 900000, probeFailLimit: 3, minDescriptorUploads: 2, probeVport: null }
  },

  // Lightning payments
  lightning: {
    enabled: false,
    rpcUrl: 'localhost:10009',
    macaroonPath: null,
    certPath: null,
    network: 'mainnet'
  },

  // Payment settlement
  payment: {
    enabled: false,
    settlementInterval: 24 * 60 * 60 * 1000, // daily
    minSettlementSats: 1000
  },

  // Shutdown
  shutdownTimeoutMs: 10_000
}