Compare commits
9
Commits
196abdb860
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b446cd5f69 | ||
|
|
073344d4d1 | ||
|
|
2b0224a678 | ||
|
|
89f4475e99 | ||
|
|
61cd3e679a | ||
|
|
1914ad3b6a | ||
|
|
0362388f7f | ||
|
|
5bd878ac2d | ||
|
|
b1d4890320 |
@@ -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 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 | |
|
||||||
@@ -125,7 +127,7 @@ mainnet-only.
|
|||||||
| Subspaces Prover | 8888 | HTTP | LAN / `.local` / Tor / clearnet (via StartOS) | `subs-prover` RISC Zero prover server (no GPU). Its own StartOS interface. Optional HTTP basic auth — off by default, gated by **Enable Subspaces Prover Auth**. When on, `SUBS_PROVER_BASIC_AUTH_USER`/`SUBS_PROVER_BASIC_AUTH_PASSWORD` are enforced. Only useful while Subspaces (and the prover toggle) is enabled. |
|
| Subspaces Prover | 8888 | HTTP | LAN / `.local` / Tor / clearnet (via StartOS) | `subs-prover` RISC Zero prover server (no GPU). Its own StartOS interface. Optional HTTP basic auth — off by default, gated by **Enable Subspaces Prover Auth**. When on, `SUBS_PROVER_BASIC_AUTH_USER`/`SUBS_PROVER_BASIC_AUTH_PASSWORD` are enforced. Only useful while Subspaces (and the prover toggle) is enabled. |
|
||||||
| Subspaces Registry | 8081 | HTTP | LAN / `.local` / Tor / clearnet (via StartOS) | `registry-server` for publishing/resolving handles (remapped from its upstream default 8080 to avoid colliding with the gotty terminal on 8080). Its own StartOS interface. Only useful while Subspaces is enabled. |
|
| Subspaces Registry | 8081 | HTTP | LAN / `.local` / Tor / clearnet (via StartOS) | `registry-server` for publishing/resolving handles (remapped from its upstream default 8080 to avoid colliding with the gotty terminal on 8080). Its own StartOS interface. Only useful while Subspaces is enabled. |
|
||||||
| Certrelay | 7778 | HTTP | LAN / `.local` / Tor / clearnet (via StartOS) | `certrelay` server (always on). Serves cryptographic proofs binding handles to owner keys. **Setup:** run **Configure Certrelay** and set `CERTRELAY_SELF_URL` to the publicly visible URL StartOS exposes for this interface. |
|
| Certrelay | 7778 | HTTP | LAN / `.local` / Tor / clearnet (via StartOS) | `certrelay` server (always on). Serves cryptographic proofs binding handles to owner keys. **Setup:** run **Configure Certrelay** and set `CERTRELAY_SELF_URL` to the publicly visible URL StartOS exposes for this interface. |
|
||||||
| Nacho | 8082 | HTTP | LAN / `.local` / Tor / clearnet (via StartOS) | nacho Expo dev server (always on). The ignore list is read at runtime from `/data/nacho/ignore_names.txt` (managed by **Configure Nacho**). `EXPO_PUBLIC_API_BASE_URL` is derived dynamically from the Subs API StartOS interface (`sdk.serviceInterface.getOwn('subs-api').const()`), preferring the `.local` URL. Falls back to `http://127.0.0.1:7777` only if no address info is available. |
|
| Nacho | 8082 | HTTP | LAN / `.local` / Tor / clearnet (via StartOS) | nacho Expo dev server (always on). The ignore list is read at runtime from `/data/nacho/ignore_names.txt` (managed by **Configure Nacho**). `EXPO_PUBLIC_API_BASE_URL` is derived dynamically from the Subs API StartOS interface (walked off its host: `sdk.host.getOwn(effects, 'subspaces-multi', h => h?.bindings[7777]?.interfaces['subs-api']).const()`), preferring the `.local` URL. Falls back to `http://127.0.0.1:7777` only if no address info is available. |
|
||||||
| Spaces API (spaced RPC) | 7225 | HTTP JSON-RPC | LAN / `.local` / Tor / clearnet (via StartOS) | spaced JSON-RPC, bound to `0.0.0.0` and exposed as the **Spaces API** interface. Static-cred-authenticated via `SPACED_RPC_USER` / `SPACED_RPC_PASSWORD` from `store.json.spacedAuth`. Internal clients still connect over loopback. |
|
| Spaces API (spaced RPC) | 7225 | HTTP JSON-RPC | LAN / `.local` / Tor / clearnet (via StartOS) | spaced JSON-RPC, bound to `0.0.0.0` and exposed as the **Spaces API** interface. Static-cred-authenticated via `SPACED_RPC_USER` / `SPACED_RPC_PASSWORD` from `store.json.spacedAuth`. Internal clients still connect over loopback. |
|
||||||
|
|
||||||
## Actions
|
## Actions
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
# TODO — bring Spaces from beta.9 to
|
# TODO — bring Spaces from beta.9 to beta.10
|
||||||
|
|
||||||
Consult the packaging guide as you go
|
Consult the packaging guide as you go
|
||||||
(`start-technologies/projects/start-sdk/docs/src/recipes.md` is the intent index). Remove items as you finish
|
(`start-technologies/projects/start-sdk/docs/src/recipes.md` is the intent index). Remove items as you finish
|
||||||
them, and add items when you defer work.
|
them, and add items when you defer work.
|
||||||
|
|
||||||
## The service
|
|
||||||
|
|
||||||
- [ ] Update @start9labs/start-sdk to the latest version.
|
|
||||||
|
|
||||||
## Docs
|
## Docs
|
||||||
|
|
||||||
- [ ] Write `README.md` (per `start-technologies/projects/start-sdk/docs/src/writing-readmes.md`).
|
- [ ] Write `README.md` (per `start-technologies/projects/start-sdk/docs/src/writing-readmes.md`).
|
||||||
@@ -16,8 +12,17 @@ them, and add items when you defer work.
|
|||||||
|
|
||||||
## Build, test, ship
|
## Build, test, ship
|
||||||
|
|
||||||
- [ ] First test build: `make` (or `start-cli s9pk pack`); fix any `tsc` / pack errors.
|
- [ ] Build for arm (`ARCHES := x86 arm` in the `Makefile`). All four images declare
|
||||||
- [ ] Install on a StartOS box and verify the service runs (and is reachable, once it
|
`aarch64` in the manifest and every tag publishes an arm64 variant, so the
|
||||||
exposes an interface).
|
package currently advertises support it never ships. Needs an arm box to verify.
|
||||||
|
- [x] First test build: `make` (or `start-cli s9pk pack`); fix any `tsc` / pack errors.
|
||||||
|
- [x] Install on a StartOS box and verify the service runs — done for 0.2.1:0 on
|
||||||
|
natural-dean.local: all daemons up, nacho's `EXPO_PUBLIC_API_BASE_URL` resolved
|
||||||
|
through the subs-api host rather than the loopback fallback.
|
||||||
|
- [ ] Verify the Bitcoin RPC task on a *fresh* install (`accept`/`set` input under SDK
|
||||||
|
2.0). `taskBtcAuth` returns early when `store.btcAuth` exists, so an update over
|
||||||
|
an existing install never exercises it.
|
||||||
|
- [ ] `npx prettier --write startos` — 9 files predate the current prettier config and
|
||||||
|
fail `--check` (untouched by the SDK bump, so left out of that diff).
|
||||||
- [ ] Backup / restore sanity check.
|
- [ ] Backup / restore sanity check.
|
||||||
- [ ] Review the README and instructions one more time against actual behavior.
|
- [ ] Review the README and instructions one more time against actual behavior.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Generated
+1309
-13
File diff suppressed because it is too large
Load Diff
+5
-2
@@ -6,13 +6,16 @@
|
|||||||
"check": "tsc --noEmit"
|
"check": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@start9labs/start-sdk": "1.5.1"
|
"@start9labs/start-sdk": "2.0.9"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"@start9labs/start-sdk": "$@start9labs/start-sdk"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.19.0",
|
"@types/node": "^22.19.0",
|
||||||
"@vercel/ncc": "^0.38.4",
|
"@vercel/ncc": "^0.38.4",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^6.0.3"
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"trailingComma": "all",
|
"trailingComma": "all",
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
# ** Plumbing. DO NOT EDIT **.
|
|
||||||
# This file is imported by ./Makefile. Make edits there
|
|
||||||
|
|
||||||
PACKAGE_ID := $(shell awk -F"'" '/id:/ {print $$2}' startos/manifest/index.ts)
|
|
||||||
INGREDIENTS := $(shell start-cli s9pk list-ingredients 2>/dev/null)
|
|
||||||
# Resolve the actual git dir so this works inside git worktrees, where .git
|
|
||||||
# is a file pointing at <main>/.git/worktrees/<name> rather than a directory.
|
|
||||||
GIT_DIR := $(shell git rev-parse --git-dir 2>/dev/null)
|
|
||||||
GIT_DEPS := $(if $(GIT_DIR),$(GIT_DIR)/HEAD $(GIT_DIR)/index)
|
|
||||||
ARCHES ?= x86 arm riscv
|
|
||||||
TARGETS ?= arches
|
|
||||||
ifdef VARIANT
|
|
||||||
BASE_NAME := $(PACKAGE_ID)_$(VARIANT)
|
|
||||||
else
|
|
||||||
BASE_NAME := $(PACKAGE_ID)
|
|
||||||
endif
|
|
||||||
|
|
||||||
.PHONY: all arches aarch64 x86_64 riscv64 arm arm64 x86 riscv arch/* clean install check-deps check-init package ingredients
|
|
||||||
.DELETE_ON_ERROR:
|
|
||||||
.SECONDARY:
|
|
||||||
|
|
||||||
define SUMMARY
|
|
||||||
@manifest=$$(start-cli s9pk inspect $(1) manifest); \
|
|
||||||
size=$$(du -h $(1) | awk '{print $$1}'); \
|
|
||||||
title=$$(printf '%s' "$$manifest" | jq -r .title); \
|
|
||||||
version=$$(printf '%s' "$$manifest" | jq -r .version); \
|
|
||||||
arches=$$(printf '%s' "$$manifest" | jq -r '[.images[].arch // []] | flatten | unique | join(", ")'); \
|
|
||||||
sdkv=$$(printf '%s' "$$manifest" | jq -r .sdkVersion); \
|
|
||||||
gitHash=$$(printf '%s' "$$manifest" | jq -r .gitHash | sed -E 's/(.*-modified)$$/\x1b[0;31m\1\x1b[0m/'); \
|
|
||||||
printf "\n"; \
|
|
||||||
printf "\033[1;32m✅ Build Complete!\033[0m\n"; \
|
|
||||||
printf "\n"; \
|
|
||||||
printf "\033[1;37m📦 $$title\033[0m \033[36mv$$version\033[0m\n"; \
|
|
||||||
printf "───────────────────────────────\n"; \
|
|
||||||
printf " \033[1;36mFilename:\033[0m %s\n" "$(1)"; \
|
|
||||||
printf " \033[1;36mSize:\033[0m %s\n" "$$size"; \
|
|
||||||
printf " \033[1;36mArch:\033[0m %s\n" "$$arches"; \
|
|
||||||
printf " \033[1;36mSDK:\033[0m %s\n" "$$sdkv"; \
|
|
||||||
printf " \033[1;36mGit:\033[0m %s\n" "$$gitHash"; \
|
|
||||||
echo ""
|
|
||||||
endef
|
|
||||||
|
|
||||||
all: $(TARGETS)
|
|
||||||
|
|
||||||
arches: $(ARCHES)
|
|
||||||
|
|
||||||
universal: $(BASE_NAME).s9pk
|
|
||||||
$(call SUMMARY,$<)
|
|
||||||
|
|
||||||
arch/%: $(BASE_NAME)_%.s9pk
|
|
||||||
$(call SUMMARY,$<)
|
|
||||||
|
|
||||||
x86 x86_64: arch/x86_64
|
|
||||||
arm arm64 aarch64: arch/aarch64
|
|
||||||
riscv riscv64: arch/riscv64
|
|
||||||
|
|
||||||
$(BASE_NAME).s9pk: $(INGREDIENTS) $(GIT_DEPS)
|
|
||||||
@$(MAKE) --no-print-directory ingredients
|
|
||||||
@echo " Packing '$@'..."
|
|
||||||
start-cli s9pk pack -o $@
|
|
||||||
|
|
||||||
$(BASE_NAME)_%.s9pk: $(INGREDIENTS) $(GIT_DEPS)
|
|
||||||
@$(MAKE) --no-print-directory ingredients
|
|
||||||
@echo " Packing '$@'..."
|
|
||||||
start-cli s9pk pack --arch=$* -o $@
|
|
||||||
|
|
||||||
ingredients: $(INGREDIENTS)
|
|
||||||
@echo " Re-evaluating ingredients..."
|
|
||||||
|
|
||||||
install: | check-deps check-init
|
|
||||||
@HOST=$$(awk -F'/' '/^host:/ {print $$3}' ~/.startos/config.yaml); \
|
|
||||||
if [ -z "$$HOST" ]; then \
|
|
||||||
echo "Error: You must define \"host: http://server-name.local\" in ~/.startos/config.yaml"; \
|
|
||||||
exit 1; \
|
|
||||||
fi; \
|
|
||||||
S9PK=$$(ls -t *.s9pk 2>/dev/null | head -1); \
|
|
||||||
if [ -z "$$S9PK" ]; then \
|
|
||||||
echo "Error: No .s9pk file found. Run 'make' first."; \
|
|
||||||
exit 1; \
|
|
||||||
fi; \
|
|
||||||
printf "\n🚀 Installing %s to %s ...\n" "$$S9PK" "$$HOST"; \
|
|
||||||
start-cli package install -s "$$S9PK"
|
|
||||||
|
|
||||||
publish: | all
|
|
||||||
@REGISTRY=$$(awk -F'/' '/^registry:/ {print $$3}' ~/.startos/config.yaml); \
|
|
||||||
if [ -z "$$REGISTRY" ]; then \
|
|
||||||
echo "Error: You must define \"registry: https://my-registry.tld\" in ~/.startos/config.yaml"; \
|
|
||||||
exit 1; \
|
|
||||||
fi; \
|
|
||||||
S3BASE=$$(awk -F'/' '/^s9pk-s3base:/ {print $$3}' ~/.startos/config.yaml); \
|
|
||||||
if [ -z "$$S3BASE" ]; then \
|
|
||||||
echo "Error: You must define \"s3base: https://s9pks.my-s3-bucket.tld\" in ~/.startos/config.yaml"; \
|
|
||||||
exit 1; \
|
|
||||||
fi; \
|
|
||||||
command -v s3cmd >/dev/null || \
|
|
||||||
(echo "Error: s3cmd not found. It must be installed to publish using s3." && exit 1); \
|
|
||||||
printf "\n🚀 Publishing to %s; indexing on %s ...\n" "$$S3BASE" "$$REGISTRY"; \
|
|
||||||
for s9pk in *.s9pk; do \
|
|
||||||
age=$$(( $$(date +%s) - $$(stat -c %Y "$$s9pk") )); \
|
|
||||||
if [ "$$age" -gt 3600 ]; then \
|
|
||||||
printf "\033[1;33m⚠️ %s is %d minutes old. Publish anyway? [y/N] \033[0m" "$$s9pk" "$$((age / 60))"; \
|
|
||||||
read -r ans; \
|
|
||||||
case "$$ans" in [yY]*) ;; *) echo "Skipping $$s9pk"; continue ;; esac; \
|
|
||||||
fi; \
|
|
||||||
start-cli s9pk publish "$$s9pk"; \
|
|
||||||
done
|
|
||||||
|
|
||||||
check-deps:
|
|
||||||
@command -v start-cli >/dev/null || \
|
|
||||||
(echo "Error: start-cli not found. Please see https://docs.start9.com/latest/developer-guide/sdk/installing-the-sdk" && exit 1)
|
|
||||||
@command -v npm >/dev/null || \
|
|
||||||
(echo "Error: npm not found. Please install Node.js and npm." && exit 1)
|
|
||||||
|
|
||||||
check-init:
|
|
||||||
@if [ ! -f ~/.startos/developer.key.pem ]; then \
|
|
||||||
echo "Initializing StartOS developer environment..."; \
|
|
||||||
start-cli init-key; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
javascript/index.js: $(shell find startos -type f) tsconfig.json node_modules
|
|
||||||
npm run check
|
|
||||||
npm run build
|
|
||||||
|
|
||||||
node_modules: package-lock.json
|
|
||||||
npm ci
|
|
||||||
|
|
||||||
package-lock.json: package.json
|
|
||||||
npm i
|
|
||||||
|
|
||||||
clean:
|
|
||||||
@echo "Cleaning up build artifacts..."
|
|
||||||
@rm -rf $(PACKAGE_ID).s9pk $(PACKAGE_ID)_x86_64.s9pk $(PACKAGE_ID)_aarch64.s9pk $(PACKAGE_ID)_riscv64.s9pk javascript node_modules
|
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ export const taskBtcAuth = sdk.setupOnInit(async (effects) => {
|
|||||||
reason: i18n('Spaces needs RPC credentials in Bitcoin'),
|
reason: i18n('Spaces needs RPC credentials in Bitcoin'),
|
||||||
input: {
|
input: {
|
||||||
kind: 'partial',
|
kind: 'partial',
|
||||||
value: { username, password },
|
accept: [{ username, password }],
|
||||||
|
set: { username, password },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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',
|
||||||
|
),
|
||||||
|
})
|
||||||
|
})
|
||||||
+38
-13
@@ -105,21 +105,21 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
|||||||
readonly: false,
|
readonly: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const spacedSub = await sdk.SubContainer.of(
|
const spacedSub = sdk.SubContainer.of(
|
||||||
effects,
|
effects,
|
||||||
{ imageId: 'spaces' },
|
{ imageId: 'spaces' },
|
||||||
mounts,
|
mounts,
|
||||||
'spaced-sub',
|
'spaced-sub',
|
||||||
)
|
)
|
||||||
|
|
||||||
const termSub = await sdk.SubContainer.of(
|
const termSub = sdk.SubContainer.of(
|
||||||
effects,
|
effects,
|
||||||
{ imageId: 'spaces' },
|
{ imageId: 'spaces' },
|
||||||
mounts,
|
mounts,
|
||||||
'terminal-sub',
|
'terminal-sub',
|
||||||
)
|
)
|
||||||
|
|
||||||
const subspacesSub = await sdk.SubContainer.of(
|
const subspacesSub = sdk.SubContainer.of(
|
||||||
effects,
|
effects,
|
||||||
{ imageId: 'subspaces' },
|
{ imageId: 'subspaces' },
|
||||||
mounts,
|
mounts,
|
||||||
@@ -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,9 +179,12 @@ 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 = await sdk.SubContainer.of(
|
const certrelaySub = sdk.SubContainer.of(
|
||||||
effects,
|
effects,
|
||||||
{ imageId: 'certrelay' },
|
{ imageId: 'certrelay' },
|
||||||
mounts,
|
mounts,
|
||||||
@@ -206,7 +207,7 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
|||||||
RUST_LOG: 'info',
|
RUST_LOG: 'info',
|
||||||
}
|
}
|
||||||
|
|
||||||
const nachoSub = await sdk.SubContainer.of(
|
const nachoSub = sdk.SubContainer.of(
|
||||||
effects,
|
effects,
|
||||||
{ imageId: 'nacho' },
|
{ imageId: 'nacho' },
|
||||||
mounts,
|
mounts,
|
||||||
@@ -217,11 +218,17 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
|||||||
// tracks the actual host:port StartOS exposes (instead of a hardcoded
|
// tracks the actual host:port StartOS exposes (instead of a hardcoded
|
||||||
// domain). Prefer the mdns/.local URL — matches how a LAN browser reaches
|
// domain). Prefer the mdns/.local URL — matches how a LAN browser reaches
|
||||||
// the device — then fall back to any non-local URL, then loopback.
|
// the device — then fall back to any non-local URL, then loopback.
|
||||||
// `.const(effects)` makes this reactive: if the interface address changes
|
// The interface is reached by walking its host ('subspaces-multi', the
|
||||||
// (clearnet enabled, Tor added, etc.), the service restarts and nacho
|
// MultiHost that bound 7777 in interfaces.ts). `.const()` makes this
|
||||||
// picks up the new URL.
|
// reactive, and the `map` selector narrows the watch to this one interface:
|
||||||
const subsApiIf = await sdk.serviceInterface
|
// if its address changes (clearnet enabled, Tor added, etc.), the service
|
||||||
.getOwn(effects, 'subs-api')
|
// restarts and nacho picks up the new URL.
|
||||||
|
const subsApiIf = await sdk.host
|
||||||
|
.getOwn(
|
||||||
|
effects,
|
||||||
|
'subspaces-multi',
|
||||||
|
(host) => host?.bindings[SUBSPACES_UI_PORT]?.interfaces['subs-api'],
|
||||||
|
)
|
||||||
.const()
|
.const()
|
||||||
const subsApiCandidateUrls = subsApiIf?.addressInfo
|
const subsApiCandidateUrls = subsApiIf?.addressInfo
|
||||||
? [
|
? [
|
||||||
@@ -250,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
|
||||||
@@ -363,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,30 +14,22 @@ 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'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
alerts: {
|
|
||||||
install: null,
|
|
||||||
update: null,
|
|
||||||
uninstall: null,
|
|
||||||
restore: null,
|
|
||||||
start: null,
|
|
||||||
stop: null,
|
|
||||||
},
|
|
||||||
dependencies: {
|
dependencies: {
|
||||||
bitcoind: {
|
bitcoind: {
|
||||||
description: depBitcoindDescription,
|
description: depBitcoindDescription,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { IMPOSSIBLE, VersionInfo } from '@start9labs/start-sdk'
|
||||||
|
|
||||||
|
export const current = VersionInfo.of({
|
||||||
|
version: '0.3.1:0',
|
||||||
|
releaseNotes: {
|
||||||
|
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).
|
||||||
|
|
||||||
|
Requires StartOS 0.4.0-beta.10 (\`start-sdk\` 2.0.9).`,
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
up: async ({ effects }) => {},
|
||||||
|
down: IMPOSSIBLE,
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { VersionGraph } from '@start9labs/start-sdk'
|
import { VersionGraph } from '@start9labs/start-sdk'
|
||||||
import { v_0_2_1_0 } from './v0.2.1.0'
|
import { current } from './current'
|
||||||
|
|
||||||
export const versionGraph = VersionGraph.of({
|
export const versionGraph = VersionGraph.of({
|
||||||
current: v_0_2_1_0,
|
current,
|
||||||
other: [],
|
other: [],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import { IMPOSSIBLE, VersionInfo } from '@start9labs/start-sdk'
|
|
||||||
|
|
||||||
export const v_0_2_1_0 = VersionInfo.of({
|
|
||||||
version: '0.2.1:0',
|
|
||||||
releaseNotes: {
|
|
||||||
en_US: `- **spaced image** updated to \`horologger/spaces:v0.2.1\` (from v0.0.9x).
|
|
||||||
- **Certrelay image** updated to \`horologger/certrelay:v0.2.5\` (from v0.2.3).`,
|
|
||||||
},
|
|
||||||
migrations: {
|
|
||||||
up: async ({ effects }) => {},
|
|
||||||
down: IMPOSSIBLE,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
+2
-9
@@ -1,11 +1,4 @@
|
|||||||
{
|
{
|
||||||
"include": ["startos/**/*.ts", "node_modules/**/startos"],
|
"extends": "@start9labs/start-sdk/tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"include": ["startos/**/*.ts", "node_modules/**/startos"]
|
||||||
"target": "ES2018",
|
|
||||||
"module": "CommonJS",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"strict": true,
|
|
||||||
"skipLibCheck": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user