# Environments

> What a new box inherits, kept as versions: repos to clone, a setup script, env vars, secret files, and which of your credentials it may receive (CLI and API only).

URL: https://prized.dev/docs/environments

## What a box receives [#what-a-box-receives]

Pick an environment at create time (`--environment` on `prized box new`, `environment` on `POST /api/v1/boxes`) or let the default apply. The box receives, in order:

| Item         | When              | Where it lands                                                                               |
| ------------ | ----------------- | -------------------------------------------------------------------------------------------- |
| Env vars     | first boot + live | `/etc/doppel/box-env.sh`, sourced by every login shell                                       |
| Repos        | first boot        | `~/<dir>` (default: the repo name), cloned as your box user, blocking repos first            |
| Setup script | first boot        | Runs once as your box user in your home, after the repos; log in `/var/log/doppel-setup.log` |
| Secret files | first boot + live | `~/<path>`, owned by your box user, mode 0600 unless you set another                         |

Env vars and secret files stay live: a change reaches the box within seconds, for new login shells. Secret files travel over the box's authenticated channel, never in the launch configuration, and the CLI shows only their path and size.

## The default environment [#the-default-environment]

There is none until you mark one: `--default` when creating, or `prized environments default <name>`. A box created from the CLI or API without naming an environment then takes it (`environment: null` on the API opts out). A box from the dashboard's [New box](https://prized.dev/docs/dashboard#new-box) page, the box your plan launches, a restore, and a fork never inherit one.

## Versions and upgrade [#versions-and-upgrade]

Every save that changes what a box inherits mints a new immutable version (a rename or new description does not), and a box keeps its version until you upgrade it. Names are unique in the workspace regardless of case: `prod` and `Prod` are one name.

```text title="Versions"
environment ml        v1 ──── v2 ──── v3 (latest)
                       │        │
box kiwi (created v1)  ●────────┼──── upgrade ──▶ v3
box mango (created v2)          ●──── stays on v2 until upgraded
```

`prized environments upgrade <name>` pins boxes to the latest version:

| On upgrade                                           | What happens                                                                                                             |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Env vars and secret files                            | Re-apply within seconds; a dropped file or var is removed from the box.                                                  |
| Repos and the setup script                           | Do not re-run.                                                                                                           |
| A safe-for-third-parties version                     | Makes the box restricted, permanently: mounted secrets stop resolving, its phone password is revoked, device keys leave. |
| A box whose own vars would not fit the launch budget | Skipped (named in `skipped`); it keeps its version.                                                                      |

Up to 50 versions are kept per environment; older ones no live box pins are pruned.

## Safe for third parties [#safe-for-third-parties]

A box for someone else (a contractor, a demo, an agent you do not fully trust) should hold none of your credentials: switch on safe for third parties on its environment (`prized environments set <name> --safe-for-third-parties true`), or tick **Restricted** when creating one box. Four channels close:

