How an operator of a fresh deployment creates the first PLATFORM-class
credential — the identity used to create tenants and delegate tenant-scoped
keys — without direct SQL. This is the sanctioned path the CLI's god-key
refusal points at: under OMNI_DB_ENFORCEMENT=on no data-plane god key can be
minted, so platform authority starts here.
One converged triple in the auth plane:
| Row | Purpose |
|---|---|
principals |
Stable operator identity (--subject, unique) |
platform_api_keys |
Platform key metadata (--key-name, unique) |
auth_credentials (credential_class='platform') |
The hash-lookup index row the API authenticates against |
The rows satisfy every invariant the auth plane checks at resolution time
(matching key hash, principal, and scopes; no tenant bindings; active status).
After writing, the command verifies the credential in-process through the
same AuthBootstrapService lookup the API uses — no running API is required.
Run server-side, wherever the deployment's database is reachable:
# Explicit URL (placeholders — substitute your own values):
bun scripts/bootstrap-platform-credential.ts --url postgres://DB_USER:DB_PASSWORD@DB_HOST:5432/omni
# Or rely on the environment the API itself uses:
DATABASE_URL=postgres://DB_USER:DB_PASSWORD@DB_HOST:5432/omni \
bun scripts/bootstrap-platform-credential.tsUseful options (see --help for all):
| Flag | Default | Meaning |
|---|---|---|
--subject |
platform-operator |
Principal subject (stable identity) |
--key-name |
platform-operator |
platform_api_keys.name |
--scope |
platform:* |
Repeatable; scopes carried by the credential |
--rotate |
off | Replace the credential material for the same key |
--acknowledge-legacy-keys |
off | Proceed past the legacy god-key worklist |
The plaintext secret (omni_sk_...) is printed exactly once to stdout and
is never stored, logged, or included in the JSON report. Save it immediately —
recovery is --rotate, which invalidates the old secret.
- Re-run = converge. Running again with the same
--subject/--key-namevalidates the existing triple and reportsconverged. No duplicate principals, keys, or credentials are ever created; no new secret is printed. - Rotation is explicit.
--rotategenerates new material for the same principal and key row and re-verifies. The previous secret stops resolving immediately. - Drift is fail-closed. If the stored rows disagree (tampered scopes,
missing index row, mismatched hash), the run reports
blockedwith redacted reasons and writes nothing. Fix deliberately — usually with--rotate. A key bound to a different principal, or a revoked key, is never rebound or resurrected; choose a new--key-nameinstead.
If the deployment still has legacy api_keys rows with the * scope or no
instance restriction, the command surfaces them through the report-only
classification worklist and stops (exit code 2). Those keys require an
explicit owner + purpose decision; the bootstrap never converts, promotes, or
revokes them. After reviewing the worklist, either revoke/scope the legacy
keys or re-run with --acknowledge-legacy-keys to proceed while leaving them
untouched.
Use the platform credential against the platform control plane to create
tenants, attach memberships, and issue tenant root keys — the delegation chain
the enforcement world expects. The control plane is only mounted when
OMNI_MULTITENANCY_ENABLED=true; without it every /api/v2/platform/**
request returns 404.
The omni tenants command group wraps the whole surface (tenants create,
tenants memberships add, tenants keys issue-root, …); every command takes
an audited --reason. On the raw API, mutations carry a reason in the JSON
body (for example POST /api/v2/platform/tenants), while the audited read
endpoints take an x-platform-reason header instead.
Day-to-day work should use tenant-scoped keys delegated from this credential, not the platform credential itself.
| Code | Meaning |
|---|---|
| 0 | created / converged / rotated, and in-process verification passed |
| 1 | usage or connection error |
| 2 | legacy god-key worklist requires an explicit decision (nothing written) |
| 3 | blocked: drift or identity conflict (nothing written) |
| 4 | rows written but in-process verification failed — investigate |