Prized docs

Environments.

What a new box inherits, kept as versions on your account. Repos to clone, a setup script, env vars, secret files, and which of your credentials a box may receive.

What a box receives

An environment is what a new box starts as. Pick one at create time (the dashboard form, or environment on POST /api/v1/boxes), or let the default apply. The box then receives, in order, at first boot:

ItemWhenWhere it lands
Env varsfirst boot + live/etc/doppel/box-env.sh, sourced by every login shell
Reposfirst boot~/<dir> (default: the repo name), cloned as your box user, blocking repos first
Setup scriptfirst bootruns once as your box user in your home, after the repos; log in /var/log/doppel-setup.log
Secret filesfirst boot + live~/<path>, owned by your box user, mode 0600 unless you set another

Env vars and secret files are also kept live: the box agent applies changes within seconds, without a reboot. Repos and the setup script run at first boot only.

The environment's vars and per-box vars (below) are written to /etc/doppel/box-env.sh, which login shells source. A shell you already have open keeps its old values; open a new one to see a change.

Secret files never ride the launch user-data (which the instance can read from its metadata service): the box agent fetches them over its authenticated channel, the same way brokered secrets travel, and writes them atomically, owned by your box user, never through a symlinked folder. The dashboard and CLI show a secret file's path and size only; its contents are write-only once saved.

Boxes created before environments existed keep whatever their first boot wrote: the agent's live channel leaves them alone (it still waits on the server between polls like every other box, so nothing spins). Give such a box per-box vars and the agent keeps its boot-time file in place, sourced first, and writes the live file beside it.

The default environment

Nothing is a default until you mark one: tick "Make it the default for new boxes" when creating an environment, use "Make default" on its card, or run prized environments default <name>. Once marked, a box created from the dashboard form or POST /api/v1/boxes without naming an environment takes it; pick none (the dashboard) or send environment: null (the API) for a box with no environment at all. Boxes Prized creates for you (the box your plan launches, a snapshot restore, a fork) never inherit an environment. Deleting the default leaves you with no default.

Versions and upgrade

Every save that changes what a box inherits (script, vars, repos, files, or the credential switches) mints a new immutable version. A running box keeps the version it was created from until you upgrade it; renaming an environment or changing its description does not mint a version.

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> (or the Upgrade button on the environment or on a box's page) pins boxes to the latest version:

  • Env vars and secret files re-apply on the box within seconds. A file the new version no longer carries is removed from the box; a var it dropped disappears from box-env.sh.
  • Repos and the setup script are first-boot work and do not re-run.
  • If the new version is safe for third parties, the upgraded box becomes restricted, permanently: secrets already mounted on it stop resolving on the box's next poll, its phone-access password is revoked, and device keys leave on the next heartbeat.
  • A box whose own per-box vars would not fit the launch budget together with the new version is skipped (named in skipped) and keeps its version; trim its vars, then upgrade again.

Each environment keeps up to 50 versions; older ones that no live box pins are pruned. Deleting an environment leaves its boxes exactly as they are, on the version they carry.

Safe for third parties

A box built for someone else (a contractor, a customer demo, an agent you do not fully trust) should hold none of your credentials. Switch on Safe for third parties on the environment, or tick Restricted when creating a single box. Four channels close:

ChannelWhat is refused
Secret mountsPOST /api/v1/boxes/:id/secrets answers 409 box_restricted; no connector or secret can be mounted
Device keysThe box receives no member device SSH keys; desktop apps that rely on them cannot log in
Phone accessPOST /api/v1/boxes/:id/mobile-ssh answers 409 box_restricted; no phone password is minted
CLI credential handoffprized agents handoff and the agent, secrets and data steps of prized setup refuse with CHECK_FAILED (exit 5)

The environment's env vars still apply (they are plain configuration, shown in the open), but a safe environment cannot carry secret files: the save is refused (safe_environment_has_secret_files) until the files are removed, so "none of your credentials" holds for everything Prized could deliver.

Restricted is one-way for a box: it is set at create (restricted: true, or an environment that is safe for third parties), by an upgrade onto a safe version, or inherited by a restore of a restricted box, and it never unsets. The three channels can also be closed one at a time on an environment (secret mounts, device keys, phone access) without making the box restricted; those boxes stay unrestricted and the closed channel simply refuses.

Repos

A repo is owner/name (GitHub) or an https:// clone URL. Each repo has an optional branch, a directory under your home (default: the repo name), an optional setup script that runs inside the clone after it lands, and a blocking flag: blocking repos clone before the others and before the environment's setup script.

Clones are plain https, so public repos work everywhere and no credential ever rides a URL into .git/config. For a private repo, clone on the box once gh is signed in there (prized agents handoff gh or prized setup), or ship a deploy key as a secret file and clone in the environment's setup script.

A repo directory that already exists (a restore, a second boot) is left alone and its clone skipped.

Vars and secret files

Env vars are non-secret by definition: names are shell identifiers, values single-line, and the dashboard and CLI show them. Put anything sensitive in a secret (the broker never exposes the value) or in a secret file.

A secret file is a body of at most 64 KB at a path relative to your home on the box (.aws/credentials, .npmrc, .config/gh/hosts.yml). It is encrypted on the control plane with the secrets key, decrypted only for the box it belongs to, and written 0600 (or the mode you set). Up to 16 files per environment.

Per-box vars

A box can carry its own vars: env at create (POST /api/v1/boxes with {"env": {"NAME": "value"}}), or the Vars editor on the box page (PATCH /api/v1/boxes/:id with {"env": {...}}), which replaces the set. A per-box var wins over an environment var of the same name; everything else from the environment stays. Changes reach the box within seconds and are visible to new login shells.

Per-box vars share the launch budget with the environment: the environment's script, vars and repos plus the box's own vars must fit EC2's 16 KB gzipped user-data as rendered for a snapshot restore (the larger of the two launch shapes, so a box that fits also relaunches from its snapshots), and a create or PATCH that would not is refused with environment_too_large rather than accepted and failed at launch.

Limits

LimitValue
Env vars per environment64, values up to 4096 characters
Per-box vars100 names, 64 KB in all, and inside the launch budget together with the environment
Repos per environment16
Secret files per environment16, each up to 64 KB
Setup script16 KB; the whole launch, as rendered for a snapshot restore (the larger launch shape), must fit EC2's 16 KB gzipped user-data budget, or the save is refused (environment_too_large)
Versions kept per environment50 (older unpinned ones are pruned)

CLI

prized environments (alias prized envs) manages environments from the terminal. Every command takes --json.

CommandWhat it does
prized environments lsOne row per environment: default, version, boxes, repos, vars, files, third-party switch. 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; each change mints a version
prized environments set-var <name> K=V / rm-var <name> KSet or remove one env var
prized environments set-file <name> <path> --from FILE|- [--mode 0600]Set a secret file from a local file or stdin; rm-file <name> <path> removes it
prized environments add-repo <name> owner/repo [--branch] [--dir] [--setup-script FILE] [--blocking]Add or replace a repo; rm-repo <name> owner/repo removes it
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]}; skipped = not this environment's live boxes, or their vars would not fit the new version

