Update to upstream Spaces 0.3.0 and refresh bundled images (subs v0.1.2c, certrelay v0.2.8, nacho v1.0.0b). Add Create/Show Registry API Keys for registry-server, remove Configure Subspaces and SUBS_PUBLISH_REQUIRE_FINALIZED, and align README/instructions with the new tags. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,65 +0,0 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
|
||||
const { InputSpec, Value } = sdk
|
||||
|
||||
const inputSpec = InputSpec.of({
|
||||
subsPublishRequireFinalized: Value.toggle({
|
||||
name: i18n('Require Finalized Publish'),
|
||||
description: i18n(
|
||||
'When on, subs blocks certificate publish until commitments are finalized (150 confirmations). Maps to SUBS_PUBLISH_REQUIRE_FINALIZED in the subs daemon environment. Off by default.',
|
||||
),
|
||||
warning: null,
|
||||
footnote: null,
|
||||
default: false,
|
||||
}),
|
||||
})
|
||||
|
||||
export const configureSubspaces = sdk.Action.withInput(
|
||||
// id
|
||||
'configure-subspaces',
|
||||
|
||||
// metadata
|
||||
async ({ effects }) => ({
|
||||
name: i18n('Configure Subspaces'),
|
||||
description: i18n(
|
||||
'Set user-tunable subs options. Saving restarts the service so the subs daemon picks up the new environment.',
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
// input
|
||||
inputSpec,
|
||||
|
||||
// prefill — current value (unset/null => false)
|
||||
async ({ effects }) => {
|
||||
const enabled = await storeJson
|
||||
.read((s) => s.subsPublishRequireFinalized)
|
||||
.once()
|
||||
return { subsPublishRequireFinalized: enabled === true }
|
||||
},
|
||||
|
||||
// run
|
||||
async ({ effects, input }) => {
|
||||
await storeJson.merge(effects, {
|
||||
subsPublishRequireFinalized: input.subsPublishRequireFinalized,
|
||||
})
|
||||
|
||||
return {
|
||||
version: '1',
|
||||
title: i18n('Success'),
|
||||
message: input.subsPublishRequireFinalized
|
||||
? i18n(
|
||||
'Subspaces configuration saved. SUBS_PUBLISH_REQUIRE_FINALIZED is enabled; the service is restarting.',
|
||||
)
|
||||
: i18n(
|
||||
'Subspaces configuration saved. SUBS_PUBLISH_REQUIRE_FINALIZED is disabled; the service is restarting.',
|
||||
),
|
||||
result: null,
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,62 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { generateRegistryApiKeys } from '../utils'
|
||||
|
||||
export const createRegistryApiKeys = sdk.Action.withoutInput(
|
||||
// id
|
||||
'create-registry-api-keys',
|
||||
|
||||
// metadata
|
||||
async ({ effects }) => ({
|
||||
name: i18n('Create Registry API Keys'),
|
||||
description: i18n(
|
||||
'Generate (or rotate) REGISTRY_API_KEY and SUBSD_API_KEY for the embedded registry-server. Both are required and must differ. Put SUBSD_API_KEY in subs Settings → Registry Server → Auth Token. REGISTRY_API_KEY is for intake clients calling POST /register. Saving restarts the service so registry-server picks up the new keys.',
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
// run
|
||||
async ({ effects }) => {
|
||||
const { registryApiKey, subsdApiKey } = generateRegistryApiKeys()
|
||||
await storeJson.merge(effects, { registryApiKey, subsdApiKey })
|
||||
|
||||
return {
|
||||
version: '1',
|
||||
title: i18n('Success'),
|
||||
message: i18n(
|
||||
'Registry API keys saved. Paste SUBSD_API_KEY into subs Settings → Registry Server → Auth Token, then click Test. Use REGISTRY_API_KEY as the Bearer token for POST /register clients. The service is restarting so registry-server picks up the new keys.',
|
||||
),
|
||||
result: {
|
||||
type: 'group',
|
||||
value: [
|
||||
{
|
||||
type: 'single',
|
||||
name: i18n('REGISTRY_API_KEY'),
|
||||
description: i18n(
|
||||
'Bearer secret for intake/POST /register clients.',
|
||||
),
|
||||
value: registryApiKey,
|
||||
masked: true,
|
||||
copyable: true,
|
||||
qr: false,
|
||||
},
|
||||
{
|
||||
type: 'single',
|
||||
name: i18n('SUBSD_API_KEY'),
|
||||
description: i18n(
|
||||
'Paste into subs Settings → Registry Server → Auth Token (subs↔registry channel).',
|
||||
),
|
||||
value: subsdApiKey,
|
||||
masked: true,
|
||||
copyable: true,
|
||||
qr: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -1,7 +1,7 @@
|
||||
import { sdk } from '../sdk'
|
||||
import { configureCertrelay } from './configureCertrelay'
|
||||
import { configureNacho } from './configureNacho'
|
||||
import { configureSubspaces } from './configureSubspaces'
|
||||
import { createRegistryApiKeys } from './createRegistryApiKeys'
|
||||
import { disableSubsAuth } from './disableSubsAuth'
|
||||
import { disableSubsProverAuth } from './disableSubsProverAuth'
|
||||
import { disableSubspaces } from './disableSubspaces'
|
||||
@@ -19,6 +19,7 @@ import { setSubsProver } from './setSubsProver'
|
||||
import { setSubsProverCredentials } from './setSubsProverCredentials'
|
||||
import { showCredentials } from './showCredentials'
|
||||
import { showPassword } from './showPassword'
|
||||
import { showRegistryApiKeys } from './showRegistryApiKeys'
|
||||
import { showSpacedCredentials } from './showSpacedCredentials'
|
||||
import { showSubsCredentials } from './showSubsCredentials'
|
||||
import { showSubsProverCredentials } from './showSubsProverCredentials'
|
||||
@@ -49,5 +50,6 @@ export const actions = sdk.Actions.of()
|
||||
.addAction(resetSubspacesState)
|
||||
.addAction(configureCertrelay)
|
||||
.addAction(configureNacho)
|
||||
.addAction(configureSubspaces)
|
||||
.addAction(createRegistryApiKeys)
|
||||
.addAction(showRegistryApiKeys)
|
||||
.addAction(uploadSupportPdf)
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
|
||||
export const showRegistryApiKeys = sdk.Action.withoutInput(
|
||||
// id
|
||||
'show-registry-api-keys',
|
||||
|
||||
// metadata
|
||||
async ({ effects }) => ({
|
||||
name: i18n('Show Registry API Keys'),
|
||||
description: i18n(
|
||||
'Display the stored REGISTRY_API_KEY and SUBSD_API_KEY used by registry-server. Returns blanks if Create Registry API Keys has not been run yet.',
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
// run
|
||||
async ({ effects }) => {
|
||||
const [registryApiKey, subsdApiKey] = await Promise.all([
|
||||
storeJson.read((s) => s.registryApiKey).once(),
|
||||
storeJson.read((s) => s.subsdApiKey).once(),
|
||||
])
|
||||
|
||||
const ready =
|
||||
!!registryApiKey &&
|
||||
!!subsdApiKey &&
|
||||
registryApiKey.length > 0 &&
|
||||
subsdApiKey.length > 0 &&
|
||||
registryApiKey !== subsdApiKey
|
||||
|
||||
return {
|
||||
version: '1',
|
||||
title: i18n('Show Registry API Keys'),
|
||||
message: ready
|
||||
? i18n(
|
||||
'Paste SUBSD_API_KEY into subs Settings → Registry Server → Auth Token. Use REGISTRY_API_KEY as the Bearer token for POST /register clients.',
|
||||
)
|
||||
: i18n(
|
||||
'No valid registry API keys are stored yet. Run Create Registry API Keys first (both keys must be non-empty and different).',
|
||||
),
|
||||
result: {
|
||||
type: 'group',
|
||||
value: [
|
||||
{
|
||||
type: 'single',
|
||||
name: i18n('REGISTRY_API_KEY'),
|
||||
description: i18n(
|
||||
'Bearer secret for intake/POST /register clients.',
|
||||
),
|
||||
value: registryApiKey ?? '',
|
||||
masked: true,
|
||||
copyable: true,
|
||||
qr: false,
|
||||
},
|
||||
{
|
||||
type: 'single',
|
||||
name: i18n('SUBSD_API_KEY'),
|
||||
description: i18n(
|
||||
'Paste into subs Settings → Registry Server → Auth Token (subs↔registry channel).',
|
||||
),
|
||||
value: subsdApiKey ?? '',
|
||||
masked: true,
|
||||
copyable: true,
|
||||
qr: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -39,7 +39,12 @@ const shape = z.object({
|
||||
certrelayBootstrap: z.boolean().nullable().catch(null),
|
||||
certrelayHealthcheckHandle: z.string().nullable().catch(null),
|
||||
nachoWorkshopPdfLinkText: z.string().nullable().catch(null),
|
||||
subsPublishRequireFinalized: z.boolean().nullable().catch(null),
|
||||
// Bearer secrets for the embedded registry-server (examples/registry-server).
|
||||
// REGISTRY_API_KEY guards POST /register; SUBSD_API_KEY is the Auth Token
|
||||
// for the subs↔registry channel (Settings → Registry Server → Auth Token).
|
||||
// Both are required and must differ or registry-server refuses to start.
|
||||
registryApiKey: z.string().nullable().catch(null),
|
||||
subsdApiKey: z.string().nullable().catch(null),
|
||||
})
|
||||
|
||||
export const storeJson = FileHelper.json(
|
||||
|
||||
@@ -289,16 +289,25 @@ const dict = {
|
||||
205,
|
||||
'Subspaces Prover Auth credentials saved. Auth is currently DISABLED — enable it with "Enable Subspaces Prover Auth" to enforce these credentials.':
|
||||
206,
|
||||
'Configure Subspaces': 207,
|
||||
'Set user-tunable subs options. Saving restarts the service so the subs daemon picks up the new environment.':
|
||||
208,
|
||||
'Require Finalized Publish': 209,
|
||||
'When on, subs blocks certificate publish until commitments are finalized (150 confirmations). Maps to SUBS_PUBLISH_REQUIRE_FINALIZED in the subs daemon environment. Off by default.':
|
||||
210,
|
||||
'Subspaces configuration saved. SUBS_PUBLISH_REQUIRE_FINALIZED is enabled; the service is restarting.':
|
||||
211,
|
||||
'Subspaces configuration saved. SUBS_PUBLISH_REQUIRE_FINALIZED is disabled; the service is restarting.':
|
||||
212,
|
||||
'Create Registry API Keys': 213,
|
||||
'Generate (or rotate) REGISTRY_API_KEY and SUBSD_API_KEY for the embedded registry-server. Both are required and must differ. Put SUBSD_API_KEY in subs Settings → Registry Server → Auth Token. REGISTRY_API_KEY is for intake clients calling POST /register. Saving restarts the service so registry-server picks up the new keys.':
|
||||
214,
|
||||
'Registry API keys saved. Paste SUBSD_API_KEY into subs Settings → Registry Server → Auth Token, then click Test. Use REGISTRY_API_KEY as the Bearer token for POST /register clients. The service is restarting so registry-server picks up the new keys.':
|
||||
215,
|
||||
REGISTRY_API_KEY: 216,
|
||||
SUBSD_API_KEY: 217,
|
||||
'Bearer secret for intake/POST /register clients.': 218,
|
||||
'Paste into subs Settings → Registry Server → Auth Token (subs↔registry channel).':
|
||||
219,
|
||||
'Show Registry API Keys': 220,
|
||||
'Display the stored REGISTRY_API_KEY and SUBSD_API_KEY used by registry-server. Returns blanks if Create Registry API Keys has not been run yet.':
|
||||
221,
|
||||
'Paste SUBSD_API_KEY into subs Settings → Registry Server → Auth Token. Use REGISTRY_API_KEY as the Bearer token for POST /register clients.':
|
||||
222,
|
||||
'No valid registry API keys are stored yet. Run Create Registry API Keys first (both keys must be non-empty and different).':
|
||||
223,
|
||||
'Subspaces registry-server requires REGISTRY_API_KEY and SUBSD_API_KEY before it can start':
|
||||
224,
|
||||
} as const
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,7 @@ import { setInterfaces } from '../interfaces'
|
||||
import { sdk } from '../sdk'
|
||||
import { versionGraph } from '../versions'
|
||||
import { taskBtcAuth } from './taskBtcAuth'
|
||||
import { taskRegistryApiKeys } from './taskRegistryApiKeys'
|
||||
import { taskSeedCertrelay } from './taskSeedCertrelay'
|
||||
import { taskSeedEnableSubspaces } from './taskSeedEnableSubspaces'
|
||||
import { taskSeedNacho } from './taskSeedNacho'
|
||||
@@ -20,6 +21,7 @@ export const init = sdk.setupInit(
|
||||
taskBtcAuth,
|
||||
taskSeedSpacedAuth,
|
||||
taskSeedEnableSubspaces,
|
||||
taskRegistryApiKeys,
|
||||
taskSeedCertrelay,
|
||||
taskSeedNacho,
|
||||
taskSetPassword,
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { createRegistryApiKeys } from '../actions/createRegistryApiKeys'
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
|
||||
// registry-server refuses to start without both keys (and they must differ).
|
||||
// Only prompt when Subspaces is enabled so installs that leave it off are not
|
||||
// blocked; enabling Subspaces without keys surfaces this critical task.
|
||||
export const taskRegistryApiKeys = sdk.setupOnInit(async (effects) => {
|
||||
const store = await storeJson.read().once()
|
||||
if (store?.enableSubspaces !== true) return
|
||||
|
||||
const registryApiKey = store?.registryApiKey
|
||||
const subsdApiKey = store?.subsdApiKey
|
||||
const ready =
|
||||
!!registryApiKey &&
|
||||
!!subsdApiKey &&
|
||||
registryApiKey.length > 0 &&
|
||||
subsdApiKey.length > 0 &&
|
||||
registryApiKey !== subsdApiKey
|
||||
if (ready) return
|
||||
|
||||
await sdk.action.createOwnTask(effects, createRegistryApiKeys, 'critical', {
|
||||
reason: i18n(
|
||||
'Subspaces registry-server requires REGISTRY_API_KEY and SUBSD_API_KEY before it can start',
|
||||
),
|
||||
})
|
||||
})
|
||||
+19
-2
@@ -149,8 +149,6 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
SUBS_SPACED_RPC_PASSWORD: spacedAuth.password,
|
||||
SUBS_PROVER_ENDPOINT: `http://127.0.0.1:${SUBSPACES_PROVER_PORT}`,
|
||||
SUBS_REGISTRY_ENDPOINT: `http://127.0.0.1:${SUBSPACES_REGISTRY_PORT}`,
|
||||
SUBS_PUBLISH_REQUIRE_FINALIZED:
|
||||
store.subsPublishRequireFinalized === true ? 'true' : 'false',
|
||||
HOME: SUBSPACES_DATA_DIR,
|
||||
RUST_LOG: 'subs=info,subs_prover=info,registry_server=info',
|
||||
...subsAuthEnv,
|
||||
@@ -181,6 +179,9 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
REGISTRY_SERVER_PORT: String(SUBSPACES_REGISTRY_PORT),
|
||||
HOME: SUBSPACES_REGISTRY_DIR,
|
||||
RUST_LOG: 'registry_server=info',
|
||||
// examples/registry-server requires both at boot; they must differ.
|
||||
REGISTRY_API_KEY: store.registryApiKey ?? '',
|
||||
SUBSD_API_KEY: store.subsdApiKey ?? '',
|
||||
}
|
||||
|
||||
const certrelaySub = sdk.SubContainer.of(
|
||||
@@ -371,6 +372,22 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
// 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.
|
||||
const withSubspaces = (chain: any): any => {
|
||||
const registryApiKey = store.registryApiKey
|
||||
const subsdApiKey = store.subsdApiKey
|
||||
if (
|
||||
!registryApiKey ||
|
||||
!subsdApiKey ||
|
||||
registryApiKey.length === 0 ||
|
||||
subsdApiKey.length === 0 ||
|
||||
registryApiKey === subsdApiKey
|
||||
) {
|
||||
// taskRegistryApiKeys surfaces Create Registry API Keys when Subspaces is
|
||||
// on without valid keys; refuse to start the registry half-configured.
|
||||
throw new Error(
|
||||
'Registry API keys are not configured. Run Create Registry API Keys.',
|
||||
)
|
||||
}
|
||||
|
||||
let c = chain
|
||||
.addOneshot('subspaces-dirs', {
|
||||
subcontainer: subspacesSub,
|
||||
|
||||
@@ -14,19 +14,19 @@ export const manifest = setupManifest({
|
||||
volumes: ['main'],
|
||||
images: {
|
||||
spaces: {
|
||||
source: { dockerTag: 'horologger/spaces:v0.2.1a' },
|
||||
source: { dockerTag: 'horologger/spaces:v0.3.0' },
|
||||
arch: ['x86_64', 'aarch64'],
|
||||
},
|
||||
subspaces: {
|
||||
source: { dockerTag: 'horologger/subs:v0.1.2b' },
|
||||
source: { dockerTag: 'horologger/subs:v0.1.2c' },
|
||||
arch: ['x86_64', 'aarch64'],
|
||||
},
|
||||
certrelay: {
|
||||
source: { dockerTag: 'horologger/certrelay:v0.2.7b' },
|
||||
source: { dockerTag: 'horologger/certrelay:v0.2.8' },
|
||||
arch: ['x86_64', 'aarch64'],
|
||||
},
|
||||
nacho: {
|
||||
source: { dockerTag: 'horologger/nacho:v1.0.0' },
|
||||
source: { dockerTag: 'horologger/nacho:v1.0.0b' },
|
||||
arch: ['x86_64', 'aarch64'],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -74,6 +74,27 @@ export function randomPassword() {
|
||||
})
|
||||
}
|
||||
|
||||
// Matches upstream docs: `openssl rand -hex 32` → 64 lowercase hex chars.
|
||||
export function randomApiKey() {
|
||||
return utils.getDefaultString({
|
||||
charset: 'a-f,0-9',
|
||||
len: 64,
|
||||
})
|
||||
}
|
||||
|
||||
// registry-server refuses to start unless both keys are non-empty and different.
|
||||
export function generateRegistryApiKeys(): {
|
||||
registryApiKey: string
|
||||
subsdApiKey: string
|
||||
} {
|
||||
const registryApiKey = randomApiKey()
|
||||
let subsdApiKey = randomApiKey()
|
||||
while (subsdApiKey === registryApiKey) {
|
||||
subsdApiKey = randomApiKey()
|
||||
}
|
||||
return { registryApiKey, subsdApiKey }
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { IMPOSSIBLE, VersionInfo } from '@start9labs/start-sdk'
|
||||
|
||||
export const current = VersionInfo.of({
|
||||
version: '0.2.1:5',
|
||||
version: '0.3.0:0',
|
||||
releaseNotes: {
|
||||
en_US: `Bump Subspaces to \`horologger/subs:v0.1.2b\` and Certrelay to \`horologger/certrelay:v0.2.7b\` (spaces \`v0.2.1a\` and nacho \`v1.0.0\` unchanged).
|
||||
en_US: `Update to upstream Spaces **0.3.0** (\`horologger/spaces:v0.3.0\`). Also bundles \`subs:v0.1.2c\`, \`certrelay:v0.2.8\`, and \`nacho:v1.0.0b\`.
|
||||
|
||||
**Wrapper**
|
||||
|
||||
- Add **Create Registry API Keys** / **Show Registry API Keys** for the embedded \`registry-server\` (\`REGISTRY_API_KEY\` + \`SUBSD_API_KEY\`; required and must differ)
|
||||
- Remove **Configure Subspaces** and \`SUBS_PUBLISH_REQUIRE_FINALIZED\`
|
||||
|
||||
Requires StartOS 0.4.0-beta.10 (\`start-sdk\` 2.0.9).`,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user