Skip to content

fix(docker): create the first admin declaratively on compose deploys - #175

Open
shani-singh1 wants to merge 1 commit into
oblien:mainfrom
shani-singh1:fix/docker-first-admin-bootstrap
Open

fix(docker): create the first admin declaratively on compose deploys#175
shani-singh1 wants to merge 1 commit into
oblien:mainfrom
shani-singh1:fix/docker-first-admin-bootstrap

Conversation

@shani-singh1

Copy link
Copy Markdown

Problem

On a fresh Docker Compose deployment there's no supported way to create the first admin through the web flow. Public sign-up is disabled on self-hosted instances, and the loopback bootstrap shortcut in the sign-up guard never fires because the request reaches the API from a Docker-bridge IP (e.g. 172.18.x.x) rather than 127.0.0.1, so it always returns SIGNUP_DISABLED. openship up's interactive wizard has no Compose equivalent.

Fixes #138.

Approach

This follows the direction discussed in #138 (declarative credentials at start, endorsed by @Hydralerne) and reuses the exact mechanism the CLI already relies on — the internal-token-gated, one-shot POST /api/system/bootstrap-admin endpoint. Public sign-up is not touched.

An optional one-shot init-admin Compose service runs once the API is healthy and creates the first admin from credentials the operator declares in .env:

OPENSHIP_ADMIN_NAME=Admin
OPENSHIP_ADMIN_EMAIL=admin@example.com
OPENSHIP_ADMIN_PASSWORD=a-strong-password   # 8-128 chars

Properties:

  • Opt-in / no-op by default — if the admin vars are unset the service prints a hint and exits 0, so nothing changes for operators who create the admin another way. depends_on is one-directional; no other service waits on it.
  • No new public surface — it POSTs to the same invite-only bootstrap endpoint the CLI wizard uses, authenticated with the existing INTERNAL_TOKEN. It does not enable public registration.
  • Idempotentbootstrap-admin returns 409 once an admin exists, which the script treats as success, so re-running docker compose up never mutates an existing admin.

Changes

  • scripts/docker-init-admin.sh — the bootstrap script (POSIX sh; JSON body is escaped for embedded quotes/backslashes).
  • docker-compose.yml — the init-admin service (pinned curlimages/curl, mounts the script read-only, waits for api health, then exits).
  • .env.example — documents the optional OPENSHIP_ADMIN_* vars.
  • docs/installation.md — a "Create the first admin" note in the Docker section.

Testing

  • docker compose config renders cleanly; init-admin receives INTERNAL_TOKEN and OPENSHIP_API_URL.
  • sh -n / bash -n syntax-check pass.
  • Ran the script against a mock of the bootstrap-admin contract covering every branch: vars-unset skip, successful create (incl. a password with embedded " and \ to confirm JSON escaping), idempotent 409 re-run, 401 wrong-token, missing-INTERNAL_TOKEN abort, and unreachable-API. Success/skip paths exit 0; error paths exit non-zero without blocking the rest of the stack.

Notes for reviewers

  • The service is intentionally non-blocking for the rest of the stack (restart: "no", nothing depends on it), so an error state surfaces as an Exited (1) container without affecting the API/dashboard.
  • No application/TypeScript code changes — this only wires up an existing endpoint for the Compose path.

A fresh `docker compose up` had no supported way to create the first
administrator through the web flow: public sign-up is disabled, and the
loopback bootstrap shortcut never fires because the request reaches the
API from a Docker-bridge IP rather than 127.0.0.1 (issue oblien#138).

Add an optional one-shot `init-admin` service that calls the existing
internal-token-gated `POST /api/system/bootstrap-admin` endpoint — the
same path the CLI wizard uses — with credentials the operator declares
in `.env` (`OPENSHIP_ADMIN_NAME` / `_EMAIL` / `_PASSWORD`).

- No-op unless the admin vars are set, so nothing changes for operators
  who create the admin another way.
- Does not enable public sign-up; reuses the invite-only bootstrap path.
- Idempotent: bootstrap-admin returns 409 once an admin exists, which the
  script treats as success, so re-running compose never mutates an admin.

Closes oblien#138.
@shani-singh1

Copy link
Copy Markdown
Author

Rebasing this on the latest main and wanted to check direction before I push an update, since a lot changed here since I opened it.

Two things I noticed:

  1. openship up --compose --non-interactive (with --admin-password / OPENSHIP_ADMIN_PASSWORD) now bootstraps the first admin via the internal token — which is the same declarative-credentials idea this PR implements, but done in the CLI. If the CLI is the intended path for first-admin on compose, this PR's separate init-admin service is probably redundant and I'm happy to close it.

  2. The compose stacks were split since I opened this (root docker-compose.yml → SaaS / from-source control plane; self-hosting → docker/docker-compose.yml). My init-admin service auto-merged into the root file, which is the wrong target now — public signup is on there, so it doesn't belong.

Where I'd like your read: is there still a gap for raw docker compose -f docker/docker-compose.yml up used without the CLI (CI / GitOps / anyone not running openship up)? For that path #138 still seems to bite — the interactive message and the install docs tell users to "register the first account in the dashboard," but on the split-container stack the dashboard proxies the signup to api:4000, so the API sees the dashboard's bridge IP rather than loopback and returns SIGNUP_DISABLED (matching the original #138 report).

If you'd like, I can either:

  • (a) re-target this to add the declarative init-admin to docker/docker-compose.yml for the raw-compose path, or
  • (b) drop the service and instead send a small docs fix pointing raw-compose users to openship up --compose --non-interactive, or
  • (c) just close this if openship up --compose is meant to be the one and only way.

Happy to go whichever way you prefer — just don't want to push a duplicate of what the CLI now does.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Fresh Docker deployment cannot create first admin (SIGNUP_DISABLED) because bootstrap request comes from Docker bridge instead of loopback

1 participant