v0.0.9:2
Build Service / BuildPackage (push) Has been cancelled

This commit is contained in:
2026-05-23 02:37:38 -04:00
parent e41ff31221
commit b308d0ceaa
17 changed files with 911 additions and 493 deletions
+101
View File
@@ -0,0 +1,101 @@
import { storeJson } from '../fileModels/storeJson'
import { i18n } from '../i18n'
import { sdk } from '../sdk'
import {
CERTRELAY_DEFAULT_BOOTSTRAP,
CERTRELAY_DEFAULT_HEALTHCHECK_HANDLE,
CERTRELAY_DEFAULT_SELF_URL,
} from '../utils'
const { InputSpec, Value } = sdk
const inputSpec = InputSpec.of({
certrelaySelfUrl: Value.text({
name: i18n('Certrelay Self URL'),
description: i18n(
'The publicly visible URL clients use to reach this certrelay (CERTRELAY_SELF_URL). Set this to match the external address StartOS exposes for the Certrelay interface (e.g. your .onion or clearnet domain).',
),
warning: null,
footnote: null,
default: CERTRELAY_DEFAULT_SELF_URL,
required: true,
masked: false,
placeholder: 'https://certrelay.example.com',
minLength: 1,
maxLength: null,
}),
certrelayBootstrap: Value.toggle({
name: i18n('Certrelay Bootstrap'),
description: i18n(
'Whether this relay bootstraps from peer relays on startup (CERTRELAY_BOOTSTRAP).',
),
warning: null,
footnote: null,
default: CERTRELAY_DEFAULT_BOOTSTRAP,
}),
certrelayHealthcheckHandle: Value.text({
name: i18n('Certrelay Healthcheck Handle'),
description: i18n(
'A handle the bundled fabric CLI resolves against this relay to verify end-to-end health (CERTRELAY_HEALTHCHECK_HANDLE).',
),
warning: null,
footnote: null,
default: CERTRELAY_DEFAULT_HEALTHCHECK_HANDLE,
required: true,
masked: false,
placeholder: 'account-digital-useful.genesis@key',
minLength: 1,
maxLength: null,
}),
})
export const configureCertrelay = sdk.Action.withInput(
// id
'configure-certrelay',
// metadata
async ({ effects }) => ({
name: i18n('Configure Certrelay'),
description: i18n(
'Set the certrelay self URL, bootstrap toggle, and healthcheck handle. Saving restarts the service so certrelay picks up the new values.',
),
warning: null,
allowedStatuses: 'any',
group: null,
visibility: 'enabled',
}),
// input
inputSpec,
// prefill — load current values from store
async ({ effects }) => {
const store = await storeJson.read().once()
return {
certrelaySelfUrl: store?.certrelaySelfUrl ?? CERTRELAY_DEFAULT_SELF_URL,
certrelayBootstrap:
store?.certrelayBootstrap ?? CERTRELAY_DEFAULT_BOOTSTRAP,
certrelayHealthcheckHandle:
store?.certrelayHealthcheckHandle ??
CERTRELAY_DEFAULT_HEALTHCHECK_HANDLE,
}
},
// run
async ({ effects, input }) => {
await storeJson.merge(effects, {
certrelaySelfUrl: input.certrelaySelfUrl,
certrelayBootstrap: input.certrelayBootstrap,
certrelayHealthcheckHandle: input.certrelayHealthcheckHandle,
})
return {
version: '1',
title: i18n('Success'),
message: i18n(
'Certrelay configuration saved. The service is restarting to apply the new settings.',
),
result: null,
}
},
)
+37
View File
@@ -0,0 +1,37 @@
import { storeJson } from '../fileModels/storeJson'
import { i18n } from '../i18n'
import { sdk } from '../sdk'
export const disableSubspaces = sdk.Action.withoutInput(
// id
'disable-subspaces',
// metadata
async ({ effects }) => {
const current = await storeJson.read((s) => s.enableSubspaces).once()
return {
name: i18n('Disable Subspaces'),
description: i18n(
'Stop the embedded Subspaces service. The compiled binaries and data at /data/subspaces are preserved and will be reused if Subspaces is re-enabled. Service restarts automatically.',
),
warning: null,
allowedStatuses: 'any',
group: null,
visibility: current === true ? 'enabled' : 'hidden',
}
},
// run
async ({ effects }) => {
await storeJson.merge(effects, { enableSubspaces: false })
return {
version: '1',
title: i18n('Success'),
message: i18n(
'Subspaces disabled. The service is restarting; the Subspaces Web UI is no longer listening. Run "Enable Subspaces" to turn it back on.',
),
result: null,
}
},
)
+37
View File
@@ -0,0 +1,37 @@
import { storeJson } from '../fileModels/storeJson'
import { i18n } from '../i18n'
import { sdk } from '../sdk'
export const enableSubspaces = sdk.Action.withoutInput(
// id
'enable-subspaces',
// metadata
async ({ effects }) => {
const current = await storeJson.read((s) => s.enableSubspaces).once()
return {
name: i18n('Enable Subspaces'),
description: i18n(
'Turn on the embedded Subspaces service (off-chain Bitcoin handles). Starts three prebuilt daemons — subs (Web UI), subs-prover, and registry-server — each on its own interface. Service restarts automatically. subs loads the existing `default` spaces wallet; create it first via the Space-CLI Web UI (`spaces createwallet`) if you have not already.',
),
warning: null,
allowedStatuses: 'any',
group: null,
visibility: current === true ? 'hidden' : 'enabled',
}
},
// run
async ({ effects }) => {
await storeJson.merge(effects, { enableSubspaces: true })
return {
version: '1',
title: i18n('Success'),
message: i18n(
'Subspaces enabled. The service is restarting; the Subspaces Web UI (7777), Prover (8888), and Registry (8081) interfaces come up once their daemons are healthy.',
),
result: null,
}
},
)
+8
View File
@@ -1,6 +1,9 @@
import { sdk } from '../sdk'
import { configureCertrelay } from './configureCertrelay'
import { disableExplorer } from './disableExplorer'
import { disableSubspaces } from './disableSubspaces'
import { enableExplorer } from './enableExplorer'
import { enableSubspaces } from './enableSubspaces'
import { exportWallet } from './exportWallet'
import { importWallet } from './importWallet'
import { resetDbState } from './resetDbState'
@@ -8,6 +11,7 @@ import { resetExplorerState } from './resetExplorerState'
import { resetIndexerState } from './resetIndexerState'
import { resetPassword } from './resetPassword'
import { resetSpacedState } from './resetSpacedState'
import { resetSubspacesState } from './resetSubspacesState'
import { setBitcoinRpc } from './setBitcoinRpc'
import { showCredentials } from './showCredentials'
import { showDbCredentials } from './showDbCredentials'
@@ -29,3 +33,7 @@ export const actions = sdk.Actions.of()
.addAction(resetDbState)
.addAction(resetIndexerState)
.addAction(resetExplorerState)
.addAction(enableSubspaces)
.addAction(disableSubspaces)
.addAction(resetSubspacesState)
.addAction(configureCertrelay)
+3 -1
View File
@@ -24,7 +24,9 @@ export const resetExplorerState = sdk.Action.withoutInput(
async ({ effects }) => {
const res = await sdk.SubContainer.withTemp(
effects,
{ imageId: 'explorer-ui' },
// TODO(prebuilt-explorer): switch back to the explorer image once it
// exists. Any image with `rm` works for the wipe; `spaces` is always present.
{ imageId: 'spaces' },
sdk.Mounts.of().mountVolume({
volumeId: 'main',
subpath: null,
+58
View File
@@ -0,0 +1,58 @@
import { i18n } from '../i18n'
import { sdk } from '../sdk'
import { dataDir, SUBSPACES_DIR } from '../utils'
export const resetSubspacesState = sdk.Action.withoutInput(
// id
'reset-subspaces-state',
// metadata
async ({ effects }) => ({
name: i18n('Reset Subspaces State'),
description: i18n(
'Wipe /data/subspaces so the next start re-creates empty Subspaces data, prover, and registry directories.',
),
warning: i18n(
'This deletes all local Subspaces state — any handles/proofs stored on disk will be lost. The binaries ship in the image, so nothing needs to be re-downloaded. The spaces wallet on spaced is preserved.',
),
allowedStatuses: 'any',
group: null,
visibility: 'enabled',
}),
// run
async ({ effects }) => {
const res = await sdk.SubContainer.withTemp(
effects,
{ imageId: 'subspaces' },
sdk.Mounts.of().mountVolume({
volumeId: 'main',
subpath: null,
mountpoint: dataDir,
readonly: false,
}),
'spaces-reset-subspaces',
(subc) => subc.exec(['rm', '-rf', SUBSPACES_DIR], { user: 'root' }),
)
if (res.exitCode !== 0) {
return {
version: '1',
title: i18n('Failure'),
message: i18n('Could not wipe Subspaces state: ${error}', {
error: (res.stderr ?? '').toString() || `exit ${res.exitCode}`,
}),
result: null,
}
}
return {
version: '1',
title: i18n('Success'),
message: i18n(
'Subspaces cache and data have been wiped. Start (or restart) the service to re-fetch and rebuild from GitHub.',
),
result: null,
}
},
)