OverviewFilesCommitsIssuesDocsReleasesPeers

atproto-bridge / pds / bin / blind-relay.js

59 lines · javascript ref:
// Runnable BLIND-PEERING RELAY — a keyless availability seeder (Model B companion to the bridge).
// Keeps an ALLOWLISTED set of self-custody hyper-proto-peers' cores online so the atproto-bridge can
// front their last-signed state on bsky even when the origin peer (e.g. a laptop) is offline. Holds
// NO keys, PUSHES ONLY the announce + core replication — it cannot sign or forge (see lib/blind-relay.js).
//
// Config (JSON): $RELAY_CONFIG or ~/.config/atproto-blind-relay/config.json
//   { "storage": "~/.config/atproto-blind-relay/data",
//     "seedPeers": [ { "did": "...", "handle": "...", "repoKey": "hex", "firehoseKey": "hex" } ] }
// seedPeers IS the gate — the relay seeds ONLY these, never every peer it can reach.

import '../lib/bare-globals.js'
import process from '#node:process'
import fs from '#node:fs'
import os from '#node:os'
import path from '#node:path'
import { fileURLToPath } from '#node:url'
import { openCorestore } from 'atproto-store/lib/corestore.js'
import { BlindRelay } from '../lib/blind-relay.js'
import { startControl } from '../lib/control.js'

const CONFIG = process.env.RELAY_CONFIG || path.join(os.homedir(), '.config', 'atproto-blind-relay', 'config.json')
const expand = (p) => (p && p.startsWith('~') ? path.join(os.homedir(), p.slice(1)) : p)

let cfg = {}
try { cfg = JSON.parse(fs.readFileSync(CONFIG, 'utf8')) } catch (e) { console.error(JSON.stringify({ msg: 'blind-relay: no/invalid config', path: CONFIG, error: e.message })) }
const storage = expand(cfg.storage) || path.join(os.homedir(), '.config', 'atproto-blind-relay', 'data')
const seedPeers = Array.isArray(cfg.seedPeers) ? cfg.seedPeers : []
if (!seedPeers.length) console.error(JSON.stringify({ msg: 'blind-relay: seedPeers allowlist is EMPTY — nothing to seed', config: CONFIG }))

const corestore = openCorestore(path.join(storage, 'relay-store'))
const relay = await new BlindRelay(corestore, { seedPeers }).ready()

// UDS control plane (pgh readiness + `pgh app status`) — same surface the membrane uses, reporting
// relay.status() instead of the front set. Without it pgh's readiness probe fails (though the relay
// still runs); with it the relay is a first-class pgh-managed app.
let version = null
try { version = JSON.parse(fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'package.json'), 'utf8')).version } catch {}
const uds = expand(cfg.uds) || path.join(storage, 'svc.sock')
const control = await startControl({ uds, name: process.env.PGH_APP_NAME || 'atproto-blind-relay', version, relay })

console.log(JSON.stringify({ msg: 'blind-relay up', config: CONFIG, uds, ...relay.status() }))

// Low-frequency status heartbeat so growing core lengths (fresh sync) are observable in the log.
const beat = setInterval(() => { try { console.log(JSON.stringify({ msg: 'blind-relay status', ...relay.status() })) } catch {} }, 60000)
if (beat && typeof beat.unref === 'function') beat.unref()

let down = false
async function shutdown () {
  if (down) return
  down = true
  clearInterval(beat)
  try { await control.close() } catch {}
  try { await relay.close() } catch {}
  try { await corestore.close() } catch {}
  process.exit(0)
}
process.on('SIGINT', shutdown)
process.on('SIGTERM', shutdown)