Release v0.1.1:2 with certrelay, nacho, and prebuilt subspaces.
Build Service / BuildPackage (push) Has been cancelled
Build Service / BuildPackage (push) Has been cancelled
Remove the embedded explorer/indexer stack, switch subspaces to prebuilt images, add certrelay and nacho as always-on services, expose Spaces and Subs APIs, and wire optional HTTP basic auth for subs and subs-prover with the correct SUBS_BASIC_AUTH_PASSWORD env var. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+109
-36
@@ -16,42 +16,6 @@ export const BITCOIND_RPC_PORT = 8332
|
||||
|
||||
export const SPACED_CHAIN = 'mainnet'
|
||||
|
||||
export const POSTGRES_PORT = 5432
|
||||
export const POSTGRES_USER = 'postgres'
|
||||
export const POSTGRES_DB = 'spacesprotocol_explorer'
|
||||
export const pgDataDir = '/data/postgres'
|
||||
|
||||
export const INDEXER_REPO = 'spacesprotocol/explorer-indexer'
|
||||
export const INDEXER_GIT_SHA = '00ae1e548734d93f1a8bb9f48d2290f459e12b35'
|
||||
// Bump the suffix to force a full re-fetch + rebuild of /data/explorer-indexer.
|
||||
export const INDEXER_BUILD_ID = `${INDEXER_GIT_SHA}-g3`
|
||||
export const INDEXER_TARBALL_URL = `https://github.com/${INDEXER_REPO}/archive/${INDEXER_GIT_SHA}.tar.gz`
|
||||
export const INDEXER_DIR = '/data/explorer-indexer'
|
||||
export const INDEXER_BIN_DIR = '/data/explorer-indexer/bin'
|
||||
export const INDEXER_SYNC_BIN = '/data/explorer-indexer/bin/sync'
|
||||
export const INDEXER_GOOSE_BIN = '/data/explorer-indexer/bin/goose'
|
||||
export const INDEXER_SCHEMA_DIR = '/data/explorer-indexer/sql/schema'
|
||||
export const INDEXER_MARKER = '/data/explorer-indexer/.installed-sha'
|
||||
|
||||
// Spaces protocol mainnet activation block (per spacesprotocol/explorer-indexer
|
||||
// env.example). Indexer skips fast-sync below FAST_SYNC and starts indexing
|
||||
// spaces data at ACTIVATION.
|
||||
export const INDEXER_ACTIVATION_HEIGHT = '871222'
|
||||
export const INDEXER_FAST_SYNC_HEIGHT = '864000'
|
||||
export const INDEXER_UPDATE_INTERVAL = '5'
|
||||
export const INDEXER_MEMPOOL_CHUNK_SIZE = '200'
|
||||
|
||||
export const EXPLORER_REPO = 'randomlogin/explorer'
|
||||
export const EXPLORER_GIT_SHA = 'c827da1754c3cba5c5507d2c29f21b8fa231344d'
|
||||
// Bump suffix to force re-fetch + rebuild of /data/explorer-ui.
|
||||
export const EXPLORER_BUILD_ID = `${EXPLORER_GIT_SHA}-e1`
|
||||
export const EXPLORER_TARBALL_URL = `https://github.com/${EXPLORER_REPO}/archive/${EXPLORER_GIT_SHA}.tar.gz`
|
||||
export const EXPLORER_DIR = '/data/explorer-ui'
|
||||
export const EXPLORER_BUILD_DIR = '/data/explorer-ui/build'
|
||||
export const EXPLORER_MARKER = '/data/explorer-ui/.installed-sha'
|
||||
export const EXPLORER_PORT = 3000
|
||||
export const EXPLORER_NETWORK = 'mainnet'
|
||||
|
||||
// Subspaces ships as the prebuilt horologger/subs image; binaries live at
|
||||
// these paths inside it. No build-from-source step.
|
||||
export const SUBSPACES_SUBS_BIN = '/usr/local/bin/subs'
|
||||
@@ -88,6 +52,17 @@ export const CERTRELAY_DEFAULT_HEALTHCHECK_HANDLE =
|
||||
'account-digital-useful.genesis@key'
|
||||
export const CERTRELAY_DEFAULT_BOOTSTRAP = false
|
||||
|
||||
// Nacho (Expo dev server, horologger/nacho image). Always on; UI interface on
|
||||
// 8082 (the image's exposed port, matches EXPO_DEV_PORT).
|
||||
export const NACHO_PORT = 8082
|
||||
export const NACHO_DIR = '/data/nacho'
|
||||
// Last-resort fallback if StartOS hasn't yet populated the subs-api interface's
|
||||
// address info at the moment main runs. In normal operation EXPO_PUBLIC_API_BASE_URL
|
||||
// is derived dynamically from the subs-api StartOS interface — see main.ts.
|
||||
export const NACHO_FALLBACK_API_BASE_URL = `http://127.0.0.1:${SUBSPACES_UI_PORT}`
|
||||
export const NACHO_DEFAULT_IGNORE_NAMES = 'fold,swifty'
|
||||
export const NACHO_DEFAULT_WORKSHOP_PDF_LINK_TEXT = ''
|
||||
|
||||
export function randomPassword() {
|
||||
// bitcoind's generate-rpc-dependent action validates the password against
|
||||
// /^[A-Za-z0-9_-]+$/, so the charset must stay in that set.
|
||||
@@ -96,3 +71,101 @@ export function randomPassword() {
|
||||
len: 32,
|
||||
})
|
||||
}
|
||||
|
||||
// Banner box rendering for the gotty terminal MOTD. Canonical width is 100
|
||||
// monospaced cells: 1 cell each for the left/right side borders, plus 1-cell
|
||||
// gutters on each side of text, leaving 96 cells of usable text. All public
|
||||
// helpers here guarantee every line they emit is exactly BANNER_WIDTH cells.
|
||||
//
|
||||
// Use renderBanner() rather than hand-drawing rows so wrapping + padding stay
|
||||
// correct when content changes. See bashrc in main.ts.
|
||||
export const BANNER_WIDTH = 100
|
||||
const BANNER_INNER = BANNER_WIDTH - 2 // 98 cells between the two side borders
|
||||
const BANNER_TEXT = BANNER_INNER - 2 // 96 cells of usable text (1-cell gutters)
|
||||
|
||||
// Cell width assuming all code points are width-1 (true for ASCII, em-dash,
|
||||
// and the box-drawing chars used here). Does NOT handle CJK or emoji — if you
|
||||
// add any, switch to a wcwidth-aware count.
|
||||
function cellWidth(s: string): number {
|
||||
return Array.from(s).length
|
||||
}
|
||||
|
||||
function padRight(s: string, width: number): string {
|
||||
const w = cellWidth(s)
|
||||
return w >= width ? s : s + ' '.repeat(width - w)
|
||||
}
|
||||
|
||||
// Greedy word-wrap on whitespace runs. Lines that already fit pass through
|
||||
// verbatim (preserving leading indent and any internal whitespace). Wrapping
|
||||
// only kicks in when a line is wider than `width`; long single tokens get
|
||||
// hard-broken so a long URL never overflows the box.
|
||||
function wrapText(text: string, width: number): string[] {
|
||||
if (cellWidth(text) <= width) return [text]
|
||||
|
||||
const out: string[] = []
|
||||
const tokens = text.match(/\s+|\S+/g) ?? ['']
|
||||
let line = ''
|
||||
for (const tok of tokens) {
|
||||
const isWs = /^\s/.test(tok)
|
||||
const candidate = line + tok
|
||||
if (cellWidth(candidate) <= width) {
|
||||
line = candidate
|
||||
continue
|
||||
}
|
||||
// Doesn't fit — flush current line and decide what to do with this token.
|
||||
if (line) out.push(line)
|
||||
if (isWs) {
|
||||
// Discard the whitespace run at the wrap point; continuation starts flush.
|
||||
line = ''
|
||||
continue
|
||||
}
|
||||
// Non-whitespace token longer than width: hard-break.
|
||||
let rest = tok
|
||||
while (cellWidth(rest) > width) {
|
||||
const arr = Array.from(rest)
|
||||
out.push(arr.slice(0, width).join(''))
|
||||
rest = arr.slice(width).join('')
|
||||
}
|
||||
line = rest
|
||||
}
|
||||
if (line) out.push(line)
|
||||
if (out.length === 0) out.push('')
|
||||
return out
|
||||
}
|
||||
|
||||
function bannerTop(title?: string): string {
|
||||
if (!title) return '┌' + '─'.repeat(BANNER_INNER) + '┐'
|
||||
const head = '┌─ ' + title + ' '
|
||||
const tail = BANNER_WIDTH - cellWidth(head) - 1
|
||||
return head + '─'.repeat(Math.max(0, tail)) + '┐'
|
||||
}
|
||||
|
||||
function bannerBottom(): string {
|
||||
return '└' + '─'.repeat(BANNER_INNER) + '┘'
|
||||
}
|
||||
|
||||
function bannerBlank(): string {
|
||||
return '│' + ' '.repeat(BANNER_INNER) + '│'
|
||||
}
|
||||
|
||||
function bannerRow(text: string): string[] {
|
||||
return wrapText(text, BANNER_TEXT).map(
|
||||
(line) => '│ ' + padRight(line, BANNER_TEXT) + ' │',
|
||||
)
|
||||
}
|
||||
|
||||
// Render a complete banner: top border, one or more content rows (text wrapped
|
||||
// to fit; '' renders as a blank inner row), and bottom border. Every returned
|
||||
// line is exactly BANNER_WIDTH cells wide.
|
||||
export function renderBanner(
|
||||
title: string | undefined,
|
||||
lines: string[],
|
||||
): string[] {
|
||||
const out: string[] = [bannerTop(title)]
|
||||
for (const l of lines) {
|
||||
if (l === '') out.push(bannerBlank())
|
||||
else out.push(...bannerRow(l))
|
||||
}
|
||||
out.push(bannerBottom())
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user