Environment JSON (info, and each row of ls):

{"id": "env_…", "name": "ml", "default": true, "safe_for_third_parties": false, "secret_mounts": true, "device_keys": true, "phone_access": true, "setup_script": "…", "vars": [{"name": "A", "value": "1"}], "secret_files": [{"path": ".npmrc", "mode": "0600", "size": 42}], "repos": [{"repo": "owner/name", "branch": "main", "dir": null, "setup_script": null, "blocking": true}], "latest_version": 3, "versions": [{"version": 3, "created": "…", "boxes": 2}], "boxes": 2}

Exit codes follow the CLI table: an unknown environment or a restricted-box refusal is CHECK_FAILED (5), a bad value is USAGE (2), a taken name is CONFLICT (6).

API

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

RouteBody / 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/:idAny of the above; a config field mints a version → 200 {environment}
DELETE /api/v1/environments/:id204; boxes keep their version
POST /api/v1/environments/:id/defaultMake it the default → {environment}
POST /api/v1/environments/:id/toggles{safeForThirdParties?, passSecretMounts?, passDeviceKeys?, passPhoneAccess?}{environment}
POST /api/v1/environments/:id/vars{name, value} (add or replace) → {environment}; DELETE /:id/vars/:name removes
PUT /api/v1/environments/:id/files{path, content (base64), mode?} (add or replace) → {environment}; DELETE /:id/files?path= removes
POST /api/v1/environments/:id/repos{repo, branch?, dir?, setupScript?, blocking?} (add or replace) → {environment}; DELETE /:id/repos?repo= removes
POST /api/v1/environments/:id/upgrade{boxIds?}{upgraded: [ids], skipped: [ids], latestVersionNo}
POST /api/v1/boxesGains environment (id or name; null = none; absent = your marked default, if any), env: {K: V}, restricted
PATCH /api/v1/boxes/:id{env: {K: V}} replaces the per-box vars
GET /api/v1/boxes/:idGains environment: {id, name, versionNo, latestVersionNo}, envVars, restricted

Errors: 400 invalid_repo, invalid_file, invalid_env_var, invalid_env, too_many_repos, too_many_files, environment_too_large (also from POST /api/v1/boxes and PATCH /api/v1/boxes/:id when the per-box vars would not fit the launch), safe_environment_has_secret_files; 404 not_found for an environment, var, file or repo that is not there; 409 name_taken; 409 box_restricted from the mount and phone-access routes.

Something unclear or out of date?

On this page