Provisioning toolkit for self-hosted Hubs (Reticulum) deployments. Two subcommands:
bootstrap— generates Phoenix/Guardian/PostgREST secrets, an RSA permissions keypair, and thedocker-compose .envfile. Replaces a host-sideinstall.sh. Runs before the stack starts.seed— waits forreticulum/health, mints an admin token, uploads the default scene, sets the app logo + name. Runs after the stack is healthy.
Latest tag: ghcr.io/worph/hubs-seed:2.0.0.
The image bundles a CC0 default — Quaternius's Outdoor Market (~1.2 MB GLB, open-air plaza). Override any of it via env vars; everything is optional.
Generates secrets and writes them to a docker-compose .env. Idempotent: existing secrets are preserved across re-runs, but framework-passthrough vars (APP_DOMAIN, APP_PUBLIC_IP_DASH, ...) are always refreshed from the current environment.
docker run --rm \
-v /DATA/AppData/hubs:/data/hubs \
-v /DATA/AppData/casaos/apps/hubs:/data/compose \
-e APP_DOMAIN -e APP_PUBLIC_IP_DASH -e APP_PUBLIC_IPV4 \
-e APP_EMAIL -e PUID -e PGID \
ghcr.io/worph/hubs-seed:2.0.0 bootstrapWrites:
| Path | Contents |
|---|---|
/data/compose/.env |
docker-compose env file (framework vars + secrets) |
/data/hubs/perms.key.pem |
RSA private key (Reticulum permissions service) |
/data/hubs/perms.pub.pem |
RSA public key |
/data/hubs/config.toml.template |
Reticulum config template (bundled in image) |
Re-running on an existing install:
- preserves every secret already in
.env(DB password, Phoenix/Guardian/PostgREST keys); - preserves the existing keypair (regenerating would invalidate Guardian tokens);
- always refreshes
APP_DOMAIN/APP_PUBLIC_IP_DASH/APP_PUBLIC_IPV4/APP_EMAIL/PUID/PGID— these are passthroughs from the framework's shell env, and on redeploy the framework is the source of truth; - always rewrites
config.toml.template(so template fixes ship via image upgrades).
| Var | Default | Notes |
|---|---|---|
APP_DOMAIN |
"" |
Customer-facing host root (e.g. wisera.inojob.com). Written into .env so docker-compose's --env-file pass doesn't shadow it with empty at deploy time. |
APP_PUBLIC_IP_DASH |
"" |
Server public IPv4 with . → - (e.g. 185-194-219-244). Required for the Caddy hubs-<ip>.nip.io vhost the Cloudflare Worker rewrites Host to. |
APP_PUBLIC_IPV4 |
"" |
Same IP, dotted. |
APP_EMAIL |
"" |
Reticulum admin_email. Required by the seed step too. |
PUID / PGID |
1000 / 1000 |
Host UID/GID for the secrets and templates so the runtime container (which runs as PUID) can read them. |
HUBS_APP_DIR |
/data/hubs |
Override for CI/test only. |
HUBS_COMPOSE_DIR |
/data/compose |
Override for CI/test only. |
docker run --rm \
--network <hubs-stack-network> \
-v /path/to/secrets.env:/secrets.env:ro \
-e SECRETS_ENV_FILE=/secrets.env \
-e RET_INTERNAL_URL=https://hubs-reticulum:4000 \
-e RET_PUBLIC_URL=https://hubs.example.com \
-e APP_EMAIL=admin@example.com \
ghcr.io/worph/hubs-seed:2.0.0 seedThat's the whole content seed on a "turkey-style" Reticulum deployment (the standard Hubs-Foundation/reticulum image with TURKEY_MODE=1).
Calling the image without a subcommand defaults to
seed, for back-compat withpost-install-cmdblocks pinned to1.x.
- Polls
RET_INTERNAL_URL/healthuntil 200. - Mints a Guardian admin JWT using one of three auth sources (see below).
- Idempotency probe —
GET /api/v1/media/search?source=scene_listings&filter=default. If any entry exists, exits 0. - Uploads scene
.glb+ screenshot toPOST /api/v1/media. POST /api/v1/scenes(creates the Scene with the uploaded files).- INSERTs the SceneListing row via
/api/postgrest/scene_listingswithtags={"tags":["default"]}— that's the same queryHub.createuses to pick a default. - Uploads the bundled
logo.pngand writesimages.logo+translations.en.app-nameto/api/v1/app_configs.
Pick whichever fits your wiring. Tried in this order:
ADMIN_TOKEN— pre-minted Guardian JWT. Use this if you have your own minting flow.DASHBOARD_ACCESS_KEY+APP_EMAIL— callsPOST /api-internal/v1/make_auth_token_for_email(gated bydashboard_access_keyinconfig.toml, only enabled when Reticulum runs withTURKEY_MODE=1).DASHBOARD_ACCESS_KEY_FILE— same as above but read the key from a file path.SECRETS_ENV_FILE— same as above but parse the key out of a Reticulum-stylesecrets.env(looks forDASHBOARD_ACCESS_KEY=orturkeyCfg_DASHBOARD_ACCESS_KEY=).
Mode 4 is the easiest in a Yundera/CasaOS post-install-cmd: bind-mount the existing secrets.env and you're done.
| Var | Required | Default | Notes |
|---|---|---|---|
RET_INTERNAL_URL |
no | https://hubs-reticulum:4000 |
Container-network reticulum URL. TLS verification is disabled (self-signed). |
RET_PUBLIC_URL |
no | (none) | Public base URL. Used to rewrite the images.logo URL we store in app_configs so the browser can fetch it; fall back to the upload's reported origin. |
APP_EMAIL |
conditional | — | Required for token-mint modes (everything except ADMIN_TOKEN). Must match Reticulum's admin_email. |
ADMIN_TOKEN |
one of | — | Pre-minted Guardian JWT. |
DASHBOARD_ACCESS_KEY |
one of | — | Direct value of Reticulum's dashboard key. |
DASHBOARD_ACCESS_KEY_FILE |
one of | — | Path to a file containing the key. |
SECRETS_ENV_FILE |
one of | — | Path to a secrets.env-style file. |
SCENE_GLB_URL |
no | bundled assets/scene.glb |
Override the default scene model. |
SCENE_SCREENSHOT_URL |
conditional | bundled assets/scene.png (if present) |
Reticulum's DB schema requires a screenshot. Either set this env var or bundle assets/scene.png; otherwise the seeder errors out before creating the scene. |
SCENE_SPOKE_URL |
no | bundled assets/scene.spoke (if present) |
Override the Spoke project file. Optional — recommended so admins can re-edit the scene. |
SCENE_NAME |
no | World Lounge |
|
SCENE_DESCRIPTION |
no | "A relaxed lounge to gather in..." | |
SCENE_ATTRIBUTIONS |
no | "" |
Free-text shown in scene metadata. |
APP_LOGO_URL |
no | bundled assets/logo.png |
Override the app logo. |
APP_HOME_BACKGROUND_URL |
no | bundled assets/home_background.png |
Override the home-page hero image. |
APP_NAME |
no | Hubs |
Sets translations.en.app-name. |
The probe (scene_listings?filter=default) is the safety check. Once an admin has customized the default scene, hubs-seed will never overwrite it.
Under assets/:
scene.glb— Default 3D scene (currently World Lounge, a Spoke export).scene.spoke— Spoke project file. Admins can re-open this in Spoke to edit the scene.scene.png— Optional preview thumbnail for the scene listing. If absent, the listing has no thumbnail.logo.png— Default app logo.home_background.png— Hero image on the landing page (separate from the scene preview).
See assets/ATTRIBUTION.md for licensing details.
MIT