# @supacloud/cli

Project-scoped CLI for SupaCloud users.

The only command-line executable is `supacloud`. The compiled server binary
installed at `/usr/local/bin/supacloud` is a separate managed-host installation.

Tool adapters import `@supacloud/cli/tool-runtime` for typed registration and
schema decoding. This side-effect-free entrypoint does not initialize the CLI.
Registration accepts unknown boundary input, decodes it before invoking the
typed callback, and validates the callback's result. Private `src/shared` imports
are not supported.

## Project Setup and Environment Profiles

For an existing SupaCloud application with a frontend, the shortest path is:

```sh
supacloud init
cp .env.supacloud.test.example .env.supacloud.test
supacloud env check
supacloud --env test deploy
```

`init` creates `supacloud.json` and non-secret environment examples without
overwriting an existing project configuration. `env list` and `env check` are
local-only, redact credentials, and provide stable JSON with `--json` for CI or
AI tooling. The real `.env.supacloud.*` files are added to `.gitignore`.
Fill in `SUPACLOUD_APPLICATION_ID` and the Management credentials in the
selected profile, and configure the application's current delivery build.
Standalone frontend deployment is no longer supported. Upgrade the CLI with
`npm install -g @supacloud/cli@latest` and upgrade the SupaCloud server together.
`deploy` checks the server protocol before building or uploading.

## Application Starter

```sh
supacloud app init --root ./orders --name orders
cd orders
bun install
bun run check
bun run dev
```

The initializer needs no platform credentials and never overwrites existing
projects. The default minimal template includes a health feature with a colocated
test, strict compilation, an Elysia service, SDK dependencies and explicit
development/test/staging/production environment wrappers. Select
`--template command` for the persistent governed-command reference, or `http` /
`edge` for their existing recipes. The compiler is a development dependency.
The demo binds to loopback and cannot run in staging/production. Production
identity, persistence and durable governance must be supplied by the application.
See [Application Starter](../../docs/application-starter.md) for the full contract.

`app dev` runs the project's `dev` script; `app watch` only compiles and watches.
`app dev --profile integration` requires a loopback database and a
`dev:integration` script, verifies database identity and the application role, and
never automatically migrates or seeds. The command recipe includes a durable host.
`app verify-plan --target <module>` lists focused single-file tests without
executing them; missing ownership or tests never falls back to the whole suite.

`app compile` and `app check` resolve `supacloud.config.ts` from `--root` (the
project directory). `app check` runs the complete compiler and artifact-drift
gate without writing; compile once and commit `generated/` before using it in CI.
Failed compilation preserves the previous generated artifacts.
Without a config file, an explicit `--root` retains the legacy source-directory
behavior.

### Application delivery

```sh
supacloud app plan --root . --format json
supacloud app build --root .
supacloud app upload --ref PROJECT --id orders --manifest_path generated/delivery/delivery.manifest.json
supacloud app configure --ref PROJECT --id orders --environment_id test --configuration_path configuration.json
supacloud app deploy --ref PROJECT --id orders --environment_id test --release_id RELEASE_SHA256 --configuration_id CONFIG_UUID --activation_id NEW_UUID --expected_activation_id absent
supacloud app status --ref PROJECT --id orders --environment_id test
supacloud app logs --ref PROJECT --id orders --environment_id test
supacloud app reconcile --ref PROJECT --id orders --environment_id test --release_id RELEASE_SHA256 --activation_id ACTIVATION_UUID
supacloud app rollback --ref PROJECT --id orders --environment_id test --release_id OLD_RELEASE_SHA256 --configuration_id CONFIG_UUID --activation_id NEW_UUID --expected_activation_id CURRENT_UUID
supacloud app retire --ref PROJECT --id orders --environment_id test --activation_id ACTIVATION_UUID
```

`plan` and `build` read `delivery` in `supacloud.config.ts`; `--root` is the
project directory, with sources defaulting to `src`. Plan writes no artifacts.
Its topology digest is not a release digest or deployment approval. Build needs
Bun and produces the compiler's immutable manifest beneath configured `outDir`.
Build, upload and configure only prepare artifacts or configuration, never deploy.

Remote actions delegate to `applications` and retain its original receipt,
identity validation, read-only and production-confirmation checks. Upload returns
the release ID; configuration writes use the existing JSON contract with an
explicit revision and expected revision. Deploy and rollback require an explicit
release, configuration, new activation ID and expected current activation ID.
Rollback activates the selected old release; it does not downgrade schema or
restore data. Server compatibility checks still apply.

Status returns the server's runtime/readiness report, not an inferred successful
deployment. An unknown mutation outcome remains unknown; reconcile the same
activation identity rather than blindly deploying again. These commands require
Management API credentials except local plan/build and authoring commands.

### Isolated Application Previews

```sh
supacloud app preview-plan --env test --id orders --environment_id test --release_id RELEASE_SHA256 --branch_ref preview-orders
supacloud app preview --env test --id orders --environment_id test --release_id RELEASE_SHA256 --configuration_id CONFIG_UUID --ttl_seconds 3600 --wait --timeout_seconds 300
supacloud app previews --env test --id orders --environment_id test
supacloud app preview-status --env test --id orders --environment_id test --preview_id PREVIEW_UUID --wait --timeout_seconds 300
supacloud app preview-reconcile --env test --id orders --environment_id test --preview_id PREVIEW_UUID
supacloud app preview-cleanup --env test --id orders --environment_id test --preview_id PREVIEW_UUID
```

The platform owns branch provisioning, queue/Storage namespaces, test Secrets,
configuration cloning, activation and runtime readiness. The CLI never performs
these operations through SSH or sequential client-side resource mutations.
Creation defaults to `schema_only`; copying source rows requires explicitly
selecting `--data_mode full_clone`. Creation requires an immutable source
configuration ID and verifies the branch release identity against the source
manifest. A `provisioning` receipt means accepted, not ready.

