# Dev VM Environment



Development VMs carry their own set of environment variables, separate from the
[function and compute-service env](/docs/ccp/environment-variables). Manage them with
`ccp env`, passing the target VM with `--vm <vm_id>`.

```bash
ccp env list --vm <vm_id>             # list your variables
ccp env get KEY --vm <vm_id>          # reveal one value
ccp env set KEY=value --vm <vm_id>    # add or update (upsert)
ccp env unset KEY --vm <vm_id>        # remove one
ccp env refresh-system --vm <vm_id>   # re-apply server-managed credentials
```

All variables are made available to every command run inside the VM (interactive
shells and `exec`).

## Visibility [#visibility]

Each variable has a visibility level that controls whether its value is readable
back through the API:

| Visibility            | In `ccp env list` | `ccp env get`                   |
| --------------------- | ----------------- | ------------------------------- |
| `plaintext`           | value shown       | value shown                     |
| `sensitive` (default) | value masked      | value shown                     |
| `secret`              | value masked      | **write-only** — never returned |

Set the level when writing:

```bash
ccp env set API_URL=https://api.example.com --plaintext --vm <vm_id>
ccp env set API_KEY=sk-... --secret --vm <vm_id>
```

Omitting the flag keeps an existing variable's level (and defaults a new one to
`sensitive`), so re-setting a secret's value never silently downgrades it.

## Automatic GitHub authentication [#automatic-github-authentication]

On a **development** VM you don't need to configure Git or `gh` — the platform
injects your GitHub credentials automatically when the VM is created:

* **`gh`** is installed and already authenticated.
* **`git`** push/pull over HTTPS authenticates with no prompt (a host-scoped
  credential helper for `github.com` is wired in).
* **Commits** are authored as you (your GitHub name and email), not a
  placeholder bot identity.

These credentials are managed by the server as hidden **system** variables
(`GH_TOKEN`, `GH_HOST`, `GIT_HTTP_*`, `GIT_AUTHOR_*`, `GIT_COMMITTER_*`). They
are applied to the VM but **never returned** by `ccp env list` or `ccp env get`,
and you cannot set or remove them — the `GH_`, `GIT_`, `CCP_`, and `CLUSTA_`
prefixes are reserved.

> **Credentials come from the GitHub account linked to your Cluster account.**
> If you haven't linked GitHub, the VM still provisions — just without Git auth.

### Re-applying after linking or rotating [#re-applying-after-linking-or-rotating]

Credentials are captured at VM-creation time. If you link GitHub (or rotate your
token) afterwards, re-apply them to an existing VM with:

```bash
ccp env refresh-system --vm <vm_id>
```

This recomputes your current GitHub credentials and publishes a new runtime
environment generation for the VM. A background reconciler applies it to the
running VM, so the update is not always instantaneous; commands that depend on
credentials (`exec`, install/update, and the agent shell) fail closed with
`runtime_environment_unavailable` until that exact generation is confirmed
active on the VM. If you later unlink GitHub, running it again clears the
stale credentials the same way.

## Server-managed keys are protected [#server-managed-keys-are-protected]

Beyond the reserved `GH_`/`GIT_`/`CCP_`/`CLUSTA_` prefixes, a VM can also carry
credentials the platform injects when the VM is created — for example a secret
[provisioned for an agent](/docs/agents/vaults-and-mcp) under an ordinary name
like `NOTION_API_KEY`. Like the system variables above, these are hidden: they
never appear in `ccp env list` or `ccp env get`.

You can't overwrite one of these by reusing its name. A `ccp env set` of a key
that's already owned by a server-managed credential is rejected with
`key_reserved_by_system` (400) instead of silently replacing the hidden value —
choose a different name for your own variable.

## Forge CLI on development VMs [#forge-cli-on-development-vms]

Development (and build-machine) VM images also ship `forge` alongside `git` and
`gh`. Unlike GitHub, Forge credentials are **not** enrolled automatically — the
VM's original creator must explicitly request enrollment first, using their
ordinary user authentication (a service identity or the VM's own CCP token
cannot):

```bash
# direct VM
curl -X POST -H "Authorization: Bearer $TOKEN" \
  https://api.clusterbase.dev/api/v1/vms/<vm_id>/credentials/forge

# Sandbox
curl -X POST -H "Authorization: Bearer $TOKEN" \
  https://api.clusterbase.dev/api/v1/sandboxes/<sandbox_id>/credentials/forge
```

A `202 {"ready":false}` response means enrollment is recorded but delivery is
still pending — repeat the same request until it returns `200 {"ready":true}`.
Once ready, use `forge` inside the VM like any other CLI:

```bash
forge repo create my-project --remote forge
git push forge HEAD
git fetch forge
forge branch list
```

The Forge credential is a separate, independently renewable five-minute token
scoped only to Forge — it doesn't grant CCP authority, and it doesn't touch
your existing GitHub `origin` or upstream. Deleting the VM or Sandbox revokes
it the same way GitHub credentials are revoked.

This only covers using the `forge` CLI as a guest inside the VM — it does not
bind a Project to a Forge source or clone one automatically into a fresh VM.
