# App identity

The immutable app id, name leases, renames, undeploy, and ownership transfer.

Every app has an **immutable id** — `app_` followed by 26 characters — minted when the app is created. It lives in `wrangler.toml`:

```toml
[vars]
DEEPSPACE_APP_ID = "app_01HZXYABCDEFGHJKMNPQRSTVWX"
```

The id **is** the app's durable identity. Data, secrets, collaborators, billing, and custom domains all address the app through it. For an identity-migrated app, the backend maps the id to a permanent private `resourceId` that still owns the physical stores — you never handle the `resourceId` directly; the app id is the one identity you work with.

**Commit `wrangler.toml`.** The app id is not a secret — it is the authorization *key*, not a credential. A collaborator who clones the repo gets working `dev`, `test`, `deploy`, and `secrets` access from the id alone, resolved against their own sign-in.

## Id versus name

The `name` field in `wrangler.toml` is only a **lease** on `<name>.app.space`. It can change without the app losing anything: the id stays, and everything keyed to the id travels along. Treat names as URL labels; treat the id as the app.

In scripts and automation, always prefer the id. A name can be renamed out from under a script; the id cannot.

## Where ids come from

There are exactly three mints, and they all end in the same place — `DEEPSPACE_APP_ID` in `wrangler.toml`:

* **Scaffold** — `npm create deepspace` mints one into the new project.
* **First deploy** — `npx deepspace deploy` in a repo without an id mints one on the spot and writes it to `wrangler.toml`. Commit that change; it is the app's identity record.
* **Explicit init** — `npx deepspace app init` stamps an id into an existing repo without deploying.

## Forking with `--new-id`

```bash
npx deepspace app init --new-id
```

`--new-id` writes a fresh id for a fork: same code, separate data, separate secrets store. Use it when a cloned repo still identifies someone else's app — the tell is a "registered to another user" error on `secrets` or `deploy`.

`--new-id` does **not** change Wrangler's `name` or reserve a URL. Pick the fork's own name before its first deploy, or the deploy will try to claim the original's lease.

## Environments are separate apps

Each `[env.<name>]` block in `wrangler.toml` is its **own app** with its own id, its own Durable Objects, and its own secrets store. Mint that identity with `npx deepspace app init --env <name>`, or let the environment's first deploy mint it. Remove one with `npx deepspace app undeploy --env <name>`.

## Renaming an app

Change `name` in `wrangler.toml` and deploy. The CLI asks you to confirm the rename (or pass `--rename` to pre-confirm):

```bash
npx deepspace deploy --rename
```

The new URL serves immediately and the old one stops. Data, secrets, collaborators, and custom domains follow the id, untouched. In particular, an attached [custom domain](/guides/custom-domains) keeps routing without any re-attach.

**The display name does not travel.** A rename moves the URL; it does not touch the human-readable name your app renders, which lives in two places you edit yourself:

* `src/constants.ts` → `APP_NAME`
* `wrangler.toml` → `[vars].APP_NAME`

Update both and redeploy, or the renamed app keeps showing its old name to users. Every surface says so now: the interactive confirmation, the non-interactive `rename_required` refusal, and the deploy's own success output. In `--json` the success envelope carries `renamedFrom` (the old host) and `staleDisplayName` (the array of locations above), so an agent can fix them without parsing prose.

The old name stays **reserved for you for 30 days** while links drain, then frees up for anyone. Within that window you can rename back at will; after it, the name is fair game.

## Listing your apps

```bash
npx deepspace app list          # every app you can access: id, URL, role, deploy state
npx deepspace app list --json
```

`app list` shows every app you can **access** — the ones you own and the ones shared with you as a [collaborator](/guides/collaborators) — with a `ROLE` column telling them apart. Use it to recover an id you have lost track of — including when a quota refusal names an app you no longer recognize.

## Undeploy and revival

```bash
npx deepspace app undeploy                    # resolves DEEPSPACE_APP_ID from the nearest wrangler.toml
npx deepspace app undeploy --env staging      # that environment's app
npx deepspace app undeploy <app-id-or-name>   # positional, registry-resolved — works from anywhere
npx deepspace app undeploy --yes              # skip the confirmation
```

