Compare commits
7
Commits
5bd878ac2d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b446cd5f69 | ||
|
|
073344d4d1 | ||
|
|
2b0224a678 | ||
|
|
89f4475e99 | ||
|
|
61cd3e679a | ||
|
|
1914ad3b6a | ||
|
|
0362388f7f |
@@ -1,3 +1,3 @@
|
||||
ARCHES := x86
|
||||
ARCHES := x86 arm
|
||||
# overrides to s9pk.mk must precede the include statement
|
||||
include node_modules/@start9labs/start-sdk/s9pk.mk
|
||||
|
||||
@@ -37,10 +37,10 @@ through a browser-based terminal.
|
||||
|
||||
| Field | Value |
|
||||
| --- | --- |
|
||||
| Spaces image | `docker.io/horologger/spaces` |
|
||||
| Subspaces image | `docker.io/horologger/subs:v0.1.2` |
|
||||
| Certrelay image | `docker.io/horologger/certrelay:v0.2.5` |
|
||||
| Nacho image | `docker.io/horologger/nacho:v1.0.0` |
|
||||
| Spaces image | `docker.io/horologger/spaces:v0.3.1` |
|
||||
| Subspaces image | `docker.io/horologger/subs:v0.1.2d` |
|
||||
| Certrelay image | `docker.io/horologger/certrelay:v0.2.8` |
|
||||
| Nacho image | `docker.io/horologger/nacho:v1.0.0b` |
|
||||
| Architectures | `linux/amd64`, `linux/arm64` |
|
||||
| Entrypoint | StartOS-managed (image entrypoints are **not** used directly) |
|
||||
|
||||
@@ -54,7 +54,7 @@ image's `docker_entrypoint.sh`; daemons are defined in `startos/main.ts`.
|
||||
| --- | --- | --- |
|
||||
| `/data` | `main` | Spaces data directory (`SPACED_DATA_DIR`), wallets, indexes, and `store.json` |
|
||||
| `/data/mainnet/.cookie` | `main` | Spaced RPC cookie (auto-generated by `spaced` at startup) |
|
||||
| `/data/store.json` | `main` | StartOS-managed credentials (Space-CLI Web UI password, bitcoind RPC user/password, spaced RPC user/password) and feature config (Subspaces toggles, Certrelay + Nacho settings). |
|
||||
| `/data/store.json` | `main` | StartOS-managed credentials (Space-CLI Web UI password, bitcoind RPC user/password, spaced RPC user/password, registry API keys) and feature config (Subspaces toggles, Certrelay + Nacho settings). |
|
||||
| `/data/subspaces/data` | `main` | `subs` runtime data (`SUBS_DATA_DIR`) — handles, proofs, config.db. Persists across restarts and enable/disable toggles. |
|
||||
| `/data/subspaces/prover` | `main` | Working directory for the `subs-prover` daemon. |
|
||||
| `/data/subspaces/registry` | `main` | Working directory for the `registry-server` daemon. |
|
||||
@@ -87,6 +87,9 @@ On the first install, StartOS:
|
||||
The nacho ignore-names list lives in `/data/nacho/ignore_names.txt` (not
|
||||
in the store); the `nacho-setup` oneshot creates that file with the
|
||||
default (`fold,swifty`) on first daemon start if it doesn't exist.
|
||||
Registry API keys (`registryApiKey` / `subsdApiKey`) are **not** seeded at
|
||||
install — run **Create Registry API Keys** before (or when prompted after)
|
||||
enabling Subspaces; `registry-server` will not start without them.
|
||||
6. Launches `spaced` as a managed daemon (no `screen`, no shell auto-start)
|
||||
and the `gotty` web terminal once the bashrc oneshot completes.
|
||||
7. Runs `certrelay-setup` (creates `/data/certrelay/data`, copies `fabric` and
|
||||
@@ -110,7 +113,6 @@ mainnet-only.
|
||||
| Bitcoin RPC username / password (registered on bitcoind) | Wallet creation, bidding, and registration -- all driven via `space-cli` inside the terminal |
|
||||
| Spaced RPC username / password (`SPACED_RPC_USER`/`SPACED_RPC_PASSWORD`) | `space-cli` flags and subcommands |
|
||||
| Subspaces auth toggle + credentials (`SUBS_BASIC_AUTH_USER`/`SUBS_BASIC_AUTH_PASSWORD`, off by default) | |
|
||||
| `SUBS_PUBLISH_REQUIRE_FINALIZED` (off by default; via **Configure Subspaces**) | |
|
||||
| Subspaces Prover auth toggle + credentials (`SUBS_PROVER_BASIC_AUTH_USER`/`SUBS_PROVER_BASIC_AUTH_PASSWORD`, off by default) | |
|
||||
| Chain selection (locked to `mainnet`) | |
|
||||
| Spaced data directory and RPC bind | |
|
||||
@@ -145,7 +147,6 @@ mainnet-only.
|
||||
| `disable-subspaces` | Disable Subspaces | Enabled (hidden when already off) | Any | Sets `store.enableSubspaces = false` and triggers a service restart so the subs and subs-prover daemons stop. On-disk data at `/data/subspaces` is preserved. |
|
||||
| `reset-subspaces-state` | Reset Subspaces State | Enabled | Any | Deletes `/data/subspaces` (runtime data only — binaries ship in the image). Use to wipe local handles/proofs. The spaces wallet on spaced is preserved. Warning-gated. |
|
||||
| `set-subs-prover` | Enable / Disable Subspaces Prover | Enabled | Any | Toggle (`store.enableSubsProver`, default **off**) that starts/stops only the `subs-prover` daemon, independent of the overall Subspaces toggle. When off, the daemon doesn't start but its interface (8888) stays registered. Saving restarts the service. |
|
||||
| `configure-subspaces` | Configure Subspaces | Enabled | Any | Toggle `SUBS_PUBLISH_REQUIRE_FINALIZED` (default **off**). When on, subs requires finalized commitments before certificate publish. Stored in `store.subsPublishRequireFinalized`; injected into the `subs` daemon env. Saving restarts the service. |
|
||||
| `enable-subs-auth` | Enable Subspaces Auth | Enabled (hidden when already on) | Any | Sets `store.subsAuthEnabled = true`. If no credentials exist yet, generates `{ username: 'spaces', password: random32 }` and stores them in `store.subsAuth`. Restart re-launches `subs` with `SUBS_BASIC_AUTH_USER`/`SUBS_BASIC_AUTH_PASSWORD` env vars set. |
|
||||
| `disable-subs-auth` | Disable Subspaces Auth | Enabled (hidden when already off) | Any | Sets `store.subsAuthEnabled = false`. Stored credentials are preserved so a later re-enable does not regenerate them. Restart re-launches `subs` without the auth env vars. |
|
||||
| `show-subs-credentials` | Show Subspaces Auth Credentials | Enabled | Any | Surfaces the current `SUBS_BASIC_AUTH_USER`/`SUBS_BASIC_AUTH_PASSWORD` (from `store.subsAuth`) plus a loopback connection URL. Reports whether auth is currently enforced. |
|
||||
@@ -154,6 +155,8 @@ mainnet-only.
|
||||
| `disable-subs-prover-auth` | Disable Subspaces Prover Auth | Enabled (hidden when already off) | Any | Sets `store.subsProverAuthEnabled = false`. Stored credentials are preserved. Restart re-launches `subs-prover` without the auth env vars. |
|
||||
| `show-subs-prover-credentials` | Show Subspaces Prover Auth Credentials | Enabled | Any | Surfaces the current `SUBS_PROVER_BASIC_AUTH_USER`/`SUBS_PROVER_BASIC_AUTH_PASSWORD` (from `store.subsProverAuth`) plus a loopback connection URL. Reports whether auth is currently enforced. |
|
||||
| `set-subs-prover-credentials` | Set Subspaces Prover Auth Credentials | Enabled | Any | Set or rotate `store.subsProverAuth`. Same UX as `set-subs-credentials` (blank password → auto-generated). Restarts the service only if prover auth is currently enabled. |
|
||||
| `create-registry-api-keys` | Create Registry API Keys | Enabled | Any | Generates (or rotates) `store.registryApiKey` + `store.subsdApiKey` as 64-char hex (`openssl rand -hex 32`). Both are required and must differ. Injected as `REGISTRY_API_KEY` / `SUBSD_API_KEY` into `registry-server`. Put `SUBSD_API_KEY` in subs Settings → Registry Server → Auth Token. Restarts so the registry picks up the new keys. When Subspaces is enabled without valid keys, a critical task points here. |
|
||||
| `show-registry-api-keys` | Show Registry API Keys | Enabled | Any | Surfaces the stored `REGISTRY_API_KEY` and `SUBSD_API_KEY`. Returns blanks until Create Registry API Keys has been run. |
|
||||
| `configure-certrelay` | Configure Certrelay | Enabled | Any | Sets `CERTRELAY_SELF_URL` (text), `CERTRELAY_BOOTSTRAP` (toggle), and `CERTRELAY_HEALTHCHECK_HANDLE` (text) in `store.json`; prefilled with current values. Saving restarts the service so certrelay picks up the new config. |
|
||||
| `configure-nacho` | Configure Nacho | Enabled | Any | Writes the Ignore Names list to `/data/nacho/ignore_names.txt` (read at runtime by nacho — survives daemon restarts independent of the store) and sets `EXPO_PUBLIC_WORKSHOP_PDF_LINK_TEXT` in `store.json`. Prefill reads the file (with the package default as fallback) plus the current store value. Saving restarts the service. |
|
||||
| `upload-support-pdf` | Upload Support PDF | Enabled | Any | Required `.pdf` file upload that overwrites `/data/support.pdf` on the main volume (read with `fs/promises`, piped via stdin into a temp `spaces` subcontainer's `cat > /data/support.pdf`). Split out from Configure Nacho because StartOS's form serializes an unselected `Value.file` as `{}` rather than `null`, which `Value.file(..., required: false).nullable()` rejects; a `required: true` dedicated action avoids the empty-form path entirely. |
|
||||
@@ -198,18 +201,22 @@ the same idempotent init logic runs and reuses the existing credentials in
|
||||
6. **The web terminal is independent of spaced.** Gotty stays reachable even
|
||||
when `spaced` is crash-looping, so you can always shell in to diagnose.
|
||||
7. **Subspaces is opt-in.** Fresh installs do not run Subspaces. Run **Enable
|
||||
Subspaces** to start it. It ships as the prebuilt `horologger/subs:v0.1.2`
|
||||
Subspaces** to start it. It ships as the prebuilt `horologger/subs:v0.1.2d`
|
||||
image (no compile step), so enabling is fast — the daemons start as soon
|
||||
as the image is on the box. Enabling runs three daemons, each with its own
|
||||
StartOS interface: **subs** (Web UI + Subs API, 7777), **registry-server**
|
||||
(8081), and — only if the separate **Enable / Disable Subspaces Prover**
|
||||
toggle is on (default **off**) — **subs-prover** (8888, no GPU). The
|
||||
prover's interface is always registered even when its daemon is off. subs
|
||||
loads the existing `default` spaces wallet at startup and does **not**
|
||||
create it — create it first from the Space-CLI Web UI with `spaces
|
||||
createwallet` if you haven't.
|
||||
prover's interface is always registered even when its daemon is off. Before
|
||||
(or when prompted after) enabling, run **Create Registry API Keys** —
|
||||
`registry-server` requires distinct `REGISTRY_API_KEY` and `SUBSD_API_KEY`
|
||||
env vars at boot. Paste `SUBSD_API_KEY` into subs Settings → Registry
|
||||
Server → Auth Token, then click Test. Use `REGISTRY_API_KEY` as the Bearer
|
||||
token for intake/`POST /register` clients. subs loads the existing
|
||||
`default` spaces wallet at startup and does **not** create it — create it
|
||||
first from the Space-CLI Web UI with `spaces createwallet` if you haven't.
|
||||
8. **Certrelay is always on.** It ships as the prebuilt
|
||||
`horologger/certrelay:v0.2.5` image (static musl binaries) and runs on
|
||||
`horologger/certrelay:v0.2.8` image (static musl binaries) and runs on
|
||||
7778 from service start. It talks to the local spaced over loopback using
|
||||
the `store.spacedAuth` credentials. The `fabric` and `monitor` binaries are copied to
|
||||
`/data/bin/` for manual use from the Space-CLI Web UI; neither is auto-started
|
||||
@@ -250,10 +257,10 @@ spaces balance
|
||||
package_id: spaces
|
||||
upstream_version: subspacesplus
|
||||
images:
|
||||
spaces: docker.io/horologger/spaces:v0.2.1
|
||||
subspaces: docker.io/horologger/subs:v0.1.2
|
||||
certrelay: docker.io/horologger/certrelay:v0.2.5
|
||||
nacho: docker.io/horologger/nacho:v1.0.0
|
||||
spaces: docker.io/horologger/spaces:v0.3.1
|
||||
subspaces: docker.io/horologger/subs:v0.1.2d
|
||||
certrelay: docker.io/horologger/certrelay:v0.2.8
|
||||
nacho: docker.io/horologger/nacho:v1.0.0b
|
||||
architectures: [x86_64, aarch64]
|
||||
volumes:
|
||||
main: /data
|
||||
@@ -285,17 +292,27 @@ spaced_env_vars:
|
||||
- BTC_RPC_PASSWORD
|
||||
- APP_USER
|
||||
- APP_PASSWORD
|
||||
spaces:
|
||||
image: horologger/spaces:v0.3.1 # spaced, space-cli, gotty (terminal image)
|
||||
subspaces:
|
||||
default_enabled: false # opt-in via Enable Subspaces action
|
||||
store_field: enableSubspaces
|
||||
subs_prover_default_enabled: false # separate toggle: set-subs-prover (store.enableSubsProver)
|
||||
image: horologger/subs:v0.1.2 # prebuilt; no compile step
|
||||
image: horologger/subs:v0.1.2d # prebuilt; no compile step
|
||||
data_dir: /data/subspaces/data
|
||||
wallet: default # loaded by subs, NOT created (create via space-cli first)
|
||||
daemons: # three separate daemons, each its own interface
|
||||
subs: { bin: /usr/local/bin/subs, port: 7777 }
|
||||
subs-prover: { bin: /usr/local/bin/subs-prover, port: 8888, gpu: none }
|
||||
registry-server: { bin: /usr/local/bin/registry-server, port: 8081 } # remapped from upstream 8080
|
||||
registry_api_keys: # mandatory for registry-server boot; must differ
|
||||
store_fields: [registryApiKey, subsdApiKey]
|
||||
env_vars: [REGISTRY_API_KEY, SUBSD_API_KEY] # always injected into registry-server
|
||||
generate: openssl-rand-hex-32 # 64 lowercase hex via create-registry-api-keys
|
||||
subs_ui: Settings → Registry Server → Auth Token = SUBSD_API_KEY
|
||||
actions:
|
||||
- create-registry-api-keys
|
||||
- show-registry-api-keys
|
||||
auth: # optional HTTP basic auth in front of subs (Web UI + Subs API on 7777)
|
||||
default_enabled: false
|
||||
store_enabled_field: subsAuthEnabled
|
||||
@@ -304,11 +321,6 @@ subspaces:
|
||||
env_vars: # injected only when subsAuthEnabled=true AND subsAuth!=null
|
||||
- SUBS_BASIC_AUTH_USER
|
||||
- SUBS_BASIC_AUTH_PASSWORD
|
||||
publish_require_finalized:
|
||||
default: false
|
||||
store_field: subsPublishRequireFinalized
|
||||
env_var: SUBS_PUBLISH_REQUIRE_FINALIZED # always 'true' or 'false' on subs daemon
|
||||
action: configure-subspaces
|
||||
actions:
|
||||
- enable-subs-auth
|
||||
- disable-subs-auth
|
||||
@@ -329,7 +341,7 @@ subspaces:
|
||||
- set-subs-prover-credentials
|
||||
certrelay:
|
||||
always_on: true
|
||||
image: horologger/certrelay:v0.2.5 # prebuilt; static musl binaries
|
||||
image: horologger/certrelay:v0.2.8 # prebuilt; static musl binaries
|
||||
server_bin: /usr/local/bin/certrelay
|
||||
fabric_bin: /usr/local/bin/fabric # copied to /data/bin/fabric for the CLI
|
||||
monitor_bin: /usr/local/bin/monitor # copied to /data/bin/monitor (CLI only; not started)
|
||||
@@ -348,7 +360,7 @@ certrelay:
|
||||
CERTRELAY_HEALTHCHECK_HANDLE: account-digital-useful.genesis@key # via action
|
||||
nacho:
|
||||
always_on: true
|
||||
image: horologger/nacho:v1.0.0 # Expo dev server
|
||||
image: horologger/nacho:v1.0.0b # Expo dev server
|
||||
port: 8082
|
||||
data_dir: /data/nacho
|
||||
ignore_names_file: /data/nacho/ignore_names.txt # read at runtime; default 'fold,swifty' seeded by nacho-setup
|
||||
@@ -372,7 +384,6 @@ actions:
|
||||
- enable-subspaces
|
||||
- disable-subspaces
|
||||
- set-subs-prover
|
||||
- configure-subspaces
|
||||
- enable-subs-auth
|
||||
- disable-subs-auth
|
||||
- show-subs-credentials
|
||||
@@ -381,6 +392,8 @@ actions:
|
||||
- disable-subs-prover-auth
|
||||
- show-subs-prover-credentials
|
||||
- set-subs-prover-credentials
|
||||
- create-registry-api-keys
|
||||
- show-registry-api-keys
|
||||
- reset-subspaces-state
|
||||
- configure-certrelay
|
||||
- configure-nacho
|
||||
|
||||
@@ -57,10 +57,32 @@ spaces walletbalance default
|
||||
`/data/mainnet/wallets/default` to `.bakNNN` suffixes so no prior state is
|
||||
lost, then calls `space-cli importwallet` and `space-cli loadwallet`.
|
||||
Requires the service to be running.
|
||||
- **Create Registry API Keys** — generates (or rotates) `REGISTRY_API_KEY` and
|
||||
`SUBSD_API_KEY` for the Subspaces registry. Required before the registry
|
||||
daemon will start.
|
||||
- **Show Registry API Keys** — displays the stored registry keys.
|
||||
|
||||
## Subspaces registry API keys
|
||||
|
||||
The embedded `registry-server` will not start without two distinct bearer
|
||||
secrets. Before enabling Subspaces (or when StartOS prompts you after):
|
||||
|
||||
1. Run **Create Registry API Keys** — generates `REGISTRY_API_KEY` and
|
||||
`SUBSD_API_KEY` (same shape as `openssl rand -hex 32`).
|
||||
2. In the Subspaces Web UI, open **Settings → Registry Server**, set **Auth
|
||||
Token** to `SUBSD_API_KEY`, and click **Test**.
|
||||
3. Give `REGISTRY_API_KEY` to any intake client that calls `POST /register`.
|
||||
|
||||
Use **Show Registry API Keys** to retrieve the stored values later, or re-run
|
||||
**Create Registry API Keys** to rotate them (then update the Auth Token).
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Mainnet only.** No testnet, signet, or regtest.
|
||||
- **Container images** (see also the package manifest): the `spaced` runtime uses
|
||||
`horologger/spaces:v0.3.1`; Subspaces, Certrelay, and Nacho use
|
||||
`horologger/subs:v0.1.2d`, `horologger/certrelay:v0.2.8`, and
|
||||
`horologger/nacho:v1.0.0b`.
|
||||
- The image's `docker_entrypoint.sh` is not used. `spaced` is managed by
|
||||
StartOS, not started inside a `screen` session by login.
|
||||
- The only externally bound port is `8080` (gotty). Spaced RPC stays on
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { i18n } from './i18n'
|
||||
|
||||
/** Headers for the Actions and Config page (`ActionMetadata.group`). */
|
||||
export const actionGroups = {
|
||||
spaceCli: i18n('Space-CLI Web UI'),
|
||||
bitcoin: i18n('Bitcoin'),
|
||||
spaced: i18n('Spaced'),
|
||||
subspaces: i18n('Subspaces'),
|
||||
subspacesAuth: i18n('Subspaces Auth'),
|
||||
subspacesProverAuth: i18n('Subspaces Prover Auth'),
|
||||
subspacesRegistry: i18n('Subspaces Registry'),
|
||||
certrelay: i18n('Certrelay'),
|
||||
nacho: i18n('Nacho'),
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import {
|
||||
@@ -61,7 +62,7 @@ export const configureCertrelay = sdk.Action.withInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.certrelay,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import {
|
||||
@@ -90,7 +91,7 @@ export const configureNacho = sdk.Action.withInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.nacho,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -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,63 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
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: actionGroups.subspacesRegistry,
|
||||
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,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
|
||||
@@ -16,7 +17,7 @@ export const disableSubsAuth = sdk.Action.withoutInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.subspacesAuth,
|
||||
visibility: enabled === true ? 'enabled' : 'hidden',
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
|
||||
@@ -16,7 +17,7 @@ export const disableSubsProverAuth = sdk.Action.withoutInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.subspacesProverAuth,
|
||||
visibility: enabled === true ? 'enabled' : 'hidden',
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
|
||||
@@ -16,7 +17,7 @@ export const disableSubspaces = sdk.Action.withoutInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.subspaces,
|
||||
visibility: current === true ? 'enabled' : 'hidden',
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { randomPassword } from '../utils'
|
||||
@@ -19,7 +20,7 @@ export const enableSubsAuth = sdk.Action.withoutInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.subspacesAuth,
|
||||
visibility: enabled === true ? 'hidden' : 'enabled',
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { randomPassword } from '../utils'
|
||||
@@ -19,7 +20,7 @@ export const enableSubsProverAuth = sdk.Action.withoutInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.subspacesProverAuth,
|
||||
visibility: enabled === true ? 'hidden' : 'enabled',
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
|
||||
@@ -16,7 +17,7 @@ export const enableSubspaces = sdk.Action.withoutInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.subspaces,
|
||||
visibility: current === true ? 'hidden' : 'enabled',
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { dataDir, SPACED_CHAIN } from '../utils'
|
||||
@@ -18,7 +19,7 @@ export const exportWallet = sdk.Action.withoutInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'only-running',
|
||||
group: null,
|
||||
group: actionGroups.spaced,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { dataDir, SPACED_CHAIN } from '../utils'
|
||||
@@ -37,7 +38,7 @@ export const importWallet = sdk.Action.withInput(
|
||||
'Existing /data/mainnet/wallets_backup/default.json and /data/mainnet/wallets/default will be renamed with .bakNNN suffixes before the import. The active spaced daemon will load the imported wallet on success.',
|
||||
),
|
||||
allowedStatuses: 'only-running',
|
||||
group: null,
|
||||
group: actionGroups.spaced,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { APP_USER, randomPassword } from '../utils'
|
||||
@@ -15,7 +16,7 @@ export const resetPassword = sdk.Action.withoutInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.spaceCli,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { dataDir, SPACED_CHAIN } from '../utils'
|
||||
@@ -16,7 +17,7 @@ export const resetSpacedState = sdk.Action.withoutInput(
|
||||
"This deletes spaced's on-disk index. The next start will resync from spaces' anchor and can take a while. store.json (passwords + RPC credentials) is preserved.",
|
||||
),
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.spaced,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { dataDir, SUBSPACES_DIR } from '../utils'
|
||||
@@ -16,7 +17,7 @@ export const resetSubspacesState = sdk.Action.withoutInput(
|
||||
'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,
|
||||
group: actionGroups.subspaces,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import {
|
||||
@@ -17,7 +18,7 @@ export const setBitcoinRpc = sdk.Action.withoutInput(
|
||||
description: i18n('Re-run the bitcoind RPC credential setup for Spaces'),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.bitcoin,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { randomPassword } from '../utils'
|
||||
@@ -50,7 +51,7 @@ export const setSubsCredentials = sdk.Action.withInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.subspacesAuth,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
|
||||
@@ -28,7 +29,7 @@ export const setSubsProver = sdk.Action.withInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.subspaces,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { randomPassword } from '../utils'
|
||||
@@ -50,7 +51,7 @@ export const setSubsProverCredentials = sdk.Action.withInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.subspacesProverAuth,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { APP_USER } from '../utils'
|
||||
@@ -15,7 +16,7 @@ export const showCredentials = sdk.Action.withoutInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.spaceCli,
|
||||
visibility: 'hidden',
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { APP_USER } from '../utils'
|
||||
@@ -15,7 +16,7 @@ export const showPassword = sdk.Action.withoutInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.spaceCli,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
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: actionGroups.subspacesRegistry,
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { spacedRpcPort } from '../utils'
|
||||
@@ -15,7 +16,7 @@ export const showSpacedCredentials = sdk.Action.withoutInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.spaced,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { SUBSPACES_UI_PORT } from '../utils'
|
||||
@@ -15,7 +16,7 @@ export const showSubsCredentials = sdk.Action.withoutInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.subspacesAuth,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { SUBSPACES_PROVER_PORT } from '../utils'
|
||||
@@ -15,7 +16,7 @@ export const showSubsProverCredentials = sdk.Action.withoutInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.subspacesProverAuth,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { storeJson } from '../fileModels/storeJson'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { dataDir, SPACED_CHAIN } from '../utils'
|
||||
@@ -15,7 +16,7 @@ export const syncStatus = sdk.Action.withoutInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'only-running',
|
||||
group: null,
|
||||
group: actionGroups.spaced,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { readFile } from 'fs/promises'
|
||||
import { actionGroups } from '../actionGroups'
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
import { dataDir } from '../utils'
|
||||
@@ -36,7 +37,7 @@ export const uploadSupportPdf = sdk.Action.withInput(
|
||||
),
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: actionGroups.nacho,
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
|
||||
@@ -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,30 @@ 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,
|
||||
Bitcoin: 225,
|
||||
Spaced: 226,
|
||||
Subspaces: 227,
|
||||
'Subspaces Auth': 228,
|
||||
'Subspaces Prover Auth': 229,
|
||||
} 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',
|
||||
),
|
||||
})
|
||||
})
|
||||
+22
-3
@@ -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(
|
||||
@@ -256,7 +257,9 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
'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',
|
||||
// Quoted delimiter: banner copy uses backticks around CLI names; an unquoted
|
||||
// <<EOF would run command substitution (including `monitor` on PATH).
|
||||
'cat <<\'EOF\'',
|
||||
'',
|
||||
// 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
|
||||
@@ -369,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.1' },
|
||||
source: { dockerTag: 'horologger/spaces:v0.3.1' },
|
||||
arch: ['x86_64', 'aarch64'],
|
||||
},
|
||||
subspaces: {
|
||||
source: { dockerTag: 'horologger/subs:v0.1.2' },
|
||||
source: { dockerTag: 'horologger/subs:v0.1.2d' },
|
||||
arch: ['x86_64', 'aarch64'],
|
||||
},
|
||||
certrelay: {
|
||||
source: { dockerTag: 'horologger/certrelay:v0.2.5' },
|
||||
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,11 +1,11 @@
|
||||
import { IMPOSSIBLE, VersionInfo } from '@start9labs/start-sdk'
|
||||
|
||||
export const current = VersionInfo.of({
|
||||
version: '0.2.1:1',
|
||||
version: '0.3.1:0',
|
||||
releaseNotes: {
|
||||
en_US: `Packaging-only revision — no upstream changes. The images are unchanged from 0.2.1:0 (\`spaces:v0.2.1\`, \`subs:v0.1.2\`, \`certrelay:v0.2.5\`, \`nacho:v1.0.0\`).
|
||||
en_US: `Update to upstream Spaces **0.3.1** (\`horologger/spaces:v0.3.1\`). Also bundles \`subs:v0.1.2d\` (\`certrelay:v0.2.8\` and \`nacho:v1.0.0b\` unchanged).
|
||||
|
||||
Rebuilt against \`start-sdk\` 2.0.9, which requires StartOS 0.4.0-beta.10.`,
|
||||
Requires StartOS 0.4.0-beta.10 (\`start-sdk\` 2.0.9).`,
|
||||
},
|
||||
migrations: {
|
||||
up: async ({ effects }) => {},
|
||||
|
||||
Reference in New Issue
Block a user