diff --git a/README.md b/README.md
index e666c2d..5b8c68b 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,7 @@ auto-connects to it — see [Installation and First-Run Flow](#installation-and-
| Field | Value |
| --- | --- |
-| Image | `docker.io/spacesops/spacesops:v1.0.0` |
+| Image | `docker.io/spacesops/spacesops:v1.0.1` |
| Base | `node:20-alpine` |
| Architectures | `x86_64` (linux/amd64), `aarch64` (linux/arm64) |
| Entrypoint | The image's `/app/docker-entrypoint.sh` is **kept**. StartOS runs `/app/docker-entrypoint.sh node server.js` as the daemon. |
@@ -139,7 +139,7 @@ volume is **not** part of this package's backup.
and sync Spaces before (or alongside) SpacesOps; the service restarts until
those credentials are available.
2. **The admin password is fixed and well-known.** Admin Basic Auth is hardcoded
- in the v1.0.0 image as `admin` / `Whatever!` with **no environment override**.
+ in the v1.0.1 image as `admin` / `Whatever!` with **no environment override**.
It cannot be changed without rebuilding the image. **Keep this service private
(Tor-only) and never expose its admin routes to the public internet** — the
admin area can run SQL and manage tenants. See **Show Admin Credentials**.
@@ -151,7 +151,11 @@ volume is **not** part of this package's backup.
5. **The SUBS backend is not auto-wired to the Spaces subs daemon in v1.** The
Spaces service's subs daemon listens on 7777 with a different API/auth than
the SUBS endpoint (port 7244) SpacesOps expects, so SUBS must be configured
- manually if used.
+ manually if used. StartOS always sets `NODE_EXTRA_CA_CERTS` to the StartOS
+ root CA (fetched via `sdk.getSslCertificate` for the SUBS `*.startos`
+ hostname when configured, otherwise `spaces.startos`) so outbound HTTPS to
+ same-host StartOS services succeeds and the v1.0.1 image entrypoint does not
+ crash on its broken `_SCRIPT_DIR` default.
## What Is Unchanged from Upstream
@@ -166,8 +170,8 @@ volume is **not** part of this package's backup.
```yaml
package_id: spacesops
-upstream_version: 1.0.0
-image: docker.io/spacesops/spacesops:v1.0.0
+upstream_version: 1.0.1
+image: docker.io/spacesops/spacesops:v1.0.1
architectures: [x86_64, aarch64]
volumes:
main: /data
@@ -196,6 +200,7 @@ optional_env_vars: # only set when configured via configure-platform
- SUBS_URI_VALUE
- SUBS_RPC_USER
- SUBS_RPC_PASSWORD
+ - NODE_EXTRA_CA_CERTS # /data/.startos/startos-root-ca.crt (always set)
admin_credentials:
username: admin
password: Whatever! # FIXED, baked into image, cannot be changed
diff --git a/assets/spaceswallet.svg b/assets/spaceswallet.svg
new file mode 100644
index 0000000..7df054a
--- /dev/null
+++ b/assets/spaceswallet.svg
@@ -0,0 +1,64 @@
+
+
+
+
diff --git a/icon.svg b/icon.svg
index 3a710e9..895ff4b 100644
--- a/icon.svg
+++ b/icon.svg
@@ -8,7 +8,7 @@
version="1.1"
id="svg1"
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
- sodipodi:docname="spacesops.svg"
+ sodipodi:docname="icon.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
@@ -25,7 +25,7 @@
inkscape:document-units="mm"
inkscape:zoom="0.5"
inkscape:cx="365"
- inkscape:cy="260"
+ inkscape:cy="452"
inkscape:window-width="1312"
inkscape:window-height="995"
inkscape:window-x="550"
@@ -40,14 +40,14 @@
id="layer1">
@
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:108.156px;font-family:Outfit;-inkscape-font-specification:'Outfit, @wght=400';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-variation-settings:'wght' 400;fill:#903369;fill-opacity:1;stroke:none;stroke-width:15.3291"
+ x="10.27656"
+ y="75.009308">@
diff --git a/spacesops.svg b/spacesops.svg
index 3a710e9..b51d91c 100644
--- a/spacesops.svg
+++ b/spacesops.svg
@@ -25,7 +25,7 @@
inkscape:document-units="mm"
inkscape:zoom="0.5"
inkscape:cx="365"
- inkscape:cy="260"
+ inkscape:cy="452"
inkscape:window-width="1312"
inkscape:window-height="995"
inkscape:window-x="550"
@@ -40,14 +40,14 @@
id="layer1">
@
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:108.156px;font-family:Outfit;-inkscape-font-specification:'Outfit, @wght=400';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-variation-settings:'wght' 400;fill:#903369;fill-opacity:1;stroke:none;stroke-width:15.3291"
+ x="10.27656"
+ y="75.009308">@
diff --git a/spaceswallet.svg b/spaceswallet.svg
new file mode 100644
index 0000000..907681d
--- /dev/null
+++ b/spaceswallet.svg
@@ -0,0 +1,64 @@
+
+
+
+
diff --git a/startos/main.ts b/startos/main.ts
index 468edad..055b899 100644
--- a/startos/main.ts
+++ b/startos/main.ts
@@ -1,3 +1,4 @@
+import { mkdir, writeFile } from 'fs/promises'
import { storeJson } from './fileModels/storeJson'
import { i18n } from './i18n'
import { sdk } from './sdk'
@@ -8,6 +9,10 @@ import {
SPACED_RPC_URL,
SPACED_WALLETLOAD_NAME,
spacesDataDir,
+ startOsHostnameFromUrl,
+ nodeExtraCaCertContainerPath,
+ nodeExtraCaCertVolumeSubpath,
+ SPACES_PACKAGE_ID,
uiPort,
} from './utils'
@@ -106,6 +111,23 @@ export const main = sdk.setupMain(async ({ effects }) => {
if (store.subsUser) env.SUBS_RPC_USER = store.subsUser
if (store.subsPassword) env.SUBS_RPC_PASSWORD = store.subsPassword
+ // v1.0.1's entrypoint sources setup-spacesops-env.sh with set -u. That file
+ // exports NODE_EXTRA_CA_CERTS using ${_SCRIPT_DIR}, but _SCRIPT_DIR is set on
+ // a separate non-export line the entrypoint never evals — startup crashes
+ // unless NODE_EXTRA_CA_CERTS is already set. Fetch the StartOS root CA for
+ // outbound *.startos HTTPS (SUBS_URI host when configured, else Spaces).
+ const caHostname =
+ (store.subsUrl && startOsHostnameFromUrl(store.subsUrl)) ||
+ `${SPACES_PACKAGE_ID}.startos`
+ const certs = await sdk.getSslCertificate(effects, [caHostname]).const()
+ const [rootCa] = certs.slice(-1)
+ await mkdir(sdk.volumes.main.subpath('.startos'), { recursive: true })
+ await writeFile(
+ sdk.volumes.main.subpath(nodeExtraCaCertVolumeSubpath),
+ rootCa,
+ )
+ env.NODE_EXTRA_CA_CERTS = nodeExtraCaCertContainerPath
+
return sdk.Daemons.of(effects).addDaemon('spacesops', {
subcontainer: sub,
exec: {
diff --git a/startos/manifest/index.ts b/startos/manifest/index.ts
index 98ce97e..69904ed 100644
--- a/startos/manifest/index.ts
+++ b/startos/manifest/index.ts
@@ -14,7 +14,7 @@ export const manifest = setupManifest({
volumes: ['main'],
images: {
spacesops: {
- source: { dockerTag: 'spacesops/spacesops:v1.0.0' },
+ source: { dockerTag: 'spacesops/spacesops:v1.0.1' },
arch: ['x86_64', 'aarch64'],
},
},
diff --git a/startos/utils.ts b/startos/utils.ts
index 212eda7..af2fc15 100644
--- a/startos/utils.ts
+++ b/startos/utils.ts
@@ -20,13 +20,34 @@ export const SPACES_PACKAGE_ID = 'spaces'
export const SPACED_RPC_URL = 'http://spaces.startos:7225'
export const SPACED_WALLETLOAD_NAME = 'main'
-// Admin Basic Auth is baked into the v1.0.0 image with NO env override. It
+// Admin Basic Auth is baked into the v1.0.1 image with NO env override. It
// cannot be changed without rebuilding the image. Surfaced (with a warning)
// via the "Show Admin Credentials" action.
export const ADMIN_USER = 'admin'
export const ADMIN_PASSWORD = 'Whatever!'
-// Optional-config defaults (see the "Configure Platform" action).
+// Where StartOS writes the StartOS root CA for outbound *.startos HTTPS (see
+// main.ts). Always injected as NODE_EXTRA_CA_CERTS — required on v1.0.1+ where
+// the image entrypoint's setup-spacesops-env.sh references _SCRIPT_DIR without
+// defining it under set -u.
+export const nodeExtraCaCertVolumeSubpath = '.startos/startos-root-ca.crt'
+export const nodeExtraCaCertContainerPath = `${dataDir}/.startos/startos-root-ca.crt`
+
+// Legacy aliases (same file path after rename).
+export const subsCaCertVolumeSubpath = nodeExtraCaCertVolumeSubpath
+export const subsCaCertContainerPath = nodeExtraCaCertContainerPath
+
+// Returns the hostname when url targets another StartOS service on this server
+// (e.g. https://spaces.startos:7777), otherwise null.
+export function startOsHostnameFromUrl(url: string): string | null {
+ try {
+ const hostname = new URL(url).hostname
+ return hostname.endsWith('.startos') ? hostname : null
+ } catch {
+ return null
+ }
+}
+
export const DEFAULT_OPERATOR_RELAY = 'wss://relay.primal.net'
export const DEFAULT_PLATFORM_MODE = 'prod'
export const DEFAULT_COINGECKO_TOKEN_COINS = 'bitcoin'
diff --git a/startos/versions/index.ts b/startos/versions/index.ts
index 06f2296..f480cb4 100644
--- a/startos/versions/index.ts
+++ b/startos/versions/index.ts
@@ -1,7 +1,8 @@
import { VersionGraph } from '@start9labs/start-sdk'
-import { v_1_0_0_0 } from './v1.0.0.0'
+import { v_1_0_1_0 } from './v1.0.1.0'
+import { v_1_0_1_1 } from './v1.0.1.1'
export const versionGraph = VersionGraph.of({
- current: v_1_0_0_0,
- other: [],
+ current: v_1_0_1_1,
+ other: [v_1_0_1_0],
})
diff --git a/startos/versions/v1.0.0.0.ts b/startos/versions/v1.0.0.0.ts
deleted file mode 100644
index f317ee6..0000000
--- a/startos/versions/v1.0.0.0.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { IMPOSSIBLE, VersionInfo } from '@start9labs/start-sdk'
-
-export const v_1_0_0_0 = VersionInfo.of({
- version: '1.0.0:0',
- releaseNotes: {
- en_US: `Initial StartOS package for SpacesOps (upstream v1.0.0).
-- Runs the SpacesOps web platform (Express + SQLite) from the prebuilt spacesops/spacesops:v1.0.0 image on x86_64 and aarch64.
-- Depends on the Spaces service and auto-connects to its spaced JSON-RPC at spaces.startos:7225 by reading the Spaces RPC credentials from the mounted Spaces volume.
-- Generates a Nostr operator keypair and a strong session secret on first install. "Show Operator Credentials" and "Import Operator Key" actions manage the keypair.
-- "Show Admin Credentials" surfaces the fixed, well-known admin login baked into the image, with a warning to keep the service private.
-- "Configure Platform" optionally sets the Nostr relay, theme mode, CoinGecko pricing, and SUBS backend.`,
- },
- migrations: {
- up: async ({ effects }) => {},
- down: IMPOSSIBLE,
- },
-})
diff --git a/startos/versions/v1.0.1.0.ts b/startos/versions/v1.0.1.0.ts
new file mode 100644
index 0000000..333c4b7
--- /dev/null
+++ b/startos/versions/v1.0.1.0.ts
@@ -0,0 +1,14 @@
+import { IMPOSSIBLE, VersionInfo } from '@start9labs/start-sdk'
+
+export const v_1_0_1_0 = VersionInfo.of({
+ version: '1.0.1:0',
+ releaseNotes: {
+ en_US: `Upstream upgrade to SpacesOps v1.0.1.
+- Bumps the image to spacesops/spacesops:v1.0.1 on x86_64 and aarch64.
+- No StartOS-side configuration, action, or migration changes; existing operator keypair, session secret, and Configure-Platform settings are preserved.`,
+ },
+ migrations: {
+ up: async ({ effects }) => {},
+ down: IMPOSSIBLE,
+ },
+})
diff --git a/startos/versions/v1.0.1.1.ts b/startos/versions/v1.0.1.1.ts
new file mode 100644
index 0000000..5a67bd6
--- /dev/null
+++ b/startos/versions/v1.0.1.1.ts
@@ -0,0 +1,13 @@
+import { IMPOSSIBLE, VersionInfo } from '@start9labs/start-sdk'
+
+export const v_1_0_1_1 = VersionInfo.of({
+ version: '1.0.1:1',
+ releaseNotes: {
+ en_US: `- Always sets NODE_EXTRA_CA_CERTS before the v1.0.1 image entrypoint runs, fetching the StartOS root CA via sdk.getSslCertificate (SUBS *.startos hostname when configured, otherwise spaces.startos). Fixes startup crash where setup-spacesops-env.sh references unset _SCRIPT_DIR under set -u.
+- Enables outbound HTTPS to same-host StartOS services from SpacesOps.`,
+ },
+ migrations: {
+ up: async ({ effects }) => {},
+ down: IMPOSSIBLE,
+ },
+})