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
|
# overrides to s9pk.mk must precede the include statement
|
||||||
include node_modules/@start9labs/start-sdk/s9pk.mk
|
include node_modules/@start9labs/start-sdk/s9pk.mk
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ through a browser-based terminal.
|
|||||||
|
|
||||||
| Field | Value |
|
| Field | Value |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Spaces image | `docker.io/horologger/spaces` |
|
| Spaces image | `docker.io/horologger/spaces:v0.3.1` |
|
||||||
| Subspaces image | `docker.io/horologger/subs:v0.1.2` |
|
| Subspaces image | `docker.io/horologger/subs:v0.1.2d` |
|
||||||
| Certrelay image | `docker.io/horologger/certrelay:v0.2.5` |
|
| Certrelay image | `docker.io/horologger/certrelay:v0.2.8` |
|
||||||
| Nacho image | `docker.io/horologger/nacho:v1.0.0` |
|
| Nacho image | `docker.io/horologger/nacho:v1.0.0b` |
|
||||||
| Architectures | `linux/amd64`, `linux/arm64` |
|
| Architectures | `linux/amd64`, `linux/arm64` |
|
||||||
| Entrypoint | StartOS-managed (image entrypoints are **not** used directly) |
|
| 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` | `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/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/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/prover` | `main` | Working directory for the `subs-prover` daemon. |
|
||||||
| `/data/subspaces/registry` | `main` | Working directory for the `registry-server` 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
|
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
|
in the store); the `nacho-setup` oneshot creates that file with the
|
||||||
default (`fold,swifty`) on first daemon start if it doesn't exist.
|
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)
|
6. Launches `spaced` as a managed daemon (no `screen`, no shell auto-start)
|
||||||
and the `gotty` web terminal once the bashrc oneshot completes.
|
and the `gotty` web terminal once the bashrc oneshot completes.
|
||||||
7. Runs `certrelay-setup` (creates `/data/certrelay/data`, copies `fabric` and
|
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 |
|
| 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 |
|
| 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) | |
|
| 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) | |
|
| Subspaces Prover auth toggle + credentials (`SUBS_PROVER_BASIC_AUTH_USER`/`SUBS_PROVER_BASIC_AUTH_PASSWORD`, off by default) | |
|
||||||
| Chain selection (locked to `mainnet`) | |
|
| Chain selection (locked to `mainnet`) | |
|
||||||
| Spaced data directory and RPC bind | |
|
| 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. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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-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. |
|
| `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. |
|
| `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
|
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.
|
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
|
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
|
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
|
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**
|
StartOS interface: **subs** (Web UI + Subs API, 7777), **registry-server**
|
||||||
(8081), and — only if the separate **Enable / Disable Subspaces Prover**
|
(8081), and — only if the separate **Enable / Disable Subspaces Prover**
|
||||||
toggle is on (default **off**) — **subs-prover** (8888, no GPU). The
|
toggle is on (default **off**) — **subs-prover** (8888, no GPU). The
|
||||||
prover's interface is always registered even when its daemon is off. subs
|
prover's interface is always registered even when its daemon is off. Before
|
||||||
loads the existing `default` spaces wallet at startup and does **not**
|
(or when prompted after) enabling, run **Create Registry API Keys** —
|
||||||
create it — create it first from the Space-CLI Web UI with `spaces
|
`registry-server` requires distinct `REGISTRY_API_KEY` and `SUBSD_API_KEY`
|
||||||
createwallet` if you haven't.
|
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
|
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
|
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
|
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
|
`/data/bin/` for manual use from the Space-CLI Web UI; neither is auto-started
|
||||||
@@ -250,10 +257,10 @@ spaces balance
|
|||||||
package_id: spaces
|
package_id: spaces
|
||||||
upstream_version: subspacesplus
|
upstream_version: subspacesplus
|
||||||
images:
|
images:
|
||||||
spaces: docker.io/horologger/spaces:v0.2.1
|
spaces: docker.io/horologger/spaces:v0.3.1
|
||||||
subspaces: docker.io/horologger/subs:v0.1.2
|
subspaces: docker.io/horologger/subs:v0.1.2d
|
||||||
certrelay: docker.io/horologger/certrelay:v0.2.5
|
certrelay: docker.io/horologger/certrelay:v0.2.8
|
||||||
nacho: docker.io/horologger/nacho:v1.0.0
|
nacho: docker.io/horologger/nacho:v1.0.0b
|
||||||
architectures: [x86_64, aarch64]
|
architectures: [x86_64, aarch64]
|
||||||
volumes:
|
volumes:
|
||||||
main: /data
|
main: /data
|
||||||
@@ -285,17 +292,27 @@ spaced_env_vars:
|
|||||||
- BTC_RPC_PASSWORD
|
- BTC_RPC_PASSWORD
|
||||||
- APP_USER
|
- APP_USER
|
||||||
- APP_PASSWORD
|
- APP_PASSWORD
|
||||||
|
spaces:
|
||||||
|
image: horologger/spaces:v0.3.1 # spaced, space-cli, gotty (terminal image)
|
||||||
subspaces:
|
subspaces:
|
||||||
default_enabled: false # opt-in via Enable Subspaces action
|
default_enabled: false # opt-in via Enable Subspaces action
|
||||||
store_field: enableSubspaces
|
store_field: enableSubspaces
|
||||||
subs_prover_default_enabled: false # separate toggle: set-subs-prover (store.enableSubsProver)
|
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
|
data_dir: /data/subspaces/data
|
||||||
wallet: default # loaded by subs, NOT created (create via space-cli first)
|
wallet: default # loaded by subs, NOT created (create via space-cli first)
|
||||||
daemons: # three separate daemons, each its own interface
|
daemons: # three separate daemons, each its own interface
|
||||||
subs: { bin: /usr/local/bin/subs, port: 7777 }
|
subs: { bin: /usr/local/bin/subs, port: 7777 }
|
||||||
subs-prover: { bin: /usr/local/bin/subs-prover, port: 8888, gpu: none }
|
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-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)
|
auth: # optional HTTP basic auth in front of subs (Web UI + Subs API on 7777)
|
||||||
default_enabled: false
|
default_enabled: false
|
||||||
store_enabled_field: subsAuthEnabled
|
store_enabled_field: subsAuthEnabled
|
||||||
@@ -304,11 +321,6 @@ subspaces:
|
|||||||
env_vars: # injected only when subsAuthEnabled=true AND subsAuth!=null
|
env_vars: # injected only when subsAuthEnabled=true AND subsAuth!=null
|
||||||
- SUBS_BASIC_AUTH_USER
|
- SUBS_BASIC_AUTH_USER
|
||||||
- SUBS_BASIC_AUTH_PASSWORD
|
- 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:
|
actions:
|
||||||
- enable-subs-auth
|
- enable-subs-auth
|
||||||
- disable-subs-auth
|
- disable-subs-auth
|
||||||
@@ -329,7 +341,7 @@ subspaces:
|
|||||||
- set-subs-prover-credentials
|
- set-subs-prover-credentials
|
||||||
certrelay:
|
certrelay:
|
||||||
always_on: true
|
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
|
server_bin: /usr/local/bin/certrelay
|
||||||
fabric_bin: /usr/local/bin/fabric # copied to /data/bin/fabric for the CLI
|
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)
|
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
|
CERTRELAY_HEALTHCHECK_HANDLE: account-digital-useful.genesis@key # via action
|
||||||
nacho:
|
nacho:
|
||||||
always_on: true
|
always_on: true
|
||||||
image: horologger/nacho:v1.0.0 # Expo dev server
|
image: horologger/nacho:v1.0.0b # Expo dev server
|
||||||
port: 8082
|
port: 8082
|
||||||
data_dir: /data/nacho
|
data_dir: /data/nacho
|
||||||
ignore_names_file: /data/nacho/ignore_names.txt # read at runtime; default 'fold,swifty' seeded by nacho-setup
|
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
|
- enable-subspaces
|
||||||
- disable-subspaces
|
- disable-subspaces
|
||||||
- set-subs-prover
|
- set-subs-prover
|
||||||
- configure-subspaces
|
|
||||||
- enable-subs-auth
|
- enable-subs-auth
|
||||||
- disable-subs-auth
|
- disable-subs-auth
|
||||||
- show-subs-credentials
|
- show-subs-credentials
|
||||||
@@ -381,6 +392,8 @@ actions:
|
|||||||
- disable-subs-prover-auth
|
- disable-subs-prover-auth
|
||||||
- show-subs-prover-credentials
|
- show-subs-prover-credentials
|
||||||
- set-subs-prover-credentials
|
- set-subs-prover-credentials
|
||||||
|
- create-registry-api-keys
|
||||||
|
- show-registry-api-keys
|
||||||
- reset-subspaces-state
|
- reset-subspaces-state
|
||||||
- configure-certrelay
|
- configure-certrelay
|
||||||
- configure-nacho
|
- configure-nacho
|
||||||
|
|||||||
@@ -57,10 +57,32 @@ spaces walletbalance default
|
|||||||
`/data/mainnet/wallets/default` to `.bakNNN` suffixes so no prior state is
|
`/data/mainnet/wallets/default` to `.bakNNN` suffixes so no prior state is
|
||||||
lost, then calls `space-cli importwallet` and `space-cli loadwallet`.
|
lost, then calls `space-cli importwallet` and `space-cli loadwallet`.
|
||||||
Requires the service to be running.
|
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
|
## Limitations
|
||||||
|
|
||||||
- **Mainnet only.** No testnet, signet, or regtest.
|
- **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
|
- The image's `docker_entrypoint.sh` is not used. `spaced` is managed by
|
||||||
StartOS, not started inside a `screen` session by login.
|
StartOS, not started inside a `screen` session by login.
|
||||||
- The only externally bound port is `8080` (gotty). Spaced RPC stays on
|
- 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 { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import {
|
import {
|
||||||
@@ -61,7 +62,7 @@ export const configureCertrelay = sdk.Action.withInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.certrelay,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import {
|
import {
|
||||||
@@ -90,7 +91,7 @@ export const configureNacho = sdk.Action.withInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.nacho,
|
||||||
visibility: 'enabled',
|
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 { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ export const disableSubsAuth = sdk.Action.withoutInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.subspacesAuth,
|
||||||
visibility: enabled === true ? 'enabled' : 'hidden',
|
visibility: enabled === true ? 'enabled' : 'hidden',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ export const disableSubsProverAuth = sdk.Action.withoutInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.subspacesProverAuth,
|
||||||
visibility: enabled === true ? 'enabled' : 'hidden',
|
visibility: enabled === true ? 'enabled' : 'hidden',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ export const disableSubspaces = sdk.Action.withoutInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.subspaces,
|
||||||
visibility: current === true ? 'enabled' : 'hidden',
|
visibility: current === true ? 'enabled' : 'hidden',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { randomPassword } from '../utils'
|
import { randomPassword } from '../utils'
|
||||||
@@ -19,7 +20,7 @@ export const enableSubsAuth = sdk.Action.withoutInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.subspacesAuth,
|
||||||
visibility: enabled === true ? 'hidden' : 'enabled',
|
visibility: enabled === true ? 'hidden' : 'enabled',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { randomPassword } from '../utils'
|
import { randomPassword } from '../utils'
|
||||||
@@ -19,7 +20,7 @@ export const enableSubsProverAuth = sdk.Action.withoutInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.subspacesProverAuth,
|
||||||
visibility: enabled === true ? 'hidden' : 'enabled',
|
visibility: enabled === true ? 'hidden' : 'enabled',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ export const enableSubspaces = sdk.Action.withoutInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.subspaces,
|
||||||
visibility: current === true ? 'hidden' : 'enabled',
|
visibility: current === true ? 'hidden' : 'enabled',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { dataDir, SPACED_CHAIN } from '../utils'
|
import { dataDir, SPACED_CHAIN } from '../utils'
|
||||||
@@ -18,7 +19,7 @@ export const exportWallet = sdk.Action.withoutInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'only-running',
|
allowedStatuses: 'only-running',
|
||||||
group: null,
|
group: actionGroups.spaced,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { dataDir, SPACED_CHAIN } from '../utils'
|
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.',
|
'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',
|
allowedStatuses: 'only-running',
|
||||||
group: null,
|
group: actionGroups.spaced,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { configureCertrelay } from './configureCertrelay'
|
import { configureCertrelay } from './configureCertrelay'
|
||||||
import { configureNacho } from './configureNacho'
|
import { configureNacho } from './configureNacho'
|
||||||
import { configureSubspaces } from './configureSubspaces'
|
import { createRegistryApiKeys } from './createRegistryApiKeys'
|
||||||
import { disableSubsAuth } from './disableSubsAuth'
|
import { disableSubsAuth } from './disableSubsAuth'
|
||||||
import { disableSubsProverAuth } from './disableSubsProverAuth'
|
import { disableSubsProverAuth } from './disableSubsProverAuth'
|
||||||
import { disableSubspaces } from './disableSubspaces'
|
import { disableSubspaces } from './disableSubspaces'
|
||||||
@@ -19,6 +19,7 @@ import { setSubsProver } from './setSubsProver'
|
|||||||
import { setSubsProverCredentials } from './setSubsProverCredentials'
|
import { setSubsProverCredentials } from './setSubsProverCredentials'
|
||||||
import { showCredentials } from './showCredentials'
|
import { showCredentials } from './showCredentials'
|
||||||
import { showPassword } from './showPassword'
|
import { showPassword } from './showPassword'
|
||||||
|
import { showRegistryApiKeys } from './showRegistryApiKeys'
|
||||||
import { showSpacedCredentials } from './showSpacedCredentials'
|
import { showSpacedCredentials } from './showSpacedCredentials'
|
||||||
import { showSubsCredentials } from './showSubsCredentials'
|
import { showSubsCredentials } from './showSubsCredentials'
|
||||||
import { showSubsProverCredentials } from './showSubsProverCredentials'
|
import { showSubsProverCredentials } from './showSubsProverCredentials'
|
||||||
@@ -49,5 +50,6 @@ export const actions = sdk.Actions.of()
|
|||||||
.addAction(resetSubspacesState)
|
.addAction(resetSubspacesState)
|
||||||
.addAction(configureCertrelay)
|
.addAction(configureCertrelay)
|
||||||
.addAction(configureNacho)
|
.addAction(configureNacho)
|
||||||
.addAction(configureSubspaces)
|
.addAction(createRegistryApiKeys)
|
||||||
|
.addAction(showRegistryApiKeys)
|
||||||
.addAction(uploadSupportPdf)
|
.addAction(uploadSupportPdf)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { APP_USER, randomPassword } from '../utils'
|
import { APP_USER, randomPassword } from '../utils'
|
||||||
@@ -15,7 +16,7 @@ export const resetPassword = sdk.Action.withoutInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.spaceCli,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { dataDir, SPACED_CHAIN } from '../utils'
|
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.",
|
"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',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.spaced,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { dataDir, SUBSPACES_DIR } from '../utils'
|
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.',
|
'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',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.subspaces,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import {
|
import {
|
||||||
@@ -17,7 +18,7 @@ export const setBitcoinRpc = sdk.Action.withoutInput(
|
|||||||
description: i18n('Re-run the bitcoind RPC credential setup for Spaces'),
|
description: i18n('Re-run the bitcoind RPC credential setup for Spaces'),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.bitcoin,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { randomPassword } from '../utils'
|
import { randomPassword } from '../utils'
|
||||||
@@ -50,7 +51,7 @@ export const setSubsCredentials = sdk.Action.withInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.subspacesAuth,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ export const setSubsProver = sdk.Action.withInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.subspaces,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { randomPassword } from '../utils'
|
import { randomPassword } from '../utils'
|
||||||
@@ -50,7 +51,7 @@ export const setSubsProverCredentials = sdk.Action.withInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.subspacesProverAuth,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { APP_USER } from '../utils'
|
import { APP_USER } from '../utils'
|
||||||
@@ -15,7 +16,7 @@ export const showCredentials = sdk.Action.withoutInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.spaceCli,
|
||||||
visibility: 'hidden',
|
visibility: 'hidden',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { APP_USER } from '../utils'
|
import { APP_USER } from '../utils'
|
||||||
@@ -15,7 +16,7 @@ export const showPassword = sdk.Action.withoutInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.spaceCli,
|
||||||
visibility: 'enabled',
|
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 { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { spacedRpcPort } from '../utils'
|
import { spacedRpcPort } from '../utils'
|
||||||
@@ -15,7 +16,7 @@ export const showSpacedCredentials = sdk.Action.withoutInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.spaced,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { SUBSPACES_UI_PORT } from '../utils'
|
import { SUBSPACES_UI_PORT } from '../utils'
|
||||||
@@ -15,7 +16,7 @@ export const showSubsCredentials = sdk.Action.withoutInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.subspacesAuth,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { SUBSPACES_PROVER_PORT } from '../utils'
|
import { SUBSPACES_PROVER_PORT } from '../utils'
|
||||||
@@ -15,7 +16,7 @@ export const showSubsProverCredentials = sdk.Action.withoutInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.subspacesProverAuth,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { storeJson } from '../fileModels/storeJson'
|
import { storeJson } from '../fileModels/storeJson'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { dataDir, SPACED_CHAIN } from '../utils'
|
import { dataDir, SPACED_CHAIN } from '../utils'
|
||||||
@@ -15,7 +16,7 @@ export const syncStatus = sdk.Action.withoutInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'only-running',
|
allowedStatuses: 'only-running',
|
||||||
group: null,
|
group: actionGroups.spaced,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { readFile } from 'fs/promises'
|
import { readFile } from 'fs/promises'
|
||||||
|
import { actionGroups } from '../actionGroups'
|
||||||
import { i18n } from '../i18n'
|
import { i18n } from '../i18n'
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { dataDir } from '../utils'
|
import { dataDir } from '../utils'
|
||||||
@@ -36,7 +37,7 @@ export const uploadSupportPdf = sdk.Action.withInput(
|
|||||||
),
|
),
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'any',
|
allowedStatuses: 'any',
|
||||||
group: null,
|
group: actionGroups.nacho,
|
||||||
visibility: 'enabled',
|
visibility: 'enabled',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,12 @@ const shape = z.object({
|
|||||||
certrelayBootstrap: z.boolean().nullable().catch(null),
|
certrelayBootstrap: z.boolean().nullable().catch(null),
|
||||||
certrelayHealthcheckHandle: z.string().nullable().catch(null),
|
certrelayHealthcheckHandle: z.string().nullable().catch(null),
|
||||||
nachoWorkshopPdfLinkText: 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(
|
export const storeJson = FileHelper.json(
|
||||||
|
|||||||
@@ -289,16 +289,30 @@ const dict = {
|
|||||||
205,
|
205,
|
||||||
'Subspaces Prover Auth credentials saved. Auth is currently DISABLED — enable it with "Enable Subspaces Prover Auth" to enforce these credentials.':
|
'Subspaces Prover Auth credentials saved. Auth is currently DISABLED — enable it with "Enable Subspaces Prover Auth" to enforce these credentials.':
|
||||||
206,
|
206,
|
||||||
'Configure Subspaces': 207,
|
'Create Registry API Keys': 213,
|
||||||
'Set user-tunable subs options. Saving restarts the service so the subs daemon picks up the new environment.':
|
'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.':
|
||||||
208,
|
214,
|
||||||
'Require Finalized Publish': 209,
|
'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.':
|
||||||
'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.':
|
215,
|
||||||
210,
|
REGISTRY_API_KEY: 216,
|
||||||
'Subspaces configuration saved. SUBS_PUBLISH_REQUIRE_FINALIZED is enabled; the service is restarting.':
|
SUBSD_API_KEY: 217,
|
||||||
211,
|
'Bearer secret for intake/POST /register clients.': 218,
|
||||||
'Subspaces configuration saved. SUBS_PUBLISH_REQUIRE_FINALIZED is disabled; the service is restarting.':
|
'Paste into subs Settings → Registry Server → Auth Token (subs↔registry channel).':
|
||||||
212,
|
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
|
} as const
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { setInterfaces } from '../interfaces'
|
|||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { versionGraph } from '../versions'
|
import { versionGraph } from '../versions'
|
||||||
import { taskBtcAuth } from './taskBtcAuth'
|
import { taskBtcAuth } from './taskBtcAuth'
|
||||||
|
import { taskRegistryApiKeys } from './taskRegistryApiKeys'
|
||||||
import { taskSeedCertrelay } from './taskSeedCertrelay'
|
import { taskSeedCertrelay } from './taskSeedCertrelay'
|
||||||
import { taskSeedEnableSubspaces } from './taskSeedEnableSubspaces'
|
import { taskSeedEnableSubspaces } from './taskSeedEnableSubspaces'
|
||||||
import { taskSeedNacho } from './taskSeedNacho'
|
import { taskSeedNacho } from './taskSeedNacho'
|
||||||
@@ -20,6 +21,7 @@ export const init = sdk.setupInit(
|
|||||||
taskBtcAuth,
|
taskBtcAuth,
|
||||||
taskSeedSpacedAuth,
|
taskSeedSpacedAuth,
|
||||||
taskSeedEnableSubspaces,
|
taskSeedEnableSubspaces,
|
||||||
|
taskRegistryApiKeys,
|
||||||
taskSeedCertrelay,
|
taskSeedCertrelay,
|
||||||
taskSeedNacho,
|
taskSeedNacho,
|
||||||
taskSetPassword,
|
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_SPACED_RPC_PASSWORD: spacedAuth.password,
|
||||||
SUBS_PROVER_ENDPOINT: `http://127.0.0.1:${SUBSPACES_PROVER_PORT}`,
|
SUBS_PROVER_ENDPOINT: `http://127.0.0.1:${SUBSPACES_PROVER_PORT}`,
|
||||||
SUBS_REGISTRY_ENDPOINT: `http://127.0.0.1:${SUBSPACES_REGISTRY_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,
|
HOME: SUBSPACES_DATA_DIR,
|
||||||
RUST_LOG: 'subs=info,subs_prover=info,registry_server=info',
|
RUST_LOG: 'subs=info,subs_prover=info,registry_server=info',
|
||||||
...subsAuthEnv,
|
...subsAuthEnv,
|
||||||
@@ -181,6 +179,9 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
|||||||
REGISTRY_SERVER_PORT: String(SUBSPACES_REGISTRY_PORT),
|
REGISTRY_SERVER_PORT: String(SUBSPACES_REGISTRY_PORT),
|
||||||
HOME: SUBSPACES_REGISTRY_DIR,
|
HOME: SUBSPACES_REGISTRY_DIR,
|
||||||
RUST_LOG: 'registry_server=info',
|
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(
|
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 PATH=/root/.cargo/bin:/data/bin:/usr/local/bin:/usr/bin:/bin',
|
||||||
"export PS1='spaces:\\w$ '",
|
"export PS1='spaces:\\w$ '",
|
||||||
`alias spaces='space-cli --chain ${SPACED_CHAIN} --rpc-user "$SPACED_RPC_USER" --rpc-password "$SPACED_RPC_PASSWORD" '`,
|
`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
|
// 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
|
// 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
|
// 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.
|
// depends on prior IDs and we can't easily express the union here.
|
||||||
const withSubspaces = (chain: any): any => {
|
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
|
let c = chain
|
||||||
.addOneshot('subspaces-dirs', {
|
.addOneshot('subspaces-dirs', {
|
||||||
subcontainer: subspacesSub,
|
subcontainer: subspacesSub,
|
||||||
|
|||||||
@@ -14,19 +14,19 @@ export const manifest = setupManifest({
|
|||||||
volumes: ['main'],
|
volumes: ['main'],
|
||||||
images: {
|
images: {
|
||||||
spaces: {
|
spaces: {
|
||||||
source: { dockerTag: 'horologger/spaces:v0.2.1' },
|
source: { dockerTag: 'horologger/spaces:v0.3.1' },
|
||||||
arch: ['x86_64', 'aarch64'],
|
arch: ['x86_64', 'aarch64'],
|
||||||
},
|
},
|
||||||
subspaces: {
|
subspaces: {
|
||||||
source: { dockerTag: 'horologger/subs:v0.1.2' },
|
source: { dockerTag: 'horologger/subs:v0.1.2d' },
|
||||||
arch: ['x86_64', 'aarch64'],
|
arch: ['x86_64', 'aarch64'],
|
||||||
},
|
},
|
||||||
certrelay: {
|
certrelay: {
|
||||||
source: { dockerTag: 'horologger/certrelay:v0.2.5' },
|
source: { dockerTag: 'horologger/certrelay:v0.2.8' },
|
||||||
arch: ['x86_64', 'aarch64'],
|
arch: ['x86_64', 'aarch64'],
|
||||||
},
|
},
|
||||||
nacho: {
|
nacho: {
|
||||||
source: { dockerTag: 'horologger/nacho:v1.0.0' },
|
source: { dockerTag: 'horologger/nacho:v1.0.0b' },
|
||||||
arch: ['x86_64', 'aarch64'],
|
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
|
// 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
|
// 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
|
// 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'
|
import { IMPOSSIBLE, VersionInfo } from '@start9labs/start-sdk'
|
||||||
|
|
||||||
export const current = VersionInfo.of({
|
export const current = VersionInfo.of({
|
||||||
version: '0.2.1:1',
|
version: '0.3.1:0',
|
||||||
releaseNotes: {
|
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: {
|
migrations: {
|
||||||
up: async ({ effects }) => {},
|
up: async ({ effects }) => {},
|
||||||
|
|||||||
Reference in New Issue
Block a user