/**
* bootstrap — constants and helpers for pgh's initial-install flow.
*
* P.10 Phase 0.5i. The bootstrap is the bridge between "user has a
* pgh binary" and "pgh has an identity + a tree store + seeded
* content." It runs once; afterwards pgh is self-hosting.
*
* Bootstrap topic:
* Hardcoded discovery key = blake2b-256("pgh-bootstrap-v1"). Multiple
* seed nodes (tabertron, stortron, any other machine we run) join
* this topic and serve the current pgh-core tree + common dep trees.
* Clients joining the same topic find any online seed via the DHT —
* no single serverKey single-point-of-failure.
*
* The `pgh init` flow:
* 1. Create ~/.config/pgh/ + ~/.local/share/store/ if missing
* 2. Load or create a shared device identity via hyperident (~/.hyperident/)
* 3. If --bootstrap <url> provided: connect to that peer
* else: join the hardcoded topic
* 4. Pull pgh-core tree into store/dependencies/ (when full network
* fetcher exists — currently stubbed; 0.5i ships the local-only path)
* 5. Install pgh-core into store/apps/pgh
*/
import crypto from 'hypercore-crypto'
export const BOOTSTRAP_VERSION = 'pgh-bootstrap-v1'
export const BOOTSTRAP_TOPIC = crypto.hash(Buffer.from(BOOTSTRAP_VERSION))
/**
* NameRegistry autobases a fresh sidecar replicates read-only by default.
*
* Name resolution is SUBSCRIPTION-based, not global-singleton: `resolve-name`
* searches the sidecar's own registry first, then each subscribed replica
* (pgh-sidecar `_resolveNameAcross`). Anyone can run their own namespace
* registry and publish its key; this list is only the default one shipped
* with pgh, so `pgh app install <name>` resolves out of the box instead of
* failing against a fresh peer's own empty registry.
*
* Read-only — subscribing does NOT let this peer claim names here, and it
* does not stop them claiming names in their own registry.
*
* Overridden by an explicit `nameRegistries` in ~/.config/pgh/subscriptions.json
* or PGH_NAME_REGISTRIES; those win outright rather than merging, so a peer
* that wants a different namespace set never has this one forced on it.
*/
export const DEFAULT_NAME_REGISTRIES = [
'3c702c3a6767fa507523caed7b992bc9b0bc38638bd4ca0d3a7f79152e68e9d1'
]
/**
* Resolve a user-supplied bootstrap spec into { kind, value }.
*
* Accepted forms:
* pgh://<publisher>/... — identity-based (future)
* punch://<serverKey>/... — raw serverKey (identity-less)
* topic:<hex> — custom discovery topic
* undefined — default hardcoded topic
*/
export function parseBootstrap (spec) {
if (!spec) {
return { kind: 'topic', value: BOOTSTRAP_TOPIC, label: BOOTSTRAP_VERSION }
}
if (spec.startsWith('punch://')) {
const rest = spec.slice('punch://'.length)
const serverKey = rest.split('/')[0]
return { kind: 'punch', value: serverKey, label: 'punch://' + serverKey.slice(0, 12) + '...' }
}
if (spec.startsWith('pgh://')) {
const rest = spec.slice('pgh://'.length)
const publisher = rest.split('/')[0]
return { kind: 'pgh', value: publisher, label: 'pgh://' + publisher }
}
if (spec.startsWith('topic:')) {
const hex = spec.slice('topic:'.length)
if (hex.length !== 64) throw new Error('bootstrap topic must be 64-char hex')
return { kind: 'topic', value: Buffer.from(hex, 'hex'), label: 'topic:' + hex.slice(0, 12) + '...' }
}
throw new Error('bootstrap spec must be pgh://, punch://, topic:<hex>, or empty (uses default)')
}