The positional form takes an app id **or** a live subdomain name, so you can undeploy an app without being in its checkout. A positional target overrides `--env`; omit both to fall back to the surrounding `wrangler.toml`.

**Undeploy confirms at an interactive terminal, defaulting to No.** At a TTY the command asks before the URL goes dark, and the question is the table below in one sentence: it names the app (`Take my-app (app_…) offline now?`), says the URL stops serving immediately and the data — records, messages, canvas state, cron history — is destroyed with the worker, and that secrets and the registration stay with the name reserved for 30 days. Declining refuses with the code `undeploy_declined` and changes nothing. `--yes` skips the prompt. Scripts and agents never see it: a non-TTY stdin or `--json` runs straight through, because the invocation itself is the consent — so an automated undeploy cannot hang waiting for an answer nobody will type. Read the sentence back to whoever asked before answering Yes: it is the only place the loss is stated at the moment of the decision.

Undeploying twice is a no-op the second time, and reported as one: `alreadyUndeployed: true` with an empty `releasedHosts` (human: "was already offline — no URL was serving, so nothing changed"). A real takedown lists the released hosts.

Undeploy deletes the Worker script and releases its routes, then best-effort deletes the recorded auto-provisioned Cloudflare resources. There is no separate data-purge step: **the app's Durable Objects — records, messages, canvas state, cron history — are destroyed with the Worker script as a consequence of its deletion**, and data in auto-provisioned resources can be lost too. What goes and what stays:

| Removed                                                          | Retained                         |
| ---------------------------------------------------------------- | -------------------------------- |
| The deployed Worker                                              | The app id and registry identity |
| Its routes and subdomain serving                                 | Collaborators                    |
| Durable Object data — records, messages, canvas, cron history    | The secrets store                |
| Auto-provisioned resources (best effort; their data can be lost) | The cloud Git repository         |

Because identity survives, a later `deploy` from the same repo **revives** the app. Active apps count against your tier's cap; undeployed ones do not — and revival is quota-checked, so reviving can be refused with `app_quota_exceeded` until you undeploy something else.

Only the owner (or a platform admin) can undeploy. Collaborators cannot — destructive access is deliberately narrower than deploy access.

## Ownership transfer

Transfer is a two-step handshake, GitHub-style: nothing changes until the recipient accepts.

```bash
# Owner:
npx deepspace app transfer offer teammate@acme.com   # 7-day offer; --replace swaps a pending one
npx deepspace app transfer status
npx deepspace app transfer cancel

# Recipient:
npx deepspace app transfer accept --app app_01HZ…    # the commit point — they own it now
```

On acceptance the app moves **as-is** — data, secrets, routes, custom domains — and only the owner (and billing) changes. The recipient must have signed in to DeepSpace at least once; offering to an unknown email fails with `user_not_found`.

**There is no in-product notification.** Tell the recipient the app id out of band — they need it for `transfer accept --app`.

An offer expires after **7 days**. Re-offering to a different email refuses with `confirmation_required` unless you pass `--replace`, which swaps the pending offer wholesale.

### Who can do what

| Action            | Who                                                                                                                                       |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `transfer offer`  | Owner (or platform admin on the owner's behalf). Collaborators can never start one — they could otherwise transfer the app to themselves. |
| `transfer status` | Owner and the named recipient only                                                                                                        |
| `transfer cancel` | Either party                                                                                                                              |
| `transfer accept` | The named recipient only                                                                                                                  |
| `undeploy`        | Owner or platform admin                                                                                                                   |

A collaborator who is the *named recipient* participates as that recipient, not through the collaborator role. Platform admins operate deploy, secrets, and undeploy through their override, but the transfer handshake stays owner-scoped — the one exception is an operator opening an offer on behalf of an unreachable owner, and even then the offer is recorded as the owner's.

## See also

* [Collaborators](/guides/collaborators) — who can ship the app without owning it
* [Source control](/guides/source-control) — the app's one authoritative Git repository
* [Custom domains](/guides/custom-domains) — domains bind to the id, not the name
* [Command reference: `app`](/cli-reference/commands#app)
