# Collaborators

Let other people deploy and operate your app.

Collaborators are DeepSpace users the app **owner** authorizes to work on the app. Ownership never moves: the deployed worker keeps the owner's identity and billing. Authorization keys to the app's immutable `DEEPSPACE_APP_ID`, so there is no per-resource grant and no link step.

**Not the same thing as record collaborators.** The `collaboratorsField` in a [collection schema](/sdk-reference/worker/schemas) controls who can see *rows inside* your app. This page is about who can *ship* the app.

## Managing collaborators

All four subcommands are **owner-only**. Run them from the app checkout, or pass `--app <id or name>`.

```bash
npx deepspace app collaborators list
npx deepspace app collaborators add teammate@example.com
npx deepspace app collaborators cancel teammate@example.com
npx deepspace app collaborators remove teammate@example.com
```

`list` prints two sections - current collaborators, and any live invites with their expiry. With `--json` you get `{ collaborators, pending }`.

**Collaborators get owner-equivalent deploy and secrets access.** They can read and overwrite every secret in the app's store and ship any code they like to your production URL, billed to you. Only add people you trust.

## Adding someone

`add` has two paths, depending on whether the email already belongs to a DeepSpace user.

The email is an existing DeepSpace user

They become a collaborator immediately. The command prints `✓ <email> can now deploy <app>`.

The email has no DeepSpace account yet

The server creates a **pending invite** and emails the person a `/join/<token>` link. The invite is billed to you as a transactional email, and expires after 7 days.

They must open that link, sign in with the invited address, and explicitly accept. Signing in to DeepSpace some other way does **not** grant access.

Re-running `add` while an invite is still live returns `already_invited`: no second email is sent and you are not charged again. To reset an invite, `cancel` it and then `add` again.

### Failures you may see

| Code                                  | Meaning                                                                                                                      |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `test_account_cannot_be_collaborator` | `@deepspace.test` accounts can never be collaborators. Use a real account.                                                   |
| `insufficient_credits`                | Inviting a brand-new email sends a billed transactional email. Top up and retry.                                             |
| `invite_email_failed`                 | Transient - the invite email could not be sent. **You were not charged**; the pending row is rolled back. Retry in a moment. |
| `no_pending_invite`                   | `cancel` was given an email with no live invite.                                                                             |
| `not_a_collaborator`                  | `remove` was given an email that isn't on the app.                                                                           |

Inviting a stranger by email works. That is **not** true of [`app transfer offer`](/cli-reference/commands#app-transfer), which refuses with `user_not_found` unless the recipient has signed in to DeepSpace at least once.

## What a collaborator can and can't do

| Action                                                           | Allowed                                                      |
| ---------------------------------------------------------------- | ------------------------------------------------------------ |
| `deploy` (including `--env`)                                     | **Yes** - on-behalf. Billing stays with the owner.           |
| `dev start` / `test run`                                         | **Yes**                                                      |
| `secrets list` / `get` / `download` / `pull`                     | **Yes** - every config in the app's store                    |
| `secrets set` / `upload` / `delete`, `configs create` / `delete` | **Yes** - writes are audited under the collaborator's own id |
| `app undeploy`                                                   | No - owner (or platform admin) only                          |
| `app transfer offer`                                             | No - owner only                                              |
| `app collaborators add` / `remove` / `cancel`                    | No - owner only                                              |
| `app source` (change source authority)                           | No - owner only                                              |

A collaborator who is the *named recipient* of an ownership transfer may accept it - but as that recipient, not through the collaborator role.

### On-behalf deploys

When a collaborator deploys, the CLI prints `Deployed on behalf of owner <id>`. The release ships their code plus the store's secrets, and is attributed to the owner for billing. Nothing about ownership changes.

## Getting started as a collaborator

You don't need to link or claim anything. The app's `wrangler.toml` already carries its `DEEPSPACE_APP_ID`, which is the whole authorization key.

Get the code

```bash
npx deepspace clone <app-name>
cd <app-name>
npm install
```

Or clone the GitHub repo if the app uses [GitHub source](/cli-reference/commands#app-source).

Sign in

```bash
npx deepspace auth login
```

Work normally

```bash
npx deepspace dev start
npx deepspace test run
npx deepspace deploy
```

No linking step. Access is resolved from the app id on every request.

**You must be told the app name or id out of band.** `deepspace app list` shows only apps **you own** - there is currently no command that lists apps shared *with* you. Ask the owner for the app name (or get the repo, whose `wrangler.toml` carries the id).

If you actually wanted your own separate copy rather than to collaborate, run `npx deepspace app init --new-id` to fork the checkout into a distinct app with fresh data and a fresh secrets store.

## Revoking access

```bash
npx deepspace app collaborators remove teammate@example.com
```

Removal takes effect immediately for authorization: the next `deploy`, `secrets`, or `dev` call from that user is refused with a 403 `Not the app owner or a collaborator`.

**Revocation is not retroactive.** It stops future access; it does not undo what the collaborator already did. A collaborator could have read every secret in the store while authorized, so **rotate any secrets they had access to** after removing someone. It also does not roll back releases they shipped - use [`rollback`](/cli-reference/commands#rollback) for that.

To rescind an invite that was never accepted, use `cancel` rather than `remove` - the person is not a collaborator yet, so `remove` refuses with `not_a_collaborator`.

## Troubleshooting

**403 `Not the app owner or a collaborator` on deploy or secrets.** Either you were never added, your access was revoked, or you are signed in as a different account than the one that was invited. Check with `npx deepspace auth whoami`.

**The invitee never got the email.** Confirm the invite is live with `collaborators list`. If a pending row exists but no email arrived, `cancel <email>` and then `add` again - a re-`add` against a live row short-circuits without sending.

## See also

* [Command reference: `app collaborators`](/cli-reference/commands#app-collaborators)
* [Deployment](/concepts/deployment) - what a release actually contains
* [Permissions](/concepts/permissions) - in-app roles, which are a separate system
