OverviewFilesCommitsIssuesDocsReleasesPeers

keet-daemon / scripts / bootstrap-global-registry.js

96 lines · javascript ref:
// bootstrap-global-registry.js — one-shot to create the global registry
// room + readOnly invite that godot-keet and hyper-tps bake into their
// source as GLOBAL_REGISTRY_ROOM_ID + GLOBAL_REGISTRY_INVITE.
//
// PRECONDITIONS:
//   1. No other keet-daemon process running against ~/.config/keet-cli/
//      (RocksDB takes an exclusive lock — stop godot-keet + hyper-tps first)
//   2. ~/.config/keet-cli/ has an active identity with a valid vault
//      (i.e. you've used godot-keet or hyper-tps before on this machine)
//   3. keet-daemon source has `npm install` done + postinstall ran
//
// Run from packages/keet-daemon:
//   $ bare scripts/bootstrap-global-registry.js
//
// Output: two lines to paste into the two source files. Exits cleanly.

import path from 'bare-path'
import os from 'bare-os'
import { startDaemon } from '../index.js'

const TITLE = 'Hyper-Fleeky Global Registry'
const SOCKET = '/tmp/keet-daemon-bootstrap.sock'   // separate from production sock
const STORAGE = path.join(os.homedir(), '.config', 'keet-cli')

async function main () {
  console.log('[bootstrap] storage:', STORAGE)
  console.log('[bootstrap] socket:', SOCKET)
  console.log('[bootstrap] starting source-tree daemon...')
  const daemon = await startDaemon({ storage: STORAGE, socketPath: SOCKET })
  console.log('[bootstrap] daemon ready')

  const router = daemon.router

  try {
    console.log('[bootstrap] creating registry room: ' + TITLE)
    const createRes = await router.handle('room.create', { title: TITLE, roomType: 0 })
    const roomId = (createRes && (createRes.data?.roomId || createRes.data || createRes.roomId)) || null
    if (!roomId || typeof roomId !== 'string') {
      throw new Error('room.create returned no roomId: ' + JSON.stringify(createRes))
    }
    console.log('[bootstrap] roomId: ' + roomId)

    // NOTE: meta.set is daemon-supported per SYSTEMATIC_PLAN.md but if the
    // installed router doesn't expose it yet, this is a no-op marker. The
    // registry pattern works on _hs:room-listing messages regardless of
    // meta.type. Phase B of the plan will surface meta.set once the UI
    // wiring catches up.
    try {
      await router.handle('meta.set', {
        roomId,
        metadata: { type: 'registry', topics: ['public', 'discovery'], discoverable: true }
      })
      console.log('[bootstrap] meta.set OK (type=registry)')
    } catch (err) {
      console.log('[bootstrap] meta.set skipped: ' + err.message)
    }

    console.log('[bootstrap] creating readOnly subscribe invite...')
    const inviteRes = await router.handle('room.invite', { roomId, readOnly: true })
    const token = (inviteRes && (inviteRes.data?.token || inviteRes.token)) || null
    if (!token || typeof token !== 'string') {
      throw new Error('room.invite returned no token: ' + JSON.stringify(inviteRes))
    }

    console.log('[bootstrap] creating WRITE curator invite (save this PRIVATELY)...')
    const curatorRes = await router.handle('room.invite', { roomId, role: 'member' })
    const curatorToken = (curatorRes && (curatorRes.data?.token || curatorRes.token)) || null

    console.log('')
    console.log('====================  PASTE INTO SOURCE  ====================')
    console.log('')
    console.log('  GLOBAL_REGISTRY_ROOM_ID = ' + JSON.stringify(roomId))
    console.log('  GLOBAL_REGISTRY_INVITE  = ' + JSON.stringify(token))
    console.log('')
    console.log('============  KEEP CURATOR INVITE PRIVATE  ============')
    console.log('')
    console.log('  curator (write) invite: ' + curatorToken)
    console.log('')
    console.log('  → hand out narrowly to people allowed to register new rooms')
    console.log('=============================================================')
  } finally {
    console.log('[bootstrap] shutting down daemon...')
    try { await daemon.uds.stop() } catch {}
    try { await daemon.router.core.close() } catch {}
    try { await daemon.core.close() } catch {}
    console.log('[bootstrap] done.')
  }
  Bare.exit(0)
}

main().catch(err => {
  console.error('[bootstrap] FAILED:', err.message)
  if (err.stack) console.error(err.stack)
  Bare.exit(1)
})