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).
On this page10
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 / |
| 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
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 page, the box your plan launches, a restore, and a fork never inherit one.
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.
environment ml v1 ──── v2 ──── v3 (latest)
│ │
box kiwi (created v1) ●────────┼──── upgrade ──▶ v3
box mango (created v2) ●──── stays on v2 until upgradedprized 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
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 / answers 409 box_restricted; no connector or secret can be mounted, and the box receives no AWS profile |
| Device keys | No member device SSH keys reach the box; desktop apps that rely on them cannot log in |
| Phone access | POST / 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
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), or ship a deploy key as a secret file and clone in the setup script.
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 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
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
Every cap, the launch budget included, is on Limits.
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: an unknown environment or a restricted-box refusal is CHECK_FAILED (5), a bad value USAGE (2), a taken name CONFLICT (6).
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 / | Make it the default → {environment} |
POST / | {safeForThirdParties?, passSecretMounts?, passDeviceKeys?, passPhoneAccess?} → {environment} |
POST / | {name, value} → {environment}; DELETE /:id/vars/:name removes |
PUT / | {path, content (base64), mode?} → {environment}; DELETE /:id/files?path= removes |
POST / | {repo, branch?, dir?, setupScript?, blocking?} → {environment}; DELETE /:id/repos?repo= removes |
POST / | {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.