OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-proto-peer / scripts / serve-loser-club.mjs

78 lines · javascript ref:
// loser-club hpp — boot + serve the loser-club community DID's repo (fronted by the membrane over its DHT
// discovery topic) and write its app.bsky.feed.generator record. Reopens the StateBaseV3 minted by
// favela-node (key derivation is identical). Run persistently: bare scripts/serve-loser-club.mjs
import '../lib/bare-globals.js'
import process from 'bare-process'
import fs from 'bare-fs'
import b4a from 'b4a'
import { bootSwarmDevice } from '../lib/device-node.js'
import { deviceKeetKeyPair, attestDeviceChain, rootIdentityPublicKey } from 'atproto-room/lib/membership.js'

const HOME = process.env.HOME
const SECRETS = HOME + '/.config/loser-club/secrets.json'
// Store path — env-overridable so a clean relaunch can use a fresh path (the DID + genesis are fixed on
// PLC and repo content is re-authorable). Hard-kills of the bare worker corrupted earlier stores.
const REPO = process.env.LOSER_CLUB_REPO || (HOME + '/.config/loser-club/repo-live')
const MEMBRANE = 'https://pds.7rnx.net'
const s = JSON.parse(fs.readFileSync(SECRETS, 'utf8'))

const founderKp = await deviceKeetKeyPair(s.founderDeviceMnemonic)
const deviceProof = b4a.toString(b4a.from(await attestDeviceChain({ rootMnemonic: s.rootMnemonic, devicePublicKey: founderKp.publicKey })), 'hex')
const rootIdentityKey = b4a.toString(await rootIdentityPublicKey(s.rootMnemonic), 'hex')
const members = [{ key: b4a.toString(founderKp.publicKey, 'hex'), role: 1 }]

console.log('[loser-club] booting hpp for', s.did, '…')
const node = await bootSwarmDevice({
  storage: REPO,
  deviceMnemonic: s.founderDeviceMnemonic,
  did: s.did,
  signingKey: s.signingKey,
  deviceProof,
  rootIdentityKey,
  handle: s.handle,
  pdsUrl: MEMBRANE,
  members,
  swarm: true
})

// Write the feed generator record (the bsky-facing feed) if not already present, then sign.
let existing = null
try { existing = await node.device.repo.getRecord('app.bsky.feed.generator', 'loser-club') } catch {}
if (!existing) {
  await node.device.putRecord('app.bsky.feed.generator', 'loser-club', {
    $type: 'app.bsky.feed.generator',
    did: s.did,
    displayName: 'loser-club',
    description: 'the loser-club — posts from p2p.7rnx.net + ashatron.7rnx.net',
    createdAt: '2026-07-15T12:00:00.000Z'
  })
  const h = await node.device.sign({ force: true })
  console.log('[loser-club] wrote app.bsky.feed.generator/loser-club (rev', h ? h.rev : '?', ')')
} else {
  console.log('[loser-club] feed.generator record already present')
}

// Federation via HEARTBEAT (not restart) — the durable fix. A subscribed non-archival relay only indexes
// LIVE #commits (bridge POSTMORTEM), so emit a fresh marker commit now and every few minutes WHILE running.
// No restart-to-federate → no corestore lock contention. A non-bsky collection so it never pollutes the feed.
const FPING = HOME + '/.config/loser-club/fedping'
let n = 0
try { n = Number(fs.readFileSync(FPING, 'utf8')) || 0 } catch {}
async function heartbeat () {
  try {
    n++
    try { fs.writeFileSync(FPING, String(n)) } catch {}
    await node.device.putRecord('net.favela.federation', 'self', { $type: 'net.favela.federation', seq: n })
    const h = await node.device.sign({ force: true })
    console.log('[loser-club] federation heartbeat #' + n + ' (rev ' + (h ? h.rev : '?') + ')')
  } catch (e) { console.error('[loser-club] heartbeat failed:', e.message) }
}
await heartbeat() // emit one now
const hb = setInterval(heartbeat, 4 * 60 * 1000) // and every 4 min while running
if (hb && typeof hb.unref === 'function') hb.unref()

console.log('[loser-club] serving', s.did, '— fronted + heartbeating for federation. (Ctrl-C to stop)')
const stop = async () => { try { await node.close() } catch {} process.exit(0) }
process.on('SIGINT', stop)
process.on('SIGTERM', stop)