New previews use the platform's default 24-hour TTL. `--ttl_seconds` accepts 300
through 604800 seconds for creation or read-only planning. `expires_at` remains
in the public receipt; a plan has no deadline unless a TTL is requested. Receipt
expiry requires the matching platform cleanup contract (#1774), not the older
CLI-only lifecycle contract.

`--wait` on creation observes only the accepted Preview ID; it never repeats
creation. `preview-status --wait` continues observing that same ID. The observation
budget is 300 seconds by default and accepts 1 through 3600 seconds with
`--timeout_seconds`; creation's source verification and initial POST precede that
budget, while status includes its initial GET. Header and body share the remaining
request budget. Completed readiness ends waiting; a failed/cleaned receipt, failed
query or identity drift ends it with nonzero status. A wait timeout returns
`waiting.status: "timed_out"` with the last validated receipt and the exact
same-ID continuation in `reconciliation`. It does not mark provisioning failed,
retry a failed lifecycle GET, delete resources or promote an environment.

`preview-plan`, `previews` and `preview-status` are read-only. They never resume
persisted provisioning. Use `preview-reconcile` to explicitly resume a selected
Preview ID; it is classified as a write, including in read-only and production
profiles. The platform may clean eligible expired
previews, but never interrupts a serving preview or empties nonempty Storage
automatically. Explicit cleanup remains available. Failed previews return
nonzero with the validated receipt and cleanup state. Unknown create outcomes
are never retried: list existing previews and reconcile their receipts first.
No production deployment, database promotion or data rollback is implied.
Runtime readiness is not authenticated business-flow acceptance.

```gherkin
Feature: Platform-owned application previews
  Scenario: Read-only planning
    Given an explicit release and proposed branch ref
    When preview-plan runs in read-only mode
    Then only a GET plan request is sent
    And no preview is created

  Scenario: Accepted is not ready
    Given an immutable source release and configuration
    When preview creates an isolated environment
    Then exactly one create request is sent
    And a provisioning receipt is returned without claiming readiness

  Scenario: TTL is validated before dispatch
    Given a lifetime outside 300 through 604800 integer seconds
    When preview or preview-plan is requested
    Then no HTTP request is sent

  Scenario: Wait preserves the selected preview
    Given a valid provisioning receipt and an exhausted observation budget
    When preview --wait returns
    Then its exit status is nonzero and the accepted receipt is preserved
    And reconciliation selects the same Preview ID without another creation

  Scenario: Ready requires verified platform evidence
    Given a provisioning preview selected by its scope, digest and Preview ID
    When its complete readiness receipt is observed
    Then waiting succeeds with that receipt
    And foreign or malformed receipts never become success

  Scenario: Safety guards precede effects
    Given a read-only or unconfirmed production profile
    When preview creation, status recovery or cleanup is requested
    Then the CLI rejects the request before HTTP dispatch

  Scenario: Unknown outcome preserves reconciliation
    Given a create response is lost or belongs to another environment
    When the CLI handles that response
    Then it reports OUTCOME_UNKNOWN without retrying creation
    And it retains the selected scope for receipt reconciliation
```

### Framework source upgrades

Breaking source-contract changes use the compiler's migration command, which is
preview-only by default:

```sh
cd orders
bunx supacloud-compiler migrate --root . --json
bunx supacloud-compiler migrate --root . --write
bunx supacloud-compiler compile
bunx supacloud-compiler check
```

Review the preview and working-tree diff before `--write`. An ambiguous route
conversion returns a non-zero result and leaves all files untouched. The command
does not migrate databases or generated artifacts; regenerate those artifacts
after the source change and use version control for rollback. See the [route
contract migration guide](../../docs/route-contract-migration.md).

## Installation

Install:

```bash
npm install -g @supacloud/cli
supacloud status
```

Bun users can run the same package explicitly with Bun, including on Windows,
without installing Node.js or a separate wrapper:

```bash
bunx --bun --package @supacloud/cli supacloud status
```

One-off execution:

```bash
npm exec --package @supacloud/cli -- supacloud status
```

Install the packaged AI Skill:

```bash
supacloud ai show_skill
supacloud ai install_skill --dry_run
supacloud ai install_skill
```

The default destination is `$CODEX_HOME/skills/supacloud` or
`~/.codex/skills/supacloud`. Use `--target /path/to/skills` for an explicit
Skill root. Different existing content is preserved unless `--force` is passed;
forced replacement creates a timestamped adjacent backup first.

The Skill directs agents to keep schema, functions/RPC, triggers, RLS, indexes,
grants, extensions, and reference-data changes in migrations; use read-only SQL
for ordinary inspection; dry-run remote migrations; and reconcile existing
remote drift before touching migration history.

## Environment profiles and production safety

Use named environment files to keep test and production configuration separate.
`--env test` reads `.env.supacloud.test` from the current working directory;
`--env prod` reads `.env.supacloud.prod` and treats the profile as production.
For example:

```dotenv
# .env.supacloud.test
SUPACLOUD_ENV=test
SUPACLOUD_API_URL=https://management.test.example.com
SUPACLOUD_API_TOKEN=<test-management-api-token>
SUPACLOUD_PROJECT_REF=test-ref
SUPACLOUD_APPLICATION_ID=orders
SUPACLOUD_APPLICATION_ENVIRONMENT_ID=test
SUPACLOUD_APPLICATION_CONFIGURATION_PATH=deploy/configuration.test.json
```

```dotenv
# .env.supacloud.prod
SUPACLOUD_ENV=production
SUPACLOUD_API_URL=https://management.example.com
SUPACLOUD_API_TOKEN=<production-management-api-token>
SUPACLOUD_PROJECT_REF=production-ref
SUPACLOUD_APPLICATION_ID=orders
SUPACLOUD_APPLICATION_ENVIRONMENT_ID=production
SUPACLOUD_APPLICATION_CONFIGURATION_PATH=deploy/configuration.production.json
```

When `SUPACLOUD_APPLICATION_ID` is present, the top-level deployment command
uses the application release workflow automatically:

```bash
supacloud --env test deploy
supacloud --env production deploy --confirm-production production-ref
```

Test and production use the same workflow. Production safety is enforced by the
CLI confirmation gate; immutable releases, configuration revisions, backups,
readiness, CAS activation, rollback artifacts and uncertain-result
reconciliation belong to SupaCloud. `SUPACLOUD_APPLICATION_CONFIGURATION_PATH`
and `SUPACLOUD_APPLICATION_CONFIGURATION_ID` are optional pins; when neither
is supplied, SupaCloud reuses the current configuration head for the selected
environment. A first deployment still requires a configuration revision to
exist on the platform. The application layer only supplies its build output and
optional configuration file; it does not implement backup, activation or
downgrade orchestration.

CLI requests (including `status`) default to `SUPACLOUD_TLS_VERIFY=false`, skipping HTTPS certificate verification so that
air-gapped installations using private or self-signed certificates work
without extra configuration. To enable strict certificate verification, set:

```dotenv
SUPACLOUD_TLS_VERIFY=true
```

This setting only affects HTTPS requests made by the CLI transport. It does not
change server-side SupaCloud, Caddy, Edge Runtime, or browser certificate
verification. Keep the default only on trusted internal networks; use a
trusted CA and `SUPACLOUD_TLS_VERIFY=true` for public or untrusted networks.
The legacy inverse setting `SUPACLOUD_TLS_INSECURE=false` is also accepted.
`SUPACLOUD_TLS_VERIFY` takes precedence if both are present. Both settings use
the selected context source, not a mix of profile and process variables.
Values accept `true/false`, `1/0`, `yes/no`, and `on/off` (case-insensitive).
Invalid or empty values stop the command before requests are sent.
Skipping verification accepts any certificate, not just a private CA, and
removes server identity authentication. HTTPS encryption and redirect refusal
remain enabled. Install your organization's CA and set verification to `true`
where identity authentication is required.

Run commands with the selected profile:

```bash
supacloud --env test status
supacloud project get --env test
supacloud status --env-file ./config/supacloud.staging.env
```

Global flags may appear before or after the command. Both `--key value` and
`--key=value` syntax are accepted. `--env` and `--env-file` are mutually
exclusive. An explicit `--env-file` must declare `SUPACLOUD_ENV`.

Environment files contain credentials. The repository root `.gitignore`
already ignores `.env` and `.env.*`; do not force-add or commit these files.
Restrict locally created files to the current user:

```bash
chmod 600 .env.supacloud.test .env.supacloud.prod
```

CI can provide one complete context through process environment variables
instead of writing a file:

```bash
SUPACLOUD_ENV=test \
SUPACLOUD_API_URL=https://management.test.example.com \
SUPACLOUD_API_TOKEN="$CI_SUPACLOUD_API_TOKEN" \
SUPACLOUD_PROJECT_REF=test-ref \
supacloud status
```

Project context is resolved from one atomic source: a named profile selected by
`--env`, an explicit `--env-file`, a complete process environment, or the
legacy `.env` fallback. Core URL, credential, and project-ref values are not
filled by mixing sources. If `SUPACLOUD_ENV` is set without a complete process
context, it strictly selects `.env.supacloud.<value>`.

For one-command deployments, declare public build variables explicitly in the
target configuration:

```json
{
  "application": { "id": "orders", "environmentId": "test" },
  "targets": {
    "web": {
      "type": "frontend",
      "root": "apps/web",
      "id": "web",
      "buildEnv": ["VITE_API_URL", "VITE_SUPABASE_ANON_KEY"]
    }
  }
}
```

`buildEnv` is an allowlist. The selected profile values are injected only into
the local build command; values are never printed, undeclared values are not
passed through, and platform credentials or runtime secrets are rejected.

The two credential scopes are separate. Management-backed remote commands use
only `SUPACLOUD_API_URL` + `SUPACLOUD_API_TOKEN`. An application profile using
`SUPABASE_URL` + `SUPABASE_SERVICE_ROLE_KEY` can be auto-linked for `status`,
but its service-role key is never substituted for a Management token and cannot
enable Management-backed tools. Both URL types must be canonical HTTPS origins;
omit explicit default ports such as `:443`. HTTP is accepted only for literal
loopback development origins, with the default `:80` likewise omitted. Use
`SUPACLOUD_PROJECT_REF` when it cannot be inferred from a managed
`<ref>.api.*` application hostname.

### Immutable frontend releases

The `frontend` command retains release inspection and explicit operator rollback.
Publish through `supacloud deploy`, which delegates activation to the platform:

```bash
supacloud frontend list_releases --ref abc123 --id web
supacloud frontend get_active_release --ref abc123 --id web
supacloud frontend get_release --ref abc123 --id web --release_id <sha256>
supacloud frontend rollback --ref abc123 --id web --release_id <retained-sha256>
supacloud frontend rollback --ref abc123 --id web
```

`deploy_git`, `deploy_upload`, `redeploy`, `upload_release` and `activate_release`
have been removed from `frontend`. Old invocations fail with an upgrade message,
without reading an archive or sending a request. Production rollback retains
the exact `--confirm-production <ref>` gate; read-only mode blocks mutations.

`get_active_release` requires the current server endpoint. An HTTP 404 returns
an upgrade/deployment-existence hint and never falls back to historical inventory.
`rollback` without `--release_id` asks SupaCloud for a journal-verified previous
immutable release, reads the current CAS identity and generates the mutation ID
itself. An explicit `--release_id` remains available for a deliberate rollback
to any retained release. It never infers a target from content-hash-sorted
history, reuploads assets or restores a database.
An uncertain result includes the mutation ID for read-only reconciliation and
must not be blindly retried.

### Immutable application releases

Store a compiler-built HTTP/Worker application as one immutable release:

```bash
supacloud applications upload_release --ref abc123 --id reviews \
  --manifest_path ./generated/delivery/delivery.manifest.json
supacloud applications list_releases --ref abc123 --id reviews --limit 50
supacloud applications get_release --ref abc123 --id reviews --release_id <sha256>
supacloud applications get_runtime --ref abc123 --id reviews --environment_id test
```

Upload verifies executable objects, transfers their inventory with multipart
form data, and checks the returned application-bound release identity. It does
not activate HTTP/Worker processes or execute migrations. Lists use release-ID
ordering; pass `--cursor <next_cursor>` for the next page. An unknown upload
outcome includes the expected release ID so `get_release` can verify it.
Uploads follow the existing read-only and exact production-confirmation rules.
`get_runtime` observes the current activation without starting it. A null
readiness result means no active release has been recorded. A non-null report
binds readiness to the environment, release, activation and target processes;
it does not claim that every business dependency or transaction has been tested.

### Versioned application configuration

Environment delivery also provides `app diff` / `app promote-plan`,
`app promote`, `app promote-status`, and `app promote-reconcile`.
Promotion obtains and validates the immutable plan automatically; complete
no-op skips the write. A required promotion generates one mutation UUID and
submits once. An uncertain outcome retains that UUID without retrying SQL,
runtime activation, rollback or database restore. Production writes require
the selected project's exact `--confirm-production <ref>`.

`app preview-plan`, `app preview-create`, `app preview-list`,
`app preview-status`, and `app preview-cleanup` manage isolated preview receipts.
Creation defaults to `schema_only`, reports provisioning rather than readiness,
and returns secret names only. An uncertain create should be inspected through
`preview-list` before any deliberate new create. Read-only profiles permit
plans and status/list reads but reject promotion, reconciliation and preview writes.
See [application promotion](../../docs/application-promotion.md) for commands
and the platform/client responsibility boundary.

Configuration revisions bind a complete target inventory to one application
environment. Saving a revision does not allocate ports, start processes, change
traffic or migrate data:

```bash
supacloud applications get_configuration --ref abc123 --id reviews --environment_id test
supacloud applications put_configuration --ref abc123 --id reviews --environment_id test \
  --configuration_path ./application.test.json
supacloud applications get_configuration --ref abc123 --id reviews --environment_id test \
  --configuration_id <uuid>
```

Example configuration write file:

```json
{
  "configuration_id": "01234567-89ab-4def-8123-456789abcdef",
  "expected_configuration_id": null,
  "configuration": {
    "bun_version": "1.4.3",
    "targets": [
      {
        "name": "api",
        "kind": "http",
        "hosts": ["reviews.example.test"],
        "environment": { "LOG_LEVEL": "info" }
      },
      { "name": "jobs", "kind": "worker", "hosts": [], "environment": {} }
    ]
  }
}
```

Generate a new UUIDv4 for each new revision. Use the current configuration ID
as `expected_configuration_id`, or `null` for an environment without a saved
configuration. The file replaces the entire configuration; omitted variables
are not carried over. HTTP targets require lowercase exact hosts; Worker
targets have no hosts. Runtime-owned variables such as `PORT` cannot be supplied.

The control plane encrypts values with its configured secret-encryption key.
API and CLI reads return names only; keep local files with values out of version
control. An identical retry with the same revision ID returns the original
receipt and never moves a newer head backward. A changed request using the same
ID or a stale expected revision returns a conflict. An unknown result preserves
the revision ID for `get_configuration` verification; the CLI does not replay
the write automatically. Reads without an ID show the current head.

Writes follow the normal read-only and exact production-confirmation policy.
Programmatic `activateConfigured` selects an explicit configuration revision,
checks its target inventory against the release and retains that revision ID
in activation state/runtime feedback. It now obtains persistent server-selected
ports rather than accepting caller ports. Allocation is bound to the release,
activation, environment, Bun version and configuration revision; a retry retains
the same ports. The default single-host pool is `20000-29999`, configurable with
`SUPACLOUD_APPLICATION_PORT_RANGE`. Existing configured platform pools,
persisted tenant port overrides and current loopback listeners are excluded.

Reservations do not expire automatically and are not deleted with project
metadata. Exhaustion is an explicit error; reusing a port still requires a
verified stopped/unrouted retirement path. Manual low-level runtimes and other
daemons must stay outside this application pool because their lifecycle does
not participate in its reservations. The public activation write API remains
unavailable pending compatibility and recovery integration.

Composed hosts can register `POST .../environments/:environmentId/activations`
and `POST .../activations/:activationId/reconcile` by supplying an
`ApplicationDeploymentService` with its required compatibility verifier.
The default Management API instance does not yet enable these endpoints.
For an explicitly composed test host:

```bash
supacloud applications activate_release --ref abc123 --id reviews --environment_id test \
  --release_id <release-sha256> --configuration_id <configuration-uuid> \
  --activation_id <new-uuid> --expected_activation_id absent
supacloud applications reconcile_activation --ref abc123 --id reviews --environment_id test \
  --release_id <release-sha256> --activation_id <same-uuid>
supacloud applications retire_activation --ref abc123 --id reviews --environment_id test \
  --activation_id <stopped-and-unrouted-uuid>
```

Retirement is addressed by activation ID, not release ID. Supplying `release_id`
to `retire_activation` is rejected locally before any request is sent.

For upgrades or explicit application rollback, supply the currently active
activation UUID instead of `absent`. Both actions are writes. The CLI checks
receipt identity and preserves the requested activation ID on unknown outcomes;
it does not retry writes. Reconciliation only confirms already committed state,
not an interrupted early-stage activation, and does not replay runtime effects.
Retirement requires the composed host's stopped/unrouted verifier and keeps the
activation ID as an audit tombstone; the CLI does not retry unknown outcomes.

### Verified release controls

`release` is an official CLI entry point for verified Management API controls
and the release-canary fixture receipt replay. Management actions require the
Management API context above. `release_canary_fixture_stage_replay` additionally
requires the same project's `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY`; the
key is sent only to that application origin and is never promoted to Management
authority.

Project pause and restore are explicit lifecycle commands. A logical backup
restore requires an operator to pause the selected project first and then use
`project get` to confirm that its status is `paused`. `project restore` resumes
the paused project lifecycle when its database exists; if the database is
missing, the platform begins project re-provisioning instead. It does not
restore a database backup and is never invoked automatically by the CLI.

```bash
supacloud project pause --ref abc123
supacloud release logical_backup_restore --ref abc123 \
  --backup_id logical-full_abc123_<backup-id-suffix> \
  --expected_sha256 <64-lowercase-hex> \
  --restore_confirmation RESTORE_PROJECT:abc123:logical-full_abc123_<backup-id-suffix>:<64-lowercase-hex>
supacloud project restore --ref abc123
```

```bash
supacloud release logical_backup_list --ref abc123
supacloud release logical_backup_create --ref abc123
supacloud release logical_backup_restore --ref abc123 \
  --backup_id logical-full_abc123_<backup-id-suffix> \
  --expected_sha256 <64-lowercase-hex> \
  --restore_confirmation RESTORE_PROJECT:abc123:logical-full_abc123_<backup-id-suffix>:<64-lowercase-hex>
supacloud release postgrest_status --ref abc123
supacloud release postgrest_restart --ref abc123
supacloud release release_canary_fixture_stage_replay --ref abc123 \
  --subject <central-subject-uuid> --request_id <stage-request-uuid>
supacloud release release_canary_fixture_disable_replay --ref abc123 \
  --fixture_id <fixture-uuid> --disable_request_id <disable-request-uuid> \
  --issuer <issuer-url> --subject <central-subject-uuid>
```

Backup creation reports success only after the CLI verifies exactly one new
logical-backup receipt against the inventory before and after the mutation.
Logical restore is project-scoped: pause the selected project first, obtain the
backup ID and SHA-256 from `logical_backup_list`, and supply both the normal
production `--confirm-production <ref>` value (for production profiles) and
the exact `--restore_confirmation
RESTORE_PROJECT:<ref>:<backup_id>:<sha256>`. Before POST, the CLI re-reads
that same project's inventory and binds the request to the complete verified
backup identity; it then verifies both the server receipt and a fresh inventory
read. It never retries a restore. A transport, server, or unreadable-response
failure is reported as `OUTCOME_UNKNOWN`; read the inventory and investigate
before any new restore decision.

PostgREST restart reports success only after it receives a matching restart
receipt and reads back `desired=running`, `actual=running`, and
`health=healthy`. Both mutating controls follow the normal production
confirmation and read-only protections.

`release_canary_fixture_stage_replay` performs one non-retried, response-bounded
call to the fixed `fa_release_canary_fixture_stage` PostgREST RPC with both
bearer and `apikey` service-role headers. It accepts only canonical subject and
request UUIDs, requires a strict `staged` and `idempotent=true` four-field
receipt, and emits only that safe projection. Before and after the call, the CLI
reads the selected project's authoritative endpoint projection and requires the
configured application origin to match its API origin or alias. Production
confirmation is mandatory.

`release_canary_fixture_disable_replay` performs one non-retried call to the
fixed `fa_release_canary_fixture_disable` PostgREST RPC using the selected
project's application service-role origin. Its receipt must contain exactly
`fixtureId`, `state="disabled"`, and a boolean `idempotent`; both the first
disable (`idempotent=false`) and same-request replay (`idempotent=true`) are
valid. The CLI then calls the existing fixed
`fa_release_canary_fixture_pending` RPC with only the exact issuer and subject
query parameters and accepts only its authoritative JSON boolean `false`
read-back. The fixture binding comes from the validated disable receipt before
reporting success. Management endpoint projection is checked before and after.

### Internal release scope controls

Do not generate, rebind or hash scope files as part of a deployment.
Use an application binding and the unified flow documented in
[`docs/cli-guide.md`](../../docs/cli-guide.md), for example:

```bash
supacloud deploy
```

FA deployment policy and the boundary between platform-owned operations and
FA-owned business features are documented in
[`docs/fa-deployment-capability-boundary.md`](../../docs/fa-deployment-capability-boundary.md).

`deploy --target`, `--id`, `--build_command`, `--output_dir`, `--plan`, `--diff`
and `--dry_run` are removed. Put build settings in `supacloud.json`; use
`app deploy-plan` for read-only review. Historical project-specific manifests
and standalone Function targets must move to application delivery configuration.
The CLI rejects them rather than falling back to a partial Web deployment.

The legacy `.env` fallback is unclassified and therefore does not enable the
production confirmation gate. Production automation must select a `prod` or
`production` profile, or set `SUPACLOUD_ENV=production` together with a complete
process context. Use `SUPACLOUD_READ_ONLY=true` when legacy workflows must be
restricted to inspection only.

`SUPACLOUD_READ_ONLY=true` is a safety override that blocks remote writes.
Production writes require an explicit confirmation equal to the selected
profile's project ref:

```bash
supacloud database push_migrations --env prod \
  --ref production-ref --dir supabase/migrations \
  --confirm-production production-ref
```

Dry runs remain read operations. Production `diagnostics repair` is always
forbidden, and unclassified actions fail closed in production or read-only
contexts.

For supported command groups, `--ref` overrides the profile's default project
for one command. A production profile cannot target a different project with
`--ref`; the requested ref and `--confirm-production` must both exactly match
the profile's project ref.

`status` checks configuration, connectivity, and authentication against the
selected credential scope. Application profiles probe the project data API at
the exact configured origin; credential-bearing probes refuse redirects. The
command exits non-zero when any required check fails. Its output includes
`credentialScope`, `environment`, `source` (`kind` and `path`), `apiUrl`,
`projectRef`, `readOnly`, `production`, and `hasApiToken`. It never prints the
API token or service-role key.

Examples:

```bash
supacloud status
supacloud project get
supacloud project logs --log_type database
supacloud project task_stats
supacloud project task_detail --task_id task_123
supacloud queue stats --queue emails
supacloud queue dlq --queue emails --limit 20
supacloud task_events inspect_webhook --ref abc123
supacloud database query --sql "select now()"
supacloud database query --ref abc123 --file ./queries/vector-search.sql
supacloud database migration_inventory --ref abc123
supacloud database lint_migrations --dir supabase/migrations
supacloud database rebase_migrations --dir supabase/migrations --baseline_file backups/schema.sql --baseline_version 20261008000000 --output_dir supabase/migrations-rebased
supacloud database push_migrations --ref abc123 --dir supabase/migrations --dry_run
supacloud supabase migration_new --name add_accounts
supacloud supabase db_diff --schema public --name add_accounts
supacloud supabase push --ref abc123 --dir supabase/migrations --dry_run
supacloud frontend list --ref abc123
supacloud branch create --name feature-orders --data_mode schema_only
supacloud branch promotion_plan --branch_ref preview123
supacloud branch promote --branch_ref preview123 --plan_checksum <sha256>
supacloud edge_functions get_config --ref abc123 --slug hello
supacloud edge_functions deploy --ref abc123 --slug hello --path ./supabase/functions/hello --expected-active-version absent --expected-activation-id legacy
supacloud edge_functions deploy --ref abc123 --slug hello --prebundled-path ./dist/hello.js --expected-sha256 <sha256> --expected-active-version 4 --expected-activation-id <uuid>
supacloud edge_functions deploy_bundle --ref abc123 --slug hello --files '{"index.ts":"export default { fetch: () => new Response(\"ok\") }"}' --expected-active-version 7 --expected-activation-id <uuid>
supacloud edge_functions source --ref abc123 --slug hello --version 7 --output ./hello-v7.ts
supacloud edge_functions activate --ref abc123 --slug hello --version 3 --expected-active-version 8 --expected-activation-id <uuid>
supacloud edge_functions delete --ref abc123 --slug hello --expected-activation-id <uuid>
supacloud scheduled_functions list --ref abc123
supacloud secrets upsert --ref abc123 --from-env API_KEY,WEBHOOK_SECRET
supacloud storage list_buckets --ref abc123
supacloud storage get_bucket --ref abc123 --bucket reports
supacloud storage create_bucket --ref abc123 --bucket reports --public false \
  --file_size_limit 10485760 --allowed_mime_types "application/pdf,image/png"
supacloud storage update_bucket --ref abc123 --bucket reports \
  --expected_revision <revision-from-get_bucket> \
  --allowed_mime_types '["application/pdf"]'
supacloud storage delete_bucket --ref abc123 --bucket reports \
  --expected_revision <revision-from-get_bucket> --require_empty true
```

`database migration_inventory` reads the canonical migration ledger through the
project-scoped Management API and prints only a validated JSON array. It rejects
non-2xx responses, malformed entries, unsafe project refs, duplicate canonical
migration versions, checksum
drift, and statement-count mismatches instead of treating them as an empty
ledger. `database list_migrations` remains available with its legacy SQL-backed,
human-readable behavior.

`database delivery_migration_plan` compares one immutable target's declared SQL
with the separate read-only `/database/migrations/inventory` endpoint:

```bash
supacloud database delivery_migration_plan --ref abc123 \
  --delivery_manifest /archive/delivery/delivery.manifest.json --delivery_target api
```

The entire target inventory is verified before the API request, without loading
current application source. Output contains hashes and statuses, not SQL.
`ledger-match` means the version, name and normalized single-statement-array
checksum match; raw SQL SHA-256 is reported separately. Baseline markers are not
execution evidence. Name/checksum conflicts and out-of-order pending migrations
return errors. Operator provisioning always requires separate verification.
`ledgerCompatible` reports only identity/order checks: it does not prove old/new
application compatibility, authorize migration execution, or validate deployment.
This command never applies SQL, repairs a ledger, or activates an application.
The server endpoint performs only ledger reads, unlike the older migration-list
endpoint which may initialize or reconcile metadata. Older servers without the
read-only endpoint fail closed; the command never falls back to the older GET.

Bucket list/get output includes the metadata `revision`. Update and delete reject
stale revisions with HTTP 409. Delete additionally requires `require_empty=true`;
it never empties a bucket. Mutation receipts bind `project_ref`, `bucket_id`,
`previous_revision`, and `new_revision` (`null` after delete).

`database push_migrations` rejects a remote row that reuses a local migration
name with another version, or a local version with another name, before either
dry-run reporting or apply can continue. This keeps the preview consistent with
the server conflict that would otherwise occur after deployment starts.
`push_migrations` requires canonical string migration identities; the narrower
`baseline_migrations` compatibility path also accepts historical safe-integer
versions and normalizes them before comparison. Repeated historical `cli_push`
names are treated as generic labels; version and exact content remain the
authority.
Mutation failures use the release-control receipt schema and never include the
server response body. `OUTCOME_UNKNOWN` requires a fresh migration inventory
read before deciding whether a retry is safe. A migration push failure also
reports the local files applied or skipped before the failed file, so operators
can reconcile a partially completed sequence without exposing SQL. Baseline
success requires a bounded migration-inventory readback that confirms every
requested version, name, baseline marker, and checksum.

`database lint_migrations` is local-only and works without Management API
credentials. Inspect one source with `--sql`, `--file`, or `--dir`; these inputs
are mutually exclusive. `--strict` (or `--fail_on_high`) exits non-zero for HIGH
findings, `--fail_on_medium` also fails on MEDIUM findings, and `--json` emits a
structured report. The analyzer detects destructive DDL (`DROP TABLE`,
`DROP COLUMN`, `TRUNCATE`, `RENAME`), table-locking risks, opaque `DO` blocks,
procedural definitions/calls that require manual review, and statements that
cannot run in the transactional migration executor. Empty migrations,
unterminated strings/comments/dollar quotes, role or cluster management,
external database access, server file/process access, transaction-boundary or
session-identity control, advisory locks, public-schema rename/removal, and
direct migration-ledger schema, table, recorder, or privilege mutation fail
before the first remote migration write. Ledger indexes, triggers, rules,
policies, comments, security labels, and table maintenance commands are covered
by the same fail-closed rule. Top-level `COPY` is also rejected
because the HTTP migration executor cannot provide a safe client stream and
server-file targets cross the project boundary. Unicode-escaped identifiers
(`U&"..."`) are rejected because the policy cannot safely canonicalize protected
schema and ledger names. One matching outer `BEGIN`/`COMMIT` wrapper is removed
because the platform owns the transaction.
Adding a `NOT NULL` column is assessed per top-level `ALTER TABLE` subcommand;
function-based defaults remain MEDIUM risk because they may rewrite the table.
Dry-run exits non-zero for statements the transactional executor cannot apply,
even when strict destructive-risk mode is not enabled.
`CREATE/DROP INDEX CONCURRENTLY`, `VACUUM`, and other non-transactional work must
run through an approved maintenance path outside `push_migrations`.

`database rebase_migrations` is local-only and never contacts the Management
API. Without `--write` it only prints the plan; with `--write` it writes a
reconstructed baseline, the retained forward migrations, and a
`migration-rebase.manifest.json` (with SHA-256 records for the complete source
history) into a separate `--output_dir`, never the source directory. Capture and
review a schema-only snapshot first with `--baseline_file` and
`--baseline_version`, then use `--retain_after_version` to keep newer
migrations. Review the baseline for omitted data backfills, reference data,
extensions, grants, RLS, functions and triggers, replay it in a clean database,
and archive the original directory until clean replay and remote read-back pass.

Migration applications and DDL executions send PostgREST schema reload
notifications (`NOTIFY pgrst_<ref>, 'reload schema'`) so schema changes become
visible without a manual PostgREST restart. Responses distinguish `notified`
from `notification_failed` and include `ddl_committed=true` when the platform
can prove the DDL committed. Admin SQL with explicit transaction control or
indirect `CALL`/`DO` execution omits `ddl_committed`; for
`database create_table_rls`, a committed DDL batch followed by a failed reload
returns `PARTIAL_SUCCESS` instead of claiming complete success or an unknown DDL
outcome.

`edge_functions deploy --path` bundles local TypeScript and dependencies with
Bun and runs a local syntax check before upload. The Management API validates and
normalizes the final server-side artifact against the multi-tenant Edge Runtime
module policy consistently for CLI, Web Console, and direct API deployments.
For an artifact already built and validated by release automation, use
`deploy --prebundled-path <file> --expected-sha256 <64-lowercase-hex>`. The CLI
holds the opened regular file while reading it, rejects metadata drift, invalid
UTF-8, or a caller-hash mismatch before HTTP, and never passes the artifact in
the process argument list. The Management API validates the hash and runtime
policy again, rejects any normalization that would change the code, and stores
the submitted bytes unchanged as both immutable source and runtime artifact.
`--prebundled-path` is mutually exclusive with `--path`, `--code`, and
`--minify`.
`deploy_bundle --files` accepts a JSON object in shell usage.
Use `source --output <file>` for large Functions so terminal or automation output
limits cannot truncate the original TS/JS source code. The destination must not
already exist. Add the positive version observed from `list` as
`source --version <N>` to read the immutable release instead of the moving active
pointer; this remains correct across an active-version A→B→A transition.

`deploy`, `deploy_bundle`, and `activate` require
`--expected-active-version <N|absent>`. Read the current non-negative integer
version from `edge_functions list`; use `0` for a listed legacy Function and
`absent` only when creating a slug that does not yet exist. A stale value returns
HTTP 409 without building, preheating, or activating another version. Every
Function mutation also requires `--expected-activation-id <uuid|legacy>` from
the same `list` or `get_config` snapshot. Use `get_config` for a single atomic
read of `active_version`, `activation_id`, and policy, including an `absent`
tombstone after deletion. Use `legacy` only for a never-created slug or a listed
legacy Function; recreating a deleted slug must use the tombstone UUID returned
by `delete` or `get_config`. This second token prevents an A→B→A version cycle
from satisfying a stale mutation. List output remains a JSON array with string
`slug`, numeric `version`, and canonical `activation_id` fields, while source
output is exactly `{ "code": "..." }`. Release automation must use
`source --version <N>` for a version-bound backup.

`edge_functions activate` restores an existing immutable Function version and
returns a machine-readable receipt containing the activated version and JWT
policy. HTTP and malformed-response failures exit non-zero without echoing the
server response body.

Mutation receipts use schema `supacloud.cli.release-control.v1`. An
`OUTCOME_UNKNOWN` error means the server may have committed the mutation before
the response was lost or failed validation; read back current state before any
retry. For Function deploy, bundle deploy, activation, config, and delete, the
CLI applies a separate 5-second, 64 KiB response-body boundary after receiving
HTTP headers. A stalled, oversized, truncated, unreadable, or malformed body is
always `OUTCOME_UNKNOWN` and its content is never included in CLI output.
Version `0` is reserved as the active-version CAS token for legacy Functions. It
can be passed only as `--expected-active-version`; immutable source reads and
activation targets still require a positive version.

```json
{
  "schema": "supacloud.cli.release-control.v1",
  "ok": true,
  "operation": "edge_functions.deploy_bundle",
  "project_ref": "abc123",
  "slug": "hello",
  "previous_active_version": "7",
  "expected_activation_id": "9dc0e8da-207f-4f25-ae74-b1de4e66784d",
  "activation_id": "6417591d-c038-46e8-91ca-4c8080514144",
  "active_version": "8",
  "version": "8",
  "verify_jwt": true
}
```

Scheduled Function lifecycle operations are also project-scoped:

```bash
supacloud scheduled_functions create --ref abc123 --name nightly \
  --slug cleanup --cron "0 2 * * *" --method POST
supacloud scheduled_functions get --ref abc123 --schedule_id <id>
supacloud scheduled_functions update --ref abc123 --schedule_id <id> \
  --expected_updated_at <updated_at-from-list> --cron "0 3 * * *"
supacloud scheduled_functions delete --ref abc123 --schedule_id <id> \
  --expected_updated_at <updated_at-from-list>
```

Schedule IDs are canonical UUIDv4 values returned by create/list. Cron values
use bounded numeric five-field syntax with wildcards, lists, ranges, and steps;
out-of-range endpoints and steps are rejected before HTTP dispatch.
Update and delete require the exact canonical UTC `updated_at` returned by list.
A stale revision fails with HTTP 409 and performs no mutation; read the list
again before deciding whether to issue a new write.

Use `--body_file ./payload.json` for a JSON-object request body. Header values
must come from environment variables: pass a JSON name mapping such as
`--header_env '{"x-schedule-token":"SCHEDULE_TOKEN"}'`. Platform-owned
`authorization`, `apikey`, and `x-project-ref` headers cannot be overridden. Receipts never
include header values or body content; list and mutation receipts report only
whether the body is empty and the configured header names. Update receipts bind
`previous_updated_at` to the requested revision and return a newer `updated_at`;
delete receipts return the matched revision as `deleted_updated_at`.

For secret writes, `--from-env` accepts a comma-separated list of environment
variable names. The CLI reads each non-empty value from the currently selected
profile source, such as `.env.supacloud.test` selected by `--env test`; command
arguments contain names only and values are never printed. Names must be unique
shell-style identifiers (`[A-Za-z_][A-Za-z0-9_]*`, up to 256 characters).
Missing or empty values fail before any HTTP request. Do not combine
`--from-env` with the compatibility `--secrets` input.

Branch promotion is migration-first. `branch promotion_plan` prints pending
versions, names, statement counts, and checksums without echoing SQL into terminal
logs; review the migration files or the Web Console SQL view before approval.
`branch promote` requires the reviewed checksum, executes with the project-scoped
database role, and does not automatically copy branch data.
Use `--data_mode full_clone` only for an explicitly approved non-sensitive or
masked debugging dataset. Whole-database replacement is an administrator-only
break-glass API mode and is intentionally not exposed by this project CLI.

## SupaCloud Lite CLI adapter

Lite can be used from its standalone `supacloud-lite` CLI and from the main
`supacloud` through the local-only `lite` module. The adapter never calls
the Management API, never invokes the official Supabase CLI, and never treats a
PGlite data directory as a Postgres DSN.

```bash
supacloud lite migrate --project_dir .
supacloud lite status --project_dir .
supacloud lite db_diff --project_dir . --file add_accounts
supacloud lite db_pull --project_dir . --file remote_schema
supacloud lite db_pull --project_dir . --file reviewed_schema --baseline
supacloud lite gen_types --project_dir . --output src/database.types.ts
supacloud lite snapshot_create --project_dir . --output backups/lite.tar.gz
supacloud lite doctor --project_dir . --json
supacloud lite start --project_dir . --port 54321
```

The adapter resolves the executable in this order:

1. `SUPACLOUD_LITE_CLI_BIN`
2. `bin["supacloud-lite"]` from the locally installed `@supacloud/lite/package.json` (currently `dist/launcher.mjs`)
3. `supacloud-lite` on `PATH`

Install `@supacloud/lite` or provide an explicit binary before using the
adapter. Lite actions are local-only, so Management API context and project
refs are not required. The `supabase` module remains the official CLI adapter;
use it for upstream Supabase CLI actions and Management-backed remote pushes.

Lite `db_pull` writes a non-executable draft directory by default and never marks
the live delta applied implicitly. `--baseline` is only for a reviewed change
already present in that database. Diff/pull require matching applied migration
history, inspect live state without applying migrations, and verify supported
DDL in a disposable shadow. Unsupported drift fails closed. Preserve any
`.sql.pending` baseline file and inspect the ledger before recovery.

## Official Supabase CLI adapter

The `supabase` command group is a thin, allowlisted adapter around the official
open-source Supabase CLI. It is not a fork. Local authoring commands work without
SupaCloud credentials:

```bash
supacloud supabase version
supacloud supabase init --workdir .
supacloud supabase migration_new --name add_accounts
supacloud supabase db_diff --schema public --name add_accounts
supacloud supabase db_reset --no_seed
```

Remote inspection and backup commands require an explicit, percent-encoded
Postgres DSN:

```bash
supacloud supabase db_pull --db_url "$SUPACLOUD_DB_URL" --declarative
supacloud supabase migration_list --db_url "$SUPACLOUD_DB_URL"
supacloud supabase db_dump --db_url "$SUPACLOUD_DB_URL" --file backups/schema.sql
supacloud supabase gen_types --db_url "$SUPACLOUD_DB_URL" --schema public --file src/database.types.ts
```

Remote migration application intentionally stays on SupaCloud's existing
project-authenticated API:

```bash
supacloud supabase push --ref abc123 --dir supabase/migrations --dry_run
supacloud supabase push --ref abc123 --dir supabase/migrations
```

`push` uses only `SUPACLOUD_API_TOKEN` for the SupaCloud Management API. That
token, upstream access tokens, database passwords, and secret/key environment
variables are removed from the official CLI child process, and command output
is redacted. Upstream access-token forwarding is separately opt-in through
`SUPACLOUD_FORWARD_SUPABASE_ACCESS_TOKEN=1`. The child can still read the official
CLI's saved login or project dotenv files; this is not a credential sandbox.

### Declarative schemas, configuration and local stacks

These commands require a compatible official CLI. Pin and check its version;
the adapter does not install a new CLI or replace an existing schema authority.
`init` preserves existing configuration and inherits the installed CLI's defaults.

```bash
supacloud supabase db_schema_declarative_generate --db_url "$SUPACLOUD_DB_URL"
supacloud supabase db_schema_declarative_sync --name add_accounts --strict_coverage
supacloud supabase db_diff --diff_engine pg-delta --name add_accounts
supacloud supabase config_pull --ref upstream-project
supacloud supabase config_pull --ref upstream-project --dry_run=false --yes
supacloud supabase stack_start --workdir .
supacloud supabase stack_status --workdir .
SUPACLOUD_ENABLE_NATIVE_STACK=1 supacloud supabase stack_prepare --workdir . --runtime native
SUPACLOUD_ENABLE_NATIVE_STACK=1 supacloud supabase stack_start --workdir . --runtime native --preparation on-demand
supacloud supabase stack_stop --workdir .
supacloud supabase stack_destroy --workdir . --confirm_destroy --yes
```

Declarative sync generates without applying unless `--apply` is explicit.
Generate uses the supplied DSN or the upstream linked project; overwriting schema
files requires `--overwrite`. Declarative commands default to experimental mode,
which can be omitted with `--experimental=false`.

Config pull reads the official Supabase control plane, defaults to dry-run, and
requires the configured context's project ref when present. It is not a
SupaCloud settings migration. Native stack mode is alpha, off by default and
requires the environment opt-in above; start/prepare otherwise use Docker
explicitly. Destruction needs both confirmations. Upstream owns per-directory
state and ports. Full boundaries and acceptance gates are documented in
`docs/select-agent-backend.md` in the source repository.

`push` requires a resolved project ref; pass `--ref` explicitly or set
`SUPACLOUD_PROJECT_REF`. Relative migration directories are resolved against
`--workdir` (or the current directory).

Executable resolution order:

1. `SUPACLOUD_SUPABASE_CLI_BIN`
2. exact `SUPABASE_CLI_VERSION` through an explicit Bun/npm package runner
3. `<workdir>/node_modules/supabase`
4. `supabase` on `PATH`

Windows under Node.js requires an installed official CLI or
`SUPACLOUD_SUPABASE_CLI_BIN`; the adapter does not execute `.cmd` through a shell.

Direct SQL/console changes do not automatically create migrations or migration
history. Keep migration files in version control and run `supabase push --dry_run`
before applying them.

If the live database already contains reviewed historical changes, first pull,
back up, and prove schema equivalence. Then preview the controlled tracking sync:

```bash
supacloud database baseline_migrations --ref abc123 --dir supabase/migrations --dry_run
```

Only after explicit approval, rerun without `--dry_run`. This records migration
files as applied without executing their DDL; never edit migration-history tables
through `database query`.

Use `database query --file` for complex SQL, pgvector queries, and single-request transaction blocks.

```sql
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE documents (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  content text NOT NULL,
  embedding vector(1536)
);

CREATE INDEX documents_embedding_hnsw_idx
ON documents
USING hnsw (embedding vector_cosine_ops);
```

Transaction boundary: SupaCloud supports transaction blocks inside one SQL request and transactional migrations. It does not expose long-lived HTTP transaction sessions; use a direct Postgres DSN for application-side long transactions.

Auth configuration commands accept a JSON object from the CLI:

```bash
supacloud auth update_settings --ref abc123 \
  --config '{"disable_signup":true,"enable_signup":false}'
supacloud auth update_config --ref abc123 \
  --config '{"third_party_auth":{"enabled":true}}'
```

Failed Auth mutations exit non-zero and print a JSON object containing the HTTP
status plus an allowlisted subset of runtime-apply state. If Management API
returns `503` with `persisted: true`, the desired configuration was saved but
runtime propagation was incomplete; automation must read the affected settings
back exactly before deciding whether it is safe to continue. Free-form server
messages and request configuration are not echoed.

Project commands owned by this CLI:

- `project get`
- `project health`
- `project logs`
- `project api_keys`
- `project settings`
- `project tasks`
- `project task_detail`
- `project task_stats`
- `project task_cancel`
- `project task_retry`
- `project dlq`
- `project background_settings`
- `project update_background_settings`

Queue commands:

- `queue list`
- `queue send`
- `queue receive`
- `queue ack`
- `queue release`
- `queue fail`
- `queue retry`
- `queue delete_message`
- `queue list_messages`
- `queue get_message`
- `queue stats`
- `queue dlq`
- `queue get_settings`
- `queue update_settings`

Task event commands:

- `task_events register_webhook`
- `task_events unregister_webhook`
- `task_events inspect_webhook`

Diagnostic commands:

- `diagnostics list_checks`
- `diagnostics run_checks`
- `diagnostics get_run`
- `diagnostics repair`

Gateway / Caddy commands (require admin privileges; config is injected via the Caddy JSON Admin API):

- `gateway routes` — list custom gateway routes (reverse_proxy / static sites / redirects)
- `gateway upsert_route` — create or replace a route
- `gateway update_route` — replace a route by id
- `gateway delete_route` — remove a route by id
- `gateway config` — update rate-limit tier, CORS origins, or JWT settings
- `gateway get_certificate` — read certificate automation settings
- `gateway update_certificate` — save certificate automation settings
- `gateway issue_certificate` — issue or renew a certificate with lego
- `gateway deploy_certificate` — deploy an existing PEM cert/key pair
- `gateway rebuild` — rebuild all tenant gateway configs (`--clean` for a full rebuild)
- `gateway custom_hostname` — read the bound custom hostname
- `gateway set_custom_hostname` — bind a custom hostname
- `gateway delete_custom_hostname` — remove the custom hostname
- `gateway verify_custom_hostname` — verify a custom hostname

```bash
supacloud gateway routes --ref abc123
supacloud gateway upsert_route --ref abc123 --route_id webhook \
  --hosts "api.example.com" --paths "/webhook/*" --upstream 10.0.0.5:8080
supacloud gateway upsert_route --ref abc123 --route_id canonical-https \
  --hosts "www.example.com" --paths "/*" --protocol http \
  --redirect_to 'https://www.example.com{http.request.uri}' --redirect_status 308
supacloud gateway config --ref abc123 --rate_limit_tier pro
supacloud gateway rebuild --ref abc123 --clean
```

For server installation, SSH diagnostics, and tenant administration, use `@supacloud/admin`.

<!-- Keep Reactive Integration coverage active for CLI package changes. -->
