# Projects



A Project is the organization-level container above your deployable Apps. Every App belongs to exactly one Project — when the hierarchy shipped, the platform backfilled one Project per existing App, so nothing you had deployed moved or changed names. Databases, stores, and compute services can be attached to a Project or left standalone.

Most of the time you never manage Projects by hand: a fresh `ccp deploy` creates a same-named Project for you (see [How deploy uses Projects](#how-deploy-uses-projects)). The `ccp project` commands exist for the rest — listing what an organization has, preparing an empty Project, or removing one and everything it owns.

## Commands [#commands]

### `ccp project ls` [#ccp-project-ls]

```bash
ccp project ls [--org-id <org>]
```

Alias: `ccp project list`.

Lists the organization's Projects, one per line: name and id.

### `ccp project create` [#ccp-project-create]

```bash
ccp project create <name> [--org-id <org>]
```

Creates an empty Project. Names are normalized to lowercase kebab-case — `My App` becomes `my-app`.

### `ccp project rm` [#ccp-project-rm]

```bash
ccp project rm <name> [--org-id <org>] [-y|--yes]
```

Alias: `ccp project remove`.

Deletes the Project named exactly `<name>` — **and all of its Apps and Deployments**. Attached databases, stores, and compute services are detached, not deleted. The command asks for confirmation; non-interactively it refuses to run without `--yes`.

## Org resolution [#org-resolution]

All three commands resolve the organization the same way as other org-scoped commands: `--org-id` → the local project config → `CCP_ORG_ID` → a saved default (`ccp org use`, see [Organizations](/docs/ccp/organizations)) → single-org auto-pick → the interactive picker (an error in headless mode).

## How deploy uses Projects [#how-deploy-uses-projects]

A fresh `ccp deploy` — no local link yet — creates a same-named Project and App in the resolved organization. Names aren't identity: if an App with that name already exists there, the deploy errors instead of attaching to it, and you pass `--app-id` to select the intended App explicitly. The Project is likewise always created rather than reused, so a same-named Project in the organization makes the deploy error even when no App shares the name. The resulting `project_id` is persisted alongside `app_id` and `organization_id` in the gitignored `.ccp/config.json`. See [Deploying → First Deploy](/docs/ccp/deploying#first-deploy).

## Attaching optional resources [#attaching-optional-resources]

Databases, stores, and compute services don't have to live in a Project — attachment is optional. A resource carries a nullable `project_id`: set it to attach, clear it to detach, and pass `project_id` as a query filter on the list endpoints to see only one Project's resources. Attach and detach run through the API today; there is no CLI flag for it yet. Deleting a Project detaches these resources rather than destroying them.

## Transferring ownership between organizations [#transferring-ownership-between-organizations]

A Project — with its Apps, deployments, Stores, databases, and Compute — can move to a different organization without restarting anything. Resource IDs, addresses, data, and credentials are preserved; only ownership and future billing move. Past usage stays billed to the source organization. There's no `ccp project` flag for this yet: use the Portal's Project transfer action, or call the API directly. The actor must own both organizations.

A linked GitHub or Forge repository stays linked when the actor can resolve the same repository identity; a Project without a source can still transfer, but needs a source set up again before you can deploy to it. Existing database and application credentials keep working after transfer until explicitly revoked.

Before transferring, preview the move — it lists attached resources and any blockers that need resolving first, such as a busy resource, a conflicting name or Store quota in the destination, a cross-Project database binding, organization credentials installed on a backing VM, or a modern organization-owned domain binding (domains have a separate handoff and aren't covered by this transfer):

```
POST /api/v1/serverless/projects/{project_id}/transfer/preview
{ "source_organization_id": "...", "destination_organization_id": "..." }
```

Once blockers are clear, confirm with the same fields plus a fresh UUID `id`:

```
POST /api/v1/serverless/projects/{project_id}/transfer
{ "source_organization_id": "...", "destination_organization_id": "...", "id": "<uuid>" }
```

The response is an immutable receipt. If the response is lost, or you get back `transfer_publication_pending`, retry with the exact same body and `id` — that replays the original receipt rather than transferring again. There's no status-polling or credential-replacement endpoint. After a transfer, switch your local CCP organization selection to the destination; the Project and App IDs don't change.
