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:
+137
-266
@@ -19,8 +19,12 @@ import {
|
||||
CERTRELAY_PORT,
|
||||
CERTRELAY_REMOTE_IP_HEADER,
|
||||
dataDir,
|
||||
pgDataDir,
|
||||
POSTGRES_PORT,
|
||||
NACHO_DEFAULT_IGNORE_NAMES,
|
||||
NACHO_DEFAULT_WORKSHOP_PDF_LINK_TEXT,
|
||||
NACHO_DIR,
|
||||
NACHO_FALLBACK_API_BASE_URL,
|
||||
NACHO_PORT,
|
||||
renderBanner,
|
||||
SPACED_CHAIN,
|
||||
spacedRpcPort,
|
||||
SUBSPACES_DATA_DIR,
|
||||
@@ -44,17 +48,15 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
if (
|
||||
!store?.password ||
|
||||
!store?.btcAuth ||
|
||||
!store?.dbAuth ||
|
||||
!store?.spacedAuth
|
||||
) {
|
||||
// taskSetPassword + taskBtcAuth + taskSeedDb + taskSeedSpacedAuth all seed
|
||||
// these in init; if they aren't populated yet, init hasn't finished — let
|
||||
// StartOS restart us.
|
||||
// taskSetPassword + taskBtcAuth + taskSeedSpacedAuth all seed these in
|
||||
// init; if they aren't populated yet, init hasn't finished — let StartOS
|
||||
// restart us.
|
||||
throw new Error('Spaces store.json is not yet populated.')
|
||||
}
|
||||
|
||||
const { password: APP_PASSWORD, btcAuth, dbAuth, spacedAuth } = store
|
||||
const enableExplorer = store.enableExplorer === true
|
||||
const { password: APP_PASSWORD, btcAuth, spacedAuth } = store
|
||||
const enableSubspaces = store.enableSubspaces === true
|
||||
// subs-prover is independently gated and defaults OFF (null/undefined => false).
|
||||
const enableSubsProver = store.enableSubsProver === true
|
||||
@@ -81,7 +83,6 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
BTC_RPC_PASSWORD: btcAuth.password,
|
||||
APP_USER,
|
||||
APP_PASSWORD,
|
||||
DB_URL: `postgres://${dbAuth.username}:${dbAuth.password}@127.0.0.1:${POSTGRES_PORT}/${dbAuth.database}`,
|
||||
}
|
||||
|
||||
// Certrelay config — user-tunable bits live in store.json (set via the
|
||||
@@ -92,6 +93,8 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
store.certrelayBootstrap ?? CERTRELAY_DEFAULT_BOOTSTRAP
|
||||
const certrelayHealthcheckHandle =
|
||||
store.certrelayHealthcheckHandle ?? CERTRELAY_DEFAULT_HEALTHCHECK_HANDLE
|
||||
const nachoWorkshopPdfLinkText =
|
||||
store.nachoWorkshopPdfLinkText ?? NACHO_DEFAULT_WORKSHOP_PDF_LINK_TEXT
|
||||
|
||||
const mounts = sdk.Mounts.of().mountVolume({
|
||||
volumeId: 'main',
|
||||
@@ -114,36 +117,6 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
'terminal-sub',
|
||||
)
|
||||
|
||||
const postgresSub = await sdk.SubContainer.of(
|
||||
effects,
|
||||
{ imageId: 'postgres' },
|
||||
mounts,
|
||||
'postgres-sub',
|
||||
)
|
||||
|
||||
const postgresEnv = {
|
||||
POSTGRES_USER: dbAuth.username,
|
||||
POSTGRES_PASSWORD: dbAuth.password,
|
||||
POSTGRES_DB: dbAuth.database,
|
||||
PGDATA: pgDataDir,
|
||||
}
|
||||
|
||||
// TODO(prebuilt-indexer / prebuilt-explorer): the Go indexer and SvelteKit
|
||||
// explorer used to be built from source in dedicated builder images
|
||||
// (golang:1.23-alpine, node:20-alpine). Those images + their build chains
|
||||
// were removed to shrink the .s9pk and cut first-enable latency. When the
|
||||
// prebuilt images exist, re-add their SubContainers + env here, e.g.:
|
||||
// const indexerSub = await sdk.SubContainer.of(
|
||||
// effects, { imageId: '<prebuilt-indexer-image>' }, mounts, 'indexer-sub')
|
||||
// const explorerSub = await sdk.SubContainer.of(
|
||||
// effects, { imageId: '<prebuilt-explorer-image>' }, mounts, 'explorer-sub')
|
||||
// const indexerEnv = { POSTGRES_URI, BITCOIN_NODE_URI/USER/PASSWORD,
|
||||
// SPACES_NODE_URI, RPC_USER/PASSWORD, ACTIVATION_BLOCK_HEIGHT,
|
||||
// FAST_SYNC_BLOCK_HEIGHT, UPDATE_DB_INTERVAL, MEMPOOL_CHUNK_SIZE }
|
||||
// const explorerEnv = { DB_URL (?sslmode=disable), PUBLIC_BTC_NETWORK, PORT }
|
||||
// Also add the image ids to startos/manifest/index.ts and re-add the daemon
|
||||
// definitions in the explorer branch below. See git history for prior shapes.
|
||||
|
||||
const subspacesSub = await sdk.SubContainer.of(
|
||||
effects,
|
||||
{ imageId: 'subspaces' },
|
||||
@@ -151,6 +124,19 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
'subspaces-sub',
|
||||
)
|
||||
|
||||
// Optional HTTP basic auth in front of subs (Web UI + API on 7777). Only
|
||||
// injected when both a stored credential pair and the enable toggle are set,
|
||||
// so flipping the toggle off (or clearing creds) leaves subs unauth.
|
||||
// NOTE: env var names match subs/src/main.rs clap #[arg(env=...)]:
|
||||
// SUBS_BASIC_AUTH_USER and SUBS_BASIC_AUTH_PASSWORD.
|
||||
const subsAuthActive = store.subsAuthEnabled === true && store.subsAuth != null
|
||||
const subsAuthEnv = subsAuthActive
|
||||
? {
|
||||
SUBS_BASIC_AUTH_USER: store.subsAuth!.username,
|
||||
SUBS_BASIC_AUTH_PASSWORD: store.subsAuth!.password,
|
||||
}
|
||||
: {}
|
||||
|
||||
// subs reads all of these from the environment (see subs/src/main.rs #[arg(env=...)]).
|
||||
const subsEnv = {
|
||||
SUBS_DATA_DIR: SUBSPACES_DATA_DIR,
|
||||
@@ -163,13 +149,28 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
SUBS_REGISTRY_ENDPOINT: `http://127.0.0.1:${SUBSPACES_REGISTRY_PORT}`,
|
||||
HOME: SUBSPACES_DATA_DIR,
|
||||
RUST_LOG: 'subs=info,subs_prover=info,registry_server=info',
|
||||
...subsAuthEnv,
|
||||
}
|
||||
|
||||
// Optional HTTP basic auth in front of subs-prover (port 8888). Same wiring
|
||||
// pattern as subsAuthEnv above: only injected when both the toggle and
|
||||
// credentials are set. Env var names assume subs-prover reads
|
||||
// SUBS_PROVER_BASIC_AUTH_USER and SUBS_PROVER_BASIC_AUTH_PASSWORD.
|
||||
const subsProverAuthActive =
|
||||
store.subsProverAuthEnabled === true && store.subsProverAuth != null
|
||||
const subsProverAuthEnv = subsProverAuthActive
|
||||
? {
|
||||
SUBS_PROVER_BASIC_AUTH_USER: store.subsProverAuth!.username,
|
||||
SUBS_PROVER_BASIC_AUTH_PASSWORD: store.subsProverAuth!.password,
|
||||
}
|
||||
: {}
|
||||
|
||||
const proverEnv = {
|
||||
SUBS_PROVER_SERVER: '1',
|
||||
SUBS_PROVER_PORT: String(SUBSPACES_PROVER_PORT),
|
||||
HOME: SUBSPACES_PROVER_DIR,
|
||||
RUST_LOG: 'subs_prover=info',
|
||||
...subsProverAuthEnv,
|
||||
}
|
||||
|
||||
const registryEnv = {
|
||||
@@ -201,19 +202,63 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
RUST_LOG: 'info',
|
||||
}
|
||||
|
||||
const nachoSub = await sdk.SubContainer.of(
|
||||
effects,
|
||||
{ imageId: 'nacho' },
|
||||
mounts,
|
||||
'nacho-sub',
|
||||
)
|
||||
|
||||
// Derive EXPO_PUBLIC_API_BASE_URL from the subs-api StartOS interface so it
|
||||
// tracks the actual host:port StartOS exposes (instead of a hardcoded
|
||||
// domain). Prefer the mdns/.local URL — matches how a LAN browser reaches
|
||||
// the device — then fall back to any non-local URL, then loopback.
|
||||
// `.const(effects)` makes this reactive: if the interface address changes
|
||||
// (clearnet enabled, Tor added, etc.), the service restarts and nacho
|
||||
// picks up the new URL.
|
||||
const subsApiIf = await sdk.serviceInterface
|
||||
.getOwn(effects, 'subs-api')
|
||||
.const()
|
||||
const subsApiCandidateUrls = subsApiIf?.addressInfo
|
||||
? [
|
||||
...subsApiIf.addressInfo.filter({ kind: 'mdns' }).format('urlstring'),
|
||||
...subsApiIf.addressInfo.nonLocal.format('urlstring'),
|
||||
]
|
||||
: []
|
||||
const nachoApiBaseUrl =
|
||||
subsApiCandidateUrls[0] ?? NACHO_FALLBACK_API_BASE_URL
|
||||
|
||||
// nacho is an Expo app; the image's entrypoint runs `expo start` on
|
||||
// EXPO_DEV_PORT (8082). EXPO_PUBLIC_* env vars are baked into the web bundle
|
||||
// at server time. The ignore list is NOT passed via env — nacho reads it at
|
||||
// runtime from /data/nacho/ignore_names.txt (managed by the "Configure Nacho"
|
||||
// action). nacho-setup seeds that file with the default if it's missing.
|
||||
const nachoEnv = {
|
||||
EXPO_PUBLIC_API_BASE_URL: nachoApiBaseUrl,
|
||||
EXPO_PUBLIC_WORKSHOP_PDF_LINK_TEXT: nachoWorkshopPdfLinkText,
|
||||
EXPO_DEV_PORT: String(NACHO_PORT),
|
||||
NODE_ENV: 'development',
|
||||
CHOKIDAR_USEPOLLING: '1',
|
||||
HOME: NACHO_DIR,
|
||||
}
|
||||
|
||||
const bashrc = [
|
||||
'export PATH=/root/.cargo/bin:/data/bin:/usr/local/bin:/usr/bin:/bin',
|
||||
"export PS1='spaces:\\w$ '",
|
||||
`alias spaces='space-cli --chain ${SPACED_CHAIN} --rpc-user "$SPACED_RPC_USER" --rpc-password "$SPACED_RPC_PASSWORD" '`,
|
||||
'cat <<EOF',
|
||||
'',
|
||||
'┌─ Spaces ─────────────────────────────────────────────────┐',
|
||||
'│ spaced is managed by StartOS — do not run it manually. │',
|
||||
'│ Use the `spaces` alias to call space-cli, e.g. │',
|
||||
'│ spaces getserverinfo │',
|
||||
'│ `fabric` resolves handles against the local certrelay. │',
|
||||
'│ Docs: https://docs.spacesprotocol.org/ │',
|
||||
'└──────────────────────────────────────────────────────────┘',
|
||||
// Banner rendered via renderBanner() — auto-wraps any line that exceeds the
|
||||
// 96-cell text budget and pads every emitted line to exactly 100 cells, so
|
||||
// the box always renders cleanly in a monospaced terminal regardless of
|
||||
// edits below.
|
||||
...renderBanner('Spaces', [
|
||||
'spaced is managed by StartOS — do not run it manually.',
|
||||
'Use the `spaces` alias to call space-cli, e.g.',
|
||||
' spaces getserverinfo',
|
||||
'`fabric` resolves handles against the local certrelay.',
|
||||
'Docs: https://docs.spacesprotocol.org/',
|
||||
]),
|
||||
'',
|
||||
'EOF',
|
||||
].join('\n')
|
||||
@@ -264,6 +309,48 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
requires: ['certrelay-setup', 'spaced'],
|
||||
})
|
||||
|
||||
// Appends the always-on nacho chain: setup oneshot (mkdir data dir) + nacho
|
||||
// daemon (the Expo dev server on 8082). Runs the image's own entrypoint so
|
||||
// we don't have to reproduce its Expo init logic.
|
||||
const withNacho = (chain: any): any =>
|
||||
chain
|
||||
.addOneshot('nacho-setup', {
|
||||
subcontainer: nachoSub,
|
||||
exec: {
|
||||
command: [
|
||||
'sh',
|
||||
'-c',
|
||||
`set -eu; mkdir -p ${NACHO_DIR}; if [ ! -f ${NACHO_DIR}/ignore_names.txt ]; then printf %s '${NACHO_DEFAULT_IGNORE_NAMES}' > ${NACHO_DIR}/ignore_names.txt; fi; echo "nacho-setup: ensured ${NACHO_DIR} and ignore_names.txt"`,
|
||||
],
|
||||
user: 'root',
|
||||
},
|
||||
requires: [],
|
||||
})
|
||||
.addDaemon('nacho', {
|
||||
subcontainer: nachoSub,
|
||||
exec: {
|
||||
// The image's entrypoint runs Expo on EXPO_DEV_PORT (8082) and
|
||||
// ignores CMD. Invoke it directly.
|
||||
command: ['sh', '/usr/local/bin/entrypoint.sh'],
|
||||
env: nachoEnv,
|
||||
// The image declares no USER; default would be root inside StartOS,
|
||||
// but be explicit so /data writes are unambiguous.
|
||||
user: 'root',
|
||||
},
|
||||
ready: {
|
||||
display: i18n('Nacho'),
|
||||
fn: () =>
|
||||
sdk.healthCheck.checkPortListening(effects, NACHO_PORT, {
|
||||
successMessage: i18n('nacho is ready'),
|
||||
errorMessage: i18n('nacho is not ready'),
|
||||
}),
|
||||
// Expo + Metro bundler can take a while to come up on first start.
|
||||
gracePeriod: 120_000,
|
||||
trigger: sdk.trigger.cooldownTrigger(30_000),
|
||||
},
|
||||
requires: ['nacho-setup'],
|
||||
})
|
||||
|
||||
// Appends the Subspaces chain (fetch → build → wallet ensure → subs-prover →
|
||||
// subs daemon) to any existing chain. `as any` because the chain's TS type
|
||||
// depends on prior IDs and we can't easily express the union here.
|
||||
@@ -366,162 +453,6 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
return c
|
||||
}
|
||||
|
||||
if (!enableExplorer) {
|
||||
// Spaces-only mode: spaced + gotty terminal. No PostgreSQL, no indexer.
|
||||
// User can flip the toggle via the Enable Embedded Explorer action;
|
||||
// store.json.enableExplorer is read via .const() so the merge triggers
|
||||
// an automatic service restart and the full chain takes over.
|
||||
let chain: any = sdk.Daemons.of(effects)
|
||||
.addOneshot('bashrc', {
|
||||
subcontainer: termSub,
|
||||
exec: {
|
||||
command: ['bash', '-c', `cat > /root/.bashrc <<'SPACES_BASHRC_EOF'
|
||||
${bashrc}
|
||||
SPACES_BASHRC_EOF`],
|
||||
user: 'root',
|
||||
},
|
||||
requires: [],
|
||||
})
|
||||
.addDaemon('spaced', {
|
||||
subcontainer: spacedSub,
|
||||
exec: {
|
||||
command: ['/root/.cargo/bin/spaced'],
|
||||
env: spacedEnv,
|
||||
},
|
||||
ready: {
|
||||
display: i18n('Spaced RPC'),
|
||||
fn: () =>
|
||||
sdk.healthCheck.checkPortListening(effects, spacedRpcPort, {
|
||||
successMessage: i18n('spaced RPC is ready'),
|
||||
errorMessage: i18n('spaced RPC is not ready'),
|
||||
}),
|
||||
gracePeriod: 120_000,
|
||||
},
|
||||
requires: [],
|
||||
})
|
||||
.addDaemon('web-terminal', {
|
||||
subcontainer: termSub,
|
||||
exec: {
|
||||
command: [
|
||||
'gotty',
|
||||
'--port',
|
||||
String(uiPort),
|
||||
'-c',
|
||||
`${APP_USER}:${APP_PASSWORD}`,
|
||||
'--permit-write',
|
||||
'--reconnect',
|
||||
'/bin/bash',
|
||||
],
|
||||
env: spacedEnv,
|
||||
},
|
||||
ready: {
|
||||
display: i18n('Web Interface'),
|
||||
fn: () =>
|
||||
sdk.healthCheck.checkPortListening(effects, uiPort, {
|
||||
successMessage: i18n('The web terminal is ready'),
|
||||
errorMessage: i18n('The web terminal is not ready'),
|
||||
}),
|
||||
},
|
||||
requires: ['bashrc'],
|
||||
})
|
||||
.addHealthCheck('sync', {
|
||||
ready: {
|
||||
display: i18n('Spaced Sync'),
|
||||
fn: async () => {
|
||||
try {
|
||||
const probe = await spacedSub.exec(
|
||||
[
|
||||
'/root/.cargo/bin/space-cli',
|
||||
'--chain',
|
||||
SPACED_CHAIN,
|
||||
'--rpc-user',
|
||||
spacedAuth.username,
|
||||
'--rpc-password',
|
||||
spacedAuth.password,
|
||||
'--output-format',
|
||||
'json',
|
||||
'getserverinfo',
|
||||
],
|
||||
{},
|
||||
)
|
||||
const stderr = (probe.stderr ?? '').toString().trim()
|
||||
const stdoutText = (probe.stdout ?? '').toString()
|
||||
const stdoutTrimmed = stdoutText.trim()
|
||||
if (probe.exitCode !== 0) {
|
||||
return {
|
||||
result: 'failure',
|
||||
message: i18n('space-cli exited ${code}: ${error}', {
|
||||
code: String(probe.exitCode),
|
||||
error: (stderr || stdoutTrimmed || '<no output>').slice(0, 200),
|
||||
}),
|
||||
}
|
||||
}
|
||||
let parsed: {
|
||||
ready?: boolean
|
||||
progress?: number
|
||||
chain?: { blocks?: number; headers?: number }
|
||||
}
|
||||
try {
|
||||
parsed = JSON.parse(stdoutText)
|
||||
} catch {
|
||||
return {
|
||||
result: 'failure',
|
||||
message: i18n(
|
||||
'getserverinfo non-JSON. stdout=${stdout} stderr=${stderr}',
|
||||
{
|
||||
stdout: (stdoutTrimmed || '<empty>').slice(0, 160),
|
||||
stderr: (stderr || '<empty>').slice(0, 160),
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
const progress = Math.min(
|
||||
100,
|
||||
Math.max(0, Math.round((parsed.progress ?? 0) * 100)),
|
||||
)
|
||||
const blocks = parsed.chain?.blocks ?? 0
|
||||
const headers = parsed.chain?.headers ?? 0
|
||||
if (parsed.ready === true && progress >= 100) {
|
||||
return {
|
||||
result: 'success',
|
||||
message: i18n(
|
||||
'spaced is fully synced (blocks ${blocks} / headers ${headers}).',
|
||||
{ blocks: String(blocks), headers: String(headers) },
|
||||
),
|
||||
}
|
||||
}
|
||||
return {
|
||||
result: 'loading',
|
||||
message: i18n(
|
||||
'spaced is indexing: ${pct}% (blocks ${blocks} / headers ${headers}).',
|
||||
{
|
||||
pct: String(progress),
|
||||
blocks: String(blocks),
|
||||
headers: String(headers),
|
||||
},
|
||||
),
|
||||
}
|
||||
} catch (e) {
|
||||
return {
|
||||
result: 'failure',
|
||||
message: i18n('Spaced Sync health check crashed: ${error}', {
|
||||
error: (e as Error)?.message ?? String(e),
|
||||
}),
|
||||
}
|
||||
}
|
||||
},
|
||||
gracePeriod: 30_000,
|
||||
trigger: sdk.trigger.cooldownTrigger(30_000),
|
||||
},
|
||||
requires: ['spaced'],
|
||||
})
|
||||
chain = withCertrelay(chain)
|
||||
if (enableSubspaces) chain = withSubspaces(chain)
|
||||
return chain
|
||||
}
|
||||
|
||||
// enableExplorer = true: full chain — spaced + terminal + embedded
|
||||
// PostgreSQL + Go indexer + indexer-sync health check.
|
||||
let chain: any = sdk.Daemons.of(effects)
|
||||
.addOneshot('bashrc', {
|
||||
subcontainer: termSub,
|
||||
@@ -533,40 +464,6 @@ SPACES_BASHRC_EOF`],
|
||||
},
|
||||
requires: [],
|
||||
})
|
||||
.addOneshot('postgres-chown', {
|
||||
subcontainer: postgresSub,
|
||||
exec: {
|
||||
command: [
|
||||
'bash',
|
||||
'-c',
|
||||
`mkdir -p ${pgDataDir} && chown -R postgres:postgres ${pgDataDir} && chmod 700 ${pgDataDir}`,
|
||||
],
|
||||
user: 'root',
|
||||
},
|
||||
requires: [],
|
||||
})
|
||||
.addDaemon('postgres', {
|
||||
subcontainer: postgresSub,
|
||||
exec: {
|
||||
command: [
|
||||
'docker-entrypoint.sh',
|
||||
'postgres',
|
||||
'-c',
|
||||
'listen_addresses=127.0.0.1',
|
||||
],
|
||||
env: postgresEnv,
|
||||
},
|
||||
ready: {
|
||||
display: i18n('Database'),
|
||||
fn: () =>
|
||||
sdk.healthCheck.checkPortListening(effects, POSTGRES_PORT, {
|
||||
successMessage: i18n('postgres is ready'),
|
||||
errorMessage: i18n('postgres is not ready'),
|
||||
}),
|
||||
gracePeriod: 60_000,
|
||||
},
|
||||
requires: ['postgres-chown'],
|
||||
})
|
||||
.addDaemon('spaced', {
|
||||
subcontainer: spacedSub,
|
||||
exec: {
|
||||
@@ -584,22 +481,6 @@ SPACES_BASHRC_EOF`],
|
||||
},
|
||||
requires: [],
|
||||
})
|
||||
// TODO(prebuilt-indexer): the Go indexer build-from-source chain
|
||||
// (indexer-fetch + indexer-build + indexer-cleanup-legacy +
|
||||
// indexer-migrate + `indexer` daemon) was removed to shrink the .s9pk
|
||||
// and cut first-enable latency. Re-add an `indexer` daemon here that runs
|
||||
// the prebuilt `sync` binary from the future indexer image (image id +
|
||||
// binary path TBD) against PostgreSQL + spaced. Env it needs is the old
|
||||
// indexerEnv (POSTGRES_URI, BITCOIN_NODE_*, SPACES_NODE_URI, RPC_USER/
|
||||
// PASSWORD, ACTIVATION_BLOCK_HEIGHT, FAST_SYNC_BLOCK_HEIGHT,
|
||||
// UPDATE_DB_INTERVAL, MEMPOOL_CHUNK_SIZE). A goose migration step must
|
||||
// run before it. See git history (build-from-source) for the prior shape.
|
||||
//
|
||||
// TODO(prebuilt-explorer): the SvelteKit explorer build chain
|
||||
// (explorer-fetch + explorer-install + `explorer-ui` daemon) was removed
|
||||
// for the same reason. Re-add an `explorer-ui` daemon that runs the
|
||||
// prebuilt explorer server from the provided explorer image (image id +
|
||||
// command TBD) on port 3000, reading DB_URL + PUBLIC_BTC_NETWORK.
|
||||
.addDaemon('web-terminal', {
|
||||
subcontainer: termSub,
|
||||
exec: {
|
||||
@@ -645,11 +526,9 @@ SPACES_BASHRC_EOF`],
|
||||
],
|
||||
{},
|
||||
)
|
||||
|
||||
const stderr = (probe.stderr ?? '').toString().trim()
|
||||
const stdoutText = (probe.stdout ?? '').toString()
|
||||
const stdoutTrimmed = stdoutText.trim()
|
||||
|
||||
if (probe.exitCode !== 0) {
|
||||
return {
|
||||
result: 'failure',
|
||||
@@ -659,7 +538,6 @@ SPACES_BASHRC_EOF`],
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
let parsed: {
|
||||
ready?: boolean
|
||||
progress?: number
|
||||
@@ -679,14 +557,12 @@ SPACES_BASHRC_EOF`],
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const progress = Math.min(
|
||||
100,
|
||||
Math.max(0, Math.round((parsed.progress ?? 0) * 100)),
|
||||
)
|
||||
const blocks = parsed.chain?.blocks ?? 0
|
||||
const headers = parsed.chain?.headers ?? 0
|
||||
|
||||
if (parsed.ready === true && progress >= 100) {
|
||||
return {
|
||||
result: 'success',
|
||||
@@ -696,7 +572,6 @@ SPACES_BASHRC_EOF`],
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
result: 'loading',
|
||||
message: i18n(
|
||||
@@ -711,10 +586,9 @@ SPACES_BASHRC_EOF`],
|
||||
} catch (e) {
|
||||
return {
|
||||
result: 'failure',
|
||||
message: i18n(
|
||||
'Spaced Sync health check crashed: ${error}',
|
||||
{ error: (e as Error)?.message ?? String(e) },
|
||||
),
|
||||
message: i18n('Spaced Sync health check crashed: ${error}', {
|
||||
error: (e as Error)?.message ?? String(e),
|
||||
}),
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -723,11 +597,8 @@ SPACES_BASHRC_EOF`],
|
||||
},
|
||||
requires: ['spaced'],
|
||||
})
|
||||
// TODO(prebuilt-indexer): re-add the `indexer-sync` standalone health
|
||||
// check once the prebuilt indexer daemon is wired back in. It queried
|
||||
// MAX(blocks.height) (non-orphan) via psql and compared against spaced's
|
||||
// tip from `space-cli getserverinfo`. See git history for the prior body.
|
||||
chain = withCertrelay(chain)
|
||||
chain = withNacho(chain)
|
||||
if (enableSubspaces) chain = withSubspaces(chain)
|
||||
return chain
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user