| Channel                | What is refused                                                                                                                                                                                  |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Secret mounts          | `POST /api/v1/boxes/:id/secrets` answers `409 box_restricted`; no [connector](https://prized.dev/docs/connectors) or secret can be mounted, and the box receives no [AWS profile](https://prized.dev/docs/aws)                       |
| Device keys            | No member device SSH keys reach the box; desktop apps that rely on them cannot log in                                                                                                            |
| Phone access           | `POST /api/v1/boxes/:id/mobile-ssh` answers `409 box_restricted`                                                                                                                                 |
| CLI credential handoff | `prized agents handoff` and the agent, secrets and data steps of `prized setup` refuse with `CHECK_FAILED` (exit 5); `prized setup` also keeps `.env` files out of every sync session to the box |

Env vars still apply. A safe environment cannot carry secret files (`safe_environment_has_secret_files`), and a restricted box never receives any, at create or on any upgrade, whatever environment it runs.

Restricted is one-way: set at create (`restricted: true`, or a safe environment), by an upgrade onto a safe version, or inherited by a restore, and never unset. The three channels can also be closed one at a time on an environment without restricting the box.

## Repos [#repos]

A repo is `owner/name` (GitHub) or an `https://` clone URL, with an optional branch, directory under your home (default: the repo name), and setup script run inside the clone. A **blocking** repo clones before the others and before the environment's setup script.

Clones are plain https, so no credential lands in `.git/config`, and an existing directory is left alone. For a private repo, clone on the box once `gh` is signed in there ([Credentials](https://prized.dev/docs/credentials)), or ship a deploy key as a secret file and clone in the setup script.

## Vars and secret files [#vars-and-secret-files]

Env vars are not secret: shell-identifier names, single-line values, shown in the CLI. Put anything sensitive in a [secret](https://prized.dev/docs/connectors) or a secret file: up to 64 KiB at a path relative to your home (`.aws/credentials`, `.npmrc`), encrypted at rest, decrypted only for its box, written 0600 or the mode you set, up to 16 per environment.

## Per-box vars [#per-box-vars]

Set a box's own vars with `env` at create (`POST /api/v1/boxes` with `{"env": {"NAME": "value"}}`) or **Variables** on the box page's **Settings** tab (`PATCH /api/v1/boxes/:id` with `{"env": {...}}`, which replaces the set). A per-box var wins over an environment var of the same name. Environment plus per-box vars must fit the launch budget of about 16 KiB compressed; a create or PATCH that would not fit is refused with `environment_too_large`.

## Limits [#limits]

Every cap, the launch budget included, is on [Limits](https://prized.dev/docs/limits#environments).

## CLI [#cli]

`prized environments` (alias `prized envs`); every command takes `--json`.

| Command                                                                                                                                                                                      | What it does                                                                                                                             |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `prized environments ls`                                                                                                                                                                     | One row per environment. JSON: `{"environments": [...]}`                                                                                 |
| `prized environments info <name>`                                                                                                                                                            | Channels, repos, vars, secret files (path, mode, size), versions with box counts. JSON: `{"environment": {...}}`                         |
| `prized environments new <name> [--description] [--setup-script FILE] [--default]`                                                                                                           | Create; `--default` marks it the default for new boxes                                                                                   |
| `prized environments default <name>`                                                                                                                                                         | Make it the default for new boxes                                                                                                        |
| `prized environments rm <name>`                                                                                                                                                              | Delete (asks; `-y` skips); boxes keep their version                                                                                      |
| `prized environments set <name> --safe-for-third-parties true\|false --secret-mounts true\|false --device-keys true\|false --phone-access true\|false [--description] [--setup-script FILE]` | Flip the switches or replace the description or script                                                                                   |
| `prized environments set-var <name> K=V` / `rm-var <name> K`                                                                                                                                 | Set or remove one env var                                                                                                                |
| `prized environments set-file <name> <path> --from FILE\|- [--mode 0600]` / `rm-file <name> <path>`                                                                                          | Set a secret file from a local file or stdin, or remove it                                                                               |
| `prized environments add-repo <name> owner/repo [--branch] [--dir] [--setup-script FILE] [--blocking]` / `rm-repo <name> owner/repo`                                                         | Add, replace, or remove a repo                                                                                                           |
| `prized environments upgrade <name> [--box B]...`                                                                                                                                            | Pin every live box (or just `--box` ones) to the latest version. JSON: `{"environment", "version", "upgraded": [ids], "skipped": [ids]}` |

The CLI's environment object carries the API row's fields in snake\_case (`safe_for_third_parties`, `secret_files`, `latest_version`, `versions` with `boxes` counts).

Exit codes follow the [CLI table](https://prized.dev/docs/cli#for-agents-and-scripts): an unknown environment or a restricted-box refusal is `CHECK_FAILED` (5), a bad value `USAGE` (2), a taken name `CONFLICT` (6).

## API [#api]

All routes take your session or a `dcp_` CLI bearer, are scoped to your workspace, and take JSON bodies.

| Route                                   | Body / answer                                                                                                                                                                                                                    |
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /api/v1/environments`              | `{"environments": [row]}`; a row carries `isDefault`, the four switches, `latestVersionNo`, `repos`, `secretFiles` as `{path, mode, size}`, `versions` as `{versionNo, createdAt, boxCount}`                                     |
| `POST /api/v1/environments`             | `{name, description?, baseTier?, setupScript?, envVars?, repos?, secretFiles?: [{path, content (base64), mode?}], safeForThirdParties?, passSecretMounts?, passDeviceKeys?, passPhoneAccess?, isDefault?}` → 201 `{environment}` |
| `PATCH /api/v1/environments/:id`        | Any of the above; a config field mints a version → 200 `{environment}`                                                                                                                                                           |
| `DELETE /api/v1/environments/:id`       | 204; boxes keep their version                                                                                                                                                                                                    |
| `POST /api/v1/environments/:id/default` | Make it the default → `{environment}`                                                                                                                                                                                            |
| `POST /api/v1/environments/:id/toggles` | `{safeForThirdParties?, passSecretMounts?, passDeviceKeys?, passPhoneAccess?}` → `{environment}`                                                                                                                                 |
| `POST /api/v1/environments/:id/vars`    | `{name, value}` → `{environment}`; `DELETE /:id/vars/:name` removes                                                                                                                                                              |
| `PUT /api/v1/environments/:id/files`    | `{path, content (base64), mode?}` → `{environment}`; `DELETE /:id/files?path=` removes                                                                                                                                           |
| `POST /api/v1/environments/:id/repos`   | `{repo, branch?, dir?, setupScript?, blocking?}` → `{environment}`; `DELETE /:id/repos?repo=` removes                                                                                                                            |
| `POST /api/v1/environments/:id/upgrade` | `{boxIds?}` → `{upgraded: [ids], skipped: [ids], latestVersionNo}`                                                                                                                                                               |

On the box routes, `POST /api/v1/boxes` takes `environment` (id or name; `null` = none; absent = your default), `env: {K: V}` and `restricted`; `PATCH /api/v1/boxes/:id` with `{env}` replaces the per-box vars; `GET /api/v1/boxes/:id` carries `environment: {id, name, versionNo, latestVersionNo}`, `envVars` and `restricted`.

Errors: `400 invalid_repo`, `invalid_file`, `invalid_env_var`, `invalid_env`, `too_many_repos`, `too_many_files`, `environment_too_large` (also from the box create and PATCH), `safe_environment_has_secret_files`; `404 not_found`; `409 name_taken`; `409 box_restricted` from the mount and phone-access routes.
