# Bounded Deploy

> Ship and configure a Bounded app: CLI setup, browser-backed developer login, verify and deploy, hosted frontend publishing, environments, source sync, domains, bounded.json, collaborators, and release recovery. Part of the Bounded skill family.

- Skill: `bounded-sh/bounded-deploy` (Agent Skill, multi-file: 10 files)
- Install (CLI): `npx skillmds@latest add bounded-sh/bounded-deploy`
- Raw SKILL.md: https://api.skillmd.com/api/skills/bounded-sh/bounded-deploy/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: bounded-sh (https://skillmd.com/u/bounded-sh)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/bounded-sh/bounded-deploy

---


# Bounded deploy

Use this skill to initialize, verify, ship, configure, and share a Bounded app.
Policy authoring belongs in **bounded-backend**; client code and app-user auth
belong in **bounded-frontend**.

## Default workflow

For a new project or a normal release, read
[docs/quickstart.md](docs/quickstart.md). The expected path is:

```bash
bounded init
bounded verify
bounded deploy --create --name my-app
```

`bounded init` reuses a saved web session or opens hosted browser login. It
writes public `bounded.json` and `policy.json`; credentials never belong in
either file.

## Task router

Read only the reference needed for the current task.

| Task | Read |
|---|---|
| First setup, normal verify/deploy, publish a site, multiple app IDs from one project | [docs/quickstart.md](docs/quickstart.md) |
| Normal web account login, session refresh, headless OTP, account switching | [docs/accounts.md](docs/accounts.md) |
| Hosted web frontend, preview, private/public access | [frontend-hosting.md](../bounded-frontend/docs/frontend-hosting.md) |
| Multi-environment policies: per-env app id, constants, schedule cadence, function scoping | [docs/environments.md](docs/environments.md) |
| Build an app from a prompt, iterate with `edit`, watch/cancel/gate a run | [docs/cli-reference.md](docs/cli-reference.md) (Prompt-driven builds) |
| Source sync, `--with-source`, clone, pull | [docs/source-sync.md](docs/source-sync.md) |
| Port an existing app (Supabase, Firebase, Express, Next, a key-holding backend) onto Bounded; decide what becomes rules, functions, schedules, or a capability | [docs/porting-an-existing-app.md](docs/porting-an-existing-app.md) |
| Third-party API discovery, readiness, and requests: `bounded services search/describe/request/status` | [docs/cli-reference.md](docs/cli-reference.md) (Capabilities) |
| Open dry run and rehearsal for an oApp: `bounded oapp preflight`, `bounded oapp rehearse` | [docs/cli-reference.md](docs/cli-reference.md) (Open Apps) |
| Custom domains and vanity slugs | [docs/domains.md](docs/domains.md) |
| Share, access, owner mismatch, `401`/`403` | [docs/access-playbook.md](docs/access-playbook.md) |
| Delete an app permanently (browser-confirmed, owner only) | [docs/cli-reference.md](docs/cli-reference.md) (`apps delete`) |
| Uncommon command or exact flag lookup | [docs/cli-reference.md](docs/cli-reference.md) |
| Local signing keys, profiles, CI key auth, legacy key-owned apps | [docs/key-and-account-safety.md](docs/key-and-account-safety.md) |

Do not load the local-key reference during normal onboarding. It is an advanced
alternative for users who explicitly request local signing, CI key auth, an
account profile, or recovery of an existing key-owned app.

## Incident router

- `503` + `proof_substrate_unavailable` (`retryable: true`) from `bounded verify`: the prover lane is warming up or busy.
  This response does not establish policy correctness.
  Retry the same policy UNCHANGED using the bounded protocol below.
  Wait 30 seconds, then rerun the same `bounded verify`; make at most 3 attempts total, meaning the initial attempt plus 2 retries.
  If the third attempt still returns this error, stop and tell the user the proving service is degraded.
  Include the `correlationId` when present.
  Do not edit the policy, switch accounts, or create a new app.
  This retry protocol applies only to `bounded verify`.
  Never use it to retry `bounded deploy`.
  In particular, never retry `bounded deploy --create` because it can create another app.
- `deploy_in_progress`, `operationId`, or `recoveryCommand`: use only the exact
  owner-visible recovery command with unchanged inputs, then let the CLI poll.
  See [deploy recovery](docs/cli-reference.md#recover-an-in-progress-policy-deploy).
- An error with `code`/`state` but NO `recoveryCommand` is terminal for that
  operation - `410 policy_operation_unrecoverable` and the abandoned, superseded,
  target-mismatch and manual-intervention states. Re-running the recovery can
  never commit it; run a fresh `bounded deploy` (or escalate, when the message
  says operator review). Never invent a recovery command for these.
- `onchain_creation_pending`, `onchain_creation_unreadable`, or
  `onchain_creation_superseded` (all `409`): the app's mainnet creation never
  finished - its on-chain owner is not proven at finalized yet, so nothing can
  be deployed to it and nothing has been signed or spent.
  Re-run the SAME `bounded deploy` for that app id: the platform resumes the
  original creation and lifts the fence as soon as the account is finalized.
  Never re-run `--create`, and never create a replacement app - the first app's
  on-chain account is already paid for and a second one strands that rent.
- `onchain_creation_owner_conflict` (`409`): the app's on-chain account is
  finalized under a wallet the creation did not intend. That is an integrity
  fault, not a state to retry; escalate for operator review.
- Unsure which applies, or unsure whether a fresh deploy is safe: run the
  read-only `bounded deploy status --json` first. It reports what holds the
  deploy slot and a `freshDeploySafe` verdict, and it never mutates anything.
- Before a deploy, to know whether it will LAND (not just whether the slot is
  free): run the read-only `bounded deploy preflight --json`. Deploys are metered
  against the app's credit balance - each leg charges its actual Cloudflare infra
  cost (sub-cent; no per-tier deploy cap, no minimum), so an out-of-credit app is
  refused. Preflight reports the credit balance + a `would_likely_admit/refuse/
  unknown` verdict so you can tell the user "this will land" or "top up first"
  before spending time on the deploy. It never mutates anything.
- `402 deploy_credit_insufficient` on `site deploy`: billing, not identity. The
  deploying account has no spendable credit for the deploy's infra cost. Run
  `bounded billing status`, add credit with `bounded billing topup --credits <n>`,
  then retry under the same identity. Switching accounts never helps here.
- `site_control_denied` (a `403`), wrong owner, or unexpected `401`/`403`: run
  `bounded whoami` and `bounded access --app-id <id>` before changing identity.
  See [access playbook](docs/access-playbook.md).
- `project_limit_exceeded`: inventory apps; never delete or repurpose one
  automatically. If the user decides an app should go, `bounded apps delete`
  exists but always requires the human to confirm in the browser. See
  [billing](../bounded/docs/billing.md).
- `boundary_violation`: changing accounts will not bypass an app boundary. Use
  the boundary-lock section of the access playbook.

## Release rules

- Read `bounded.json` first in an existing app.
- Regenerate a generated `policy.json` before both verify and deploy.
- `bounded verify` is the fast proof loop; `bounded deploy` still fails closed
  if the exact deployed policy does not pass its release gate.
- After a release-critical deploy, use
  `bounded apps inspect --app-id <id> --json` to confirm the active policy and
  runtime publication before measuring behavior.
- Resolve the hosted URL from the exact site receipt or
  `bounded domains list --app-id <id> --env <environment> --json`. Do not invent
  a hostname.
- Share control-plane access with
  `bounded share <email-or-wallet> --role admin|developer|viewer|billing`.
- Do not commit credentials, web sessions, refresh tokens, or provider secrets.

