# Overview > Prized gives you a dedicated Linux box in the cloud. Your files appear in a folder on your machine, box ports appear on your localhost, and terminal sessions survive closing the laptop. URL: https://prized.dev/docs ## How it works [#how-it-works] You edit and run everything on the box over SSH. Three things tie it to your laptop: * **Sync.** A folder on your machine mirrors `~/code/` on the box, both ways, .git included. * **Ports.** A dev server you start on the box shows up on your laptop's localhost within seconds. * **Sessions.** Your shell runs in tmux on the box, so a closed lid ends the connection, not the work. ## The pieces [#the-pieces] | Piece | What it is | | -------------- | --------------------------------------------------------------------------------------------------------------------------- | | `prized` | The CLI on your laptop (macOS, Linux, or Windows). | | `prizedd` | A background daemon on your machine. Runs sync and mirrors box ports onto localhost. | | `prized-agent` | A service on the box. Finds listening ports, reports vitals, lists tmux sessions. | | Dashboard | [prized.dev](https://prized.dev/dashboard). Create boxes, watch their state, manage billing; every screen is on [Dashboard](https://prized.dev/docs/dashboard). | ## What's on the box [#whats-on-the-box] Ubuntu, with root. Preinstalled: Docker (compose and buildx), git, tmux, mosh, ripgrep, jq, the GitHub CLI, Claude Code, and Codex; Cursor's CLI installs on demand; the full inventory, sizes, and disks are on [Machines](https://prized.dev/docs/machines). `prized setup` hands your own agent sign-ins over from your machine, so nothing on the box needs a fresh OAuth flow. See [Credentials](https://prized.dev/docs/credentials). ## For agents [#for-agents] Every page here is also plain Markdown: add `.md` to its URL (this page is [/docs.md](https://prized.dev/docs.md)), or request the page URL with `Accept: text/markdown`. The buttons under each title copy the page as Markdown or open it in ChatGPT, Claude, or Cursor. * **Index.** [/llms.txt](https://prized.dev/llms.txt) lists every page with a one-line summary, in this order. * **Everything.** [/llms-full.txt](https://prized.dev/llms-full.txt) is the whole set in one file. * **Search.** `GET /api/search?query=` returns JSON hits, each with the page URL and the matching heading or paragraph. * **Setup.** To let an agent install and set Prized up for you, paste the prompt on [Agent setup](https://prized.dev/docs/agent-setup). ## All pages [#all-pages] The same groups as the sidebar. ### Get started [#get-started] - [Quickstart](https://prized.dev/docs/quickstart): Signup to a synced project in about five minutes. - [Agent setup](https://prized.dev/docs/agent-setup): One prompt that lets your coding agent do the setup. - [Bring your environment](https://prized.dev/docs/environment): Your shell, toolchains, CLIs, Docker images, and databases, moved with a few yes or no questions. ### Connect [#connect] - [Sync](https://prized.dev/docs/sync): Two-way file sync between the box and your machine. - [Mount](https://prized.dev/docs/mount): The whole box in Finder, nothing copied. - [Ports](https://prized.dev/docs/ports): Servers on the box appear on your localhost. - [Sessions](https://prized.dev/docs/sessions): Terminal sessions that survive a closed lid. - [Run commands and files](https://prized.dev/docs/exec): Run a command, leave one running, or copy files, from the CLI or any script. - [Desktop](https://prized.dev/docs/desktop): A Linux desktop, or a single Chrome window, from the box in your browser. ### Agents [#agents] - [Agents](https://prized.dev/docs/agents): Claude Code, Codex, or Cursor working on the box overnight. - [Prompt an agent remotely](https://prized.dev/docs/remote-agents): Send the box's agent a prompt from your machine, follow it, interrupt it. - [Codex remote](https://prized.dev/docs/codex): The ChatGPT desktop app running Codex tasks on your box. - [Claude Desktop remote](https://prized.dev/docs/claude-desktop): The Claude desktop app pointed at your box over SSH. ### Boxes [#boxes] - [Boxes](https://prized.dev/docs/boxes): Create, pause, resize, snapshot, and delete boxes. - [Machines](https://prized.dev/docs/machines): Sizes, disk, Ubuntu 24.04, and what is preinstalled. - [Snapshots](https://prized.dev/docs/snapshots): Snapshot, restore, fork, templates, and schedules. - [Environments](https://prized.dev/docs/environments): What a new box inherits: repos, a setup script, vars, secret files. - [Credentials](https://prized.dev/docs/credentials): Git, cloud, and model credentials live on your box. - [Connectors](https://prized.dev/docs/connectors): Brokered credentials mounted into a box, never written to its disk. ### Reference [#reference] - [CLI](https://prized.dev/docs/cli): Every prized command, flag, and exit code. - [API reference](https://prized.dev/docs/api): Every HTTP route a CLI token can call. - [Dashboard](https://prized.dev/docs/dashboard): Every screen in the web app, and where each setting lives. ### Account [#account] - [Workspaces](https://prized.dev/docs/workspaces): One account for the whole team; join by email domain or invite link. - [Teams](https://prized.dev/docs/teams): Roles, policies, quiet hours, the audit log, scoped secrets, and live activity for a shared workspace. - [Billing](https://prized.dev/docs/billing): Monthly plans, hourly rates, and the balance your plan refills. - [Limits](https://prized.dev/docs/limits): Every hard cap a box or a workspace can hit, and how a refusal reads. - [Data retention](https://prized.dev/docs/data-retention): What pausing, deleting, and closing keep and remove. ### Support [#support] - [Troubleshooting](https://prized.dev/docs/troubleshooting): prized doctor and fixes for the common failures. - [Security and trust](https://prized.dev/docs/security): How a box is reached, what Prized can and cannot see, and how to report a problem. - [Privacy](https://prized.dev/docs/privacy): What Prized records about your usage, and what it never records. --- # Quickstart > Four steps from signup to a box with your project on it and your coding agent signed in. You need a Mac, a Linux machine, or a Windows PC. URL: https://prized.dev/docs/quickstart ## 1. Sign up and create a box [#1-sign-up-and-create-a-box] Sign up at [prized.dev](https://prized.dev/sign-in). New workspaces get [$30 of free credit](https://prized.dev/docs/billing#free-credit), once: the tour has you pick a box size, Nano through Small, and starts it on that credit, no card needed. Each size says how long the credit runs it; a Small, the suggested start, runs about nine days around the clock. Want a bigger box from day one? Pick it on the same step and start its plan; it launches when the payment lands. The box boots while you install the CLI, bills by the hour against the credit while awake, and pauses itself after an hour idle so the credit lasts. When it does, boxes pause and the dashboard asks you to pick a plan; each plan is priced to run one size around the clock, and paused boxes start again from the dashboard. See [Billing](https://prized.dev/docs/billing). More boxes later come from the CLI (`prized box new`) or the [dashboard](https://prized.dev/docs/dashboard#new-box); see [Boxes](https://prized.dev/docs/boxes). ## 2. Install the CLI [#2-install-the-cli] The onboarding tour shows you a command with a single-use link in it, in the shape for the machine you are on. **Copy that one**: it installs the CLI, signs it in, and connects your box, so `prized ssh` works the moment it finishes. The plain commands are the same installer without the sign-in. macOS and Linux: ```bash curl -fsSL https://prized.dev/install.sh | sh ``` Windows, in PowerShell or Windows Terminal: ```powershell irm https://prized.dev/install.ps1 | iex ``` This installs `prized`, its background daemon, and a managed copy of Mutagen, which powers sync; the CLI then [updates itself](https://prized.dev/docs/cli#updates) from signed releases. It works on macOS (Apple Silicon and Intel), Linux (x86\_64 and arm64), and Windows 10 1809 or later (x64 and arm64); a few commands are [not on Windows yet](https://prized.dev/docs/cli#windows), and [`prized mount`](https://prized.dev/docs/mount) is macOS-only. * **macOS and Linux** install into `/usr/local/bin` when that is writable, otherwise `~/.local/bin`, and add that directory to your shell's startup file; `--no-modify-path` (or `PRIZED_NO_MODIFY_PATH=1`) prints the `export` line instead. * **Windows** installs into `%LOCALAPPDATA%\Prized\bin`, adds it to your user `PATH`, and needs no administrator rights; `PRIZED_VERSION`, `PRIZED_CHANNEL`, `PRIZED_INSTALL_DIR`, and `PRIZED_NO_MODIFY_PATH=1` are its knobs. WSL is Linux: use the `curl` line inside it. * **Verification.** The installer never installs a download it cannot vouch for. It checks the release signature with [minisign](https://jedisct1.github.io/minisign/) when that is installed; otherwise it checks the release's checksum file against a digest served with the script itself, which Prized verified against the signature before serving. Pinning a release yourself (`--version`, or `PRIZED_VERSION` on Windows) needs minisign. * **Telemetry.** Once signed in, the CLI reports command usage, never arguments or file contents; [turn it off here](https://prized.dev/docs/cli#usage-telemetry). ## 3. Log in, only if the installer did not [#3-log-in-only-if-the-installer-did-not] If you used the tour's command, the installer already signed you in and connected your box (its output has `✓ signed in as you@…` and `✓ fig connected` lines): **skip to step 4**. ```bash prized login ``` Your browser opens the dashboard to mint a CLI token; paste it back. The CLI stores it in `~/.config/prized` (`%LOCALAPPDATA%\Prized` on Windows), sets up your SSH identity, and connects your box when your account has exactly one; with several, or none yet, `prized setup` connects one. > An install link is single-use and lasts 15 minutes; running that command again reports it as expired, which is expected. Upgrade with `prized update`, not the install link. ## 4. Set up [#4-set-up] ```bash prized setup # asks: auto or manual prized setup auto # everything, no questions ``` One command for the rest, safe to re-run, run on your machine in the folder you want on the box. Setup checks the box connection and repairs what its health check finds (a stopped daemon, a missing SSH `Include`, a box agent behind your CLI), stopping only on what it could not fix. Then one question: **auto** brings everything below without asking again; **manual** asks a yes or no card per step. The report ends with the `prized ssh` command that connects. The optional parts: * **Project.** `prized setup myproject` creates `~/code/myproject` on the box, mirrored to `~/Prized/myproject` on your machine. Pass a path (`prized setup ~/src/myproject`) and one yes or no asks whether to sync it in place; `--copy-once`, `--copy`, and `--clone` answer from the command line. See [Sync](https://prized.dev/docs/sync#add-a-project). * **Environment.** Setup scans your machine read-only and asks a few yes or no questions: your shell, the same Python and Node, the CLIs on your PATH, your Docker images, the Postgres you run locally. Installs and secrets default to yes; data defaults to no. Skipped on Windows for now. See [Bring your environment](https://prized.dev/docs/environment). * **Agents.** The coding agents found on your machine (Claude Code, Codex, Cursor; `--agent chatgpt` for the ChatGPT app) sign in on the box after one card, over the SSH tunnel; gh and your git identity go along on their own. See [Credentials](https://prized.dev/docs/credentials). * **Session.** With one runnable agent picked, setup starts it in a detached tmux session and prints the command that reattaches to it — your terminal stays where it is. `--attach` opens the session here instead; `--no-start` skips it. Then connect any time: ```bash prized ssh # a persistent tmux session on the box prized status # box vitals, sync state, mirrored ports, daemon health ``` ## Or let an agent do it [#or-let-an-agent-do-it] Steps 2 through 4 are one paste into your coding agent: the prompt is on [Agent setup](https://prized.dev/docs/agent-setup). --- # Agent setup > Paste one prompt into Claude Code or Codex on your machine; the agent installs the CLI, runs prized setup, and asks before putting your project or your credentials on the box. URL: https://prized.dev/docs/agent-setup ## One prompt [#one-prompt] Open your agent on your machine, in the project you want on the box, and paste the prompt below. It covers steps 2 through 4 of the [Quickstart](https://prized.dev/docs/quickstart); create the box in the [dashboard](https://prized.dev/dashboard) first. The agent asks you about the project and each credential before touching them, and relays your choices as flags. ```text title="Prompt" Set up Prized on this machine and, if I confirm each part, put this project and my coding agents on my box. Prized is a cloud dev box that syncs folders with this machine; the docs live at https://prized.dev/docs (plain-text mirror at /llms-full.txt). Rules: copy, never move or delete; never print credentials or read ~/.config/prized; ask me before installing software, before transferring any credential, and before doing anything that could cost money. Non-interactive prized commands accept --json and print exactly one JSON object with an "ok" field; error codes and exit codes are stable and listed at https://prized.dev/docs/cli. 1. Install: if `prized version` fails, ask me, then run this and re-check (on Windows, in PowerShell: irm https://prized.dev/install.ps1 | iex): curl -fsSL https://prized.dev/install.sh | sh 2. Sign in: if prized commands fail with code AUTH or a "prized login" hint, stop and ask me to run `prized login` in another terminal (it needs my browser). I can instead paste a CLI token from the dashboard for you to use as `prized login --token `. 3. Everything else is one command, safe to re-run: prized setup --json --non-interactive It never prompts; each consent-gated step runs only when its grant flag is present and is reported as skipped otherwise. Ask me before adding each flag, and only pass the ones I say yes to: - `--project `: sync this folder with the box in place; the folder itself becomes the live side and the box wins conflicts. Ask me first. Two alternatives are their own distinct grants, so relay my choice exactly: `--copy` syncs a copy under ~/Prized and leaves the folder alone; `--clone` clones the repo onto the box over git with no live mirror, the right choice for a repo I actively work in elsewhere. Say which one I picked in your report; if I say no to all of them, run setup without --project. - `--agent claude|codex|cursor|chatgpt` (repeatable): hand that agent's sign-in and MCP config to the box. Ask me per agent. - `--install-cursor`: run Cursor's installer on the box. Its own yes. - `--gh`: copy my GitHub CLI token and git identity (an interactive setup does this on its own; your non-interactive run needs the flag). Its own yes. - `--env `: bring parts of my local environment to the box without asking: shell, python, node, tools, clis, docker, services (comma-separated). Run `prized env plan --json` first and read me the groups and items it lists; pass only the groups I say yes to. - `--env-secrets`: copy my secrets: the project's `.env` files, secret-looking env vars, and CLI sign-in files (aws, gcloud, kubectl, ...). Its own yes. - `--env-data`: copy database contents, Docker volumes, and local-only images; the plan shows sizes. Its own yes. If setup fails with exit 5 naming multiple candidate boxes, ask me which one and re-run with `--box `; if it finds no box on the account, ask me to create one in the dashboard. Read steps[] in the JSON: "skipped" entries name what was declined or not granted, "failed" entries carry the hint. 4. Claude Code sign-in copies from this machine when I am signed in here, so `--agent claude` works from your run. If the claude row comes back manual with a note saying no cached token, this machine is not signed in: ask me to run `prized agents handoff claude` myself in another terminal (a one-time browser step); the sign-in goes from this machine to the box directly and you never see it. Then re-run. 5. Verify: `prized status --json` reports ok; if we set up the project as a sync (not `--clone`; a clone has no sync session), also that `prized sync flush ` completes. Check `prized agents ls --json` shows each chosen agent installed and signed in on the box. 6. Report: the box name, the project path on both sides (or the clone path on the box, or that I chose to keep this repo local), which project mode I granted, each agent's handoff outcome, and the reattach command for any session setup started. ``` ## What the agent asks you [#what-the-agent-asks-you] * **Sign-in.** `prized login` needs your browser, so the agent stops; run it yourself, or mint a CLI token in the dashboard for `prized login --token`. * **The project.** In place, `--copy` (a synced copy under `~/Prized`), or `--clone` (a git clone, no mirror); say no to all three and you still get a signed-in CLI and a healthy box. The modes are compared in [Sync](https://prized.dev/docs/sync#add-a-project). * **Your environment.** The groups from `prized env plan --json`, then `--env` with the ones you accept; secrets and data are their own yes. See [Bring your environment](https://prized.dev/docs/environment). * **Each credential.** One flag per yes (`--agent `, `--gh`, `--install-cursor`); the transfer runs over the SSH tunnel and the agent never handles a value. See [Credentials](https://prized.dev/docs/credentials). > Agents you picked before are saved as `[agents].preferred`, so a later run hands them off without `--agent`; `--no-agents` runs on nothing but its own flags. Every command in the prompt is safe to re-run. For running agents on the box afterwards, see [Agents](https://prized.dev/docs/agents). --- # Bring your environment > prized setup looks at your machine, asks a few yes or no questions, and reproduces the parts you pick on the box - shell, toolchains, CLIs, Docker images, databases, and env vars. URL: https://prized.dev/docs/environment ## What it does [#what-it-does] ```bash prized setup # ✓ fig ready # ✓ project ~/src/app ⇄ fig:~/code/app # bring Bring your dev environment to the box? # shell aliases + exports · zsh as the login shell · ~/.tmux.conf # python Python 3.12 · ruff · pre-commit · project deps (uv) # node Node 22.4.0 · pnpm · typescript # docker postgres:16 · redis:7 · compose up: app # services postgres 16 on :5432 · redis on :6379 # [Y/n] n = choose group by group ``` The environment step runs inside `prized setup`, between the project and agents steps: it scans your machine read-only, asks a few yes or no questions, and applies what you accept. Anything that did not carry is named with its reason (`✓ env 41 moved · 2 not carried (~/.vimrc: contains a key-shaped value)`). Re-running is safe: every item checks the box first and reports `already on the box`. `prized env scan` shows the scan, `prized env plan` the questions without applying anything, and `prized env migrate` runs the step outside setup; `prized setup --dry-run` prints the whole run and changes nothing. ## The questions [#the-questions] The install groups are one question; `y` or Enter takes them all, `n` asks group by group. Secrets are one question listing exactly what would move; data copies are their own question and default to no. | Group | Default | What it brings | | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | shell | yes | Aliases, functions, and non-secret exports into `~/.config/prized/shell.sh` on the box; zsh with oh-my-zsh, powerlevel10k, or starship when you use them; `.tmux.conf`, `.vimrc`, and nvim config. | | python | yes | The same Python versions via `uv` (or just the one the project pins), your pipx and `uv tool` packages, and the project's dependencies from its lockfile. | | node | yes | The same Node versions via `fnm` (or just the one the project pins), your global npm packages, and pnpm or yarn through corepack. | | tools | yes | Go and Rust at the same versions, `cargo install` binaries, Homebrew formulae as apt packages or Linux bottles (no bottle means `not carried`), and executables in `~/bin`, `~/.local/bin`, and `~/go/bin` reinstalled from their origin. | | clis | yes | Developer CLIs on your PATH: aws, gcloud, az, kubectl, helm, terraform, flyctl, vercel, wrangler, supabase, stripe, ngrok, and more. One whose sign-in lives in the macOS Keychain is `not carried` with the login command to run on the box. | | docker | yes | Registry images pulled on the box, project Dockerfiles rebuilt there, and a running compose project brought up after the secrets step so its `env_file` is in place. Another compose project's images and volumes are `not carried`; set it up from its own folder. | | services | yes | Local Postgres, MySQL, Redis, MongoDB, and similar as containers at the same major version and port, data in a named volume, client installed alongside. A port already taken on the box fails the item with the reason. | | data | no | Database contents, named Docker volumes, and local-only images streamed over the tunnel. Sizes are shown before you answer. Images and volumes are read from the engine the scan inspected, whatever `DOCKER_*` says by the time the copy runs, and the copy runs `docker`, `pg_dump`, and `gzip` from the same standard locations the scan found them in, never from `PATH`. | | secrets | yes | The project's `.env` files (below), secret-looking env vars from your shell files, and CLI sign-in files (`~/.aws`, `~/.config/gcloud`, `~/.kube/config`, ...), all 0600; values are never shown. | The box is amd64: an arm64-only image is rebuilt or re-pulled, never streamed. Things with no recipe yet (conda, Ruby and Java toolchains, casks, devcontainers) are listed as `not carried` so you know what to set up by hand. ### `.env` files [#env-files] A sync session `prized setup` creates ignores `.env` and `.env.*` at every depth (`.env.example` and `.env.sample` still sync), so a credential file never reaches the box as a side effect of syncing. Instead the secrets question lists each one it found, a monorepo's `apps/web/.env.local` included, and with the default yes copies it to the same relative path on the box, 0600. A symlink to another `.env` inside the project is recreated as a symlink; a hard link, a symlink that leaves the project, or a file you cannot read is refused and named as not carried. Under `--non-interactive`, `-y` or `--env-secrets` grants the copy. > A project synced before this rule, or added by hand with `prized sync add`, still carries `.env` files in the mirror; add `.env` to that project's `extra_ignores` to gate it. On a [restricted box](https://prized.dev/docs/environments#safe-for-third-parties), `prized setup` adds the ignores to every sync project itself. Once a session ignores `.env` files, `--unignore` brings back only `.env.example` and `.env.sample`. ## Flags [#flags] | Flag | Effect | | ------------------------- | ------------------------------------------------------------------------------------------------------------------- | | `--env shell,python,...` | Bring these install groups without asking (the grant for `--non-interactive`; `-y` covers them too). | | `--env-secrets` | Copy your secrets without asking: `.env` files, env-var values, and CLI sign-ins (`-y` covers this too). | | `--env-data` | Copy database contents, volumes, and local-only images. Never covered by `-y`. | | `--no-env` | Skip the step. | | `--no-advisor` | Never contact the advisor; questions come from the built-in rules. | | `--env-workers N` | How many items install at once (default: up to 8); `1` installs them one at a time, in order. | | `--attach`, `--no-attach` | Open the box session in this terminal when setup finishes. The default (`--no-attach`) prints the reattach command. | ## The advisor [#the-advisor] The scan is deterministic. Before asking, the inventory and your project's manifests (README, Makefile, package.json, compose file, version pins) go to Prized's advisor, a bounded Claude pass that rewrites the questions around what the project actually needs. It sees names, versions, and sizes only: secret values are redacted before anything leaves your machine, credential files are never read, and paths are relative (`.` for the project, `~` for your home). It cannot run anything, picks only from the recipe catalog the rules use, and cannot add a secrets or data item the scan did not find. If it is unreachable the questions come from the rules; `--no-advisor` or `PRIZED_ADVISOR=off` skips it. ## What never moves [#what-never-moves] * **Private keys and vaults.** SSH private keys, GPG keys, the macOS Keychain, browser profiles, 1Password data; the report tells you when commit signing needs a box key. * **Secret values into any output.** The JSON envelope, the terminal, telemetry, and the advisor carry names only. * **An export that is not one literal assignment on its own line.** A computed value (`$(...)`, backticks), a second command, pipe, redirect, or open quote on the line, a `PATH` edit, or a secret-looking name sharing a line stays home and is listed as `not carried`; put the value on a line of its own to carry it. * **Anything through the control plane.** Every transfer goes over your SSH tunnel straight to the box. See [Credentials](https://prized.dev/docs/credentials). * **Data unless you say so.** Database contents, volumes, and local-only images move only on an explicit yes or `--env-data`. ## On the box afterwards [#on-the-box-afterwards] | Path | What it is | | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `~/.config/prized/shell.sh` | Your aliases, functions, exports, and the PATH hooks for the installed toolchains; sourced from `~/.profile`, `~/.bashrc`, and `~/.zshrc`. | | `~/.config/prized/env.secret` | The env-var values you chose to move, 0600, sourced by `shell.sh`. | | `~/.config/prized/welcome.txt` | The welcome block setup printed: what moved, what was left. | | `prized-postgres`, `prized-redis`, ... | Service containers, `--restart unless-stopped`, data in `prized--data` volumes. | Services listen on `127.0.0.1` on the box; `prized ports` mirrors them to your machine like any other box port. --- # Sync > A synced project is one folder pair, ~/code/ on the box mirrored into a folder on your machine. Edits land on the other side in under a second, both directions, .git included. URL: https://prized.dev/docs/sync ## The model [#the-model] | Side | Path | | ------------ | ---------------------------- | | box | `~/code/` | | your machine | `~/Prized/` by default | `prized sync add ` wires the pair and keeps it mirrored both ways. `--local` syncs a folder of your own in place, and `local_root` under `[sync]` in the config moves the default parent for everything. Sync keeps a real local copy, which is what makes tools that scan a whole repo fast. To browse the box without copying anything, use [mount](https://prized.dev/docs/mount). ## How fast [#how-fast] Edits in a folder that changed recently land on the other side in under a second. The first edit in a folder nothing has touched for a while can take up to 10 seconds to leave the box; `watch_polling_interval` under a project's `[[sync.project]]` entry lowers that, at the cost of a full scan per interval. ## Add a project [#add-a-project] Pick the case that matches where the code is today. **Nothing exists yet.** `--create` makes `~/code/myproject` on the box, and the empty pair fills up as you or an agent work: ```bash prized sync add myproject --create ``` **The code is on the box.** The first sync copies `~/code/myproject` down into `~/Prized/myproject` (`C:\Users\you\Prized\myproject` on Windows). Pass a path for a folder outside `~/code` (`prized sync add ~/work/api` derives the name `api`); a name that cannot be a project name is folded into one, and `--name` picks one yourself: ```bash prized sync add myproject ``` **The code is on your machine.** Point setup at the folder. A manual run asks one yes or no: sync it in place (the default) or copy it to the box once with no sync; two more modes are flags: ```bash prized setup ~/src/myproject # asks: sync in place? (n = copy once) prized setup ~/src/myproject --copy-once # copy to ~/code/myproject on the box, no sync prized setup ~/src/myproject --copy # sync a copy under ~/Prized prized setup ~/src/myproject --clone # git clone on the box, no mirror ``` | Mode | What happens | Right for | | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | **In place** | Your folder becomes the live local side; setup asks first, because the box now wins conflicts there. | A project one person edits. | | `--copy-once` | One upload to `~/code/myproject`, `.git` included, ignored paths and `.env` files left out; no sync session, and a populated destination is never overwritten. | A checkout others or several agents edit, or a one-time import. | | `--copy` | Copies the folder into `~/Prized/myproject` (never a move) and syncs the copy; the original is out of the loop. A hard link inside the folder is refused. | Leaving the original untouched; retire it once the copy is in use. | | `--clone` | Clones the folder's `origin` to `~/code/myproject` on the box at your current branch; no sync session, work moves by push and pull. | A checkout others edit, worktrees, uncommitted work in flight. | In place by hand is `prized sync add --local ~/src/myproject --create`. A clone is not a sync project, so it never appears in `prized sync ls`. Rules for every add: * **Both sides already have content:** `add` refuses until you pass `--merge` (`prized setup --merge` passes it through), which accepts the conflict rule below; a copy of the local folder is kept under `~/Prized/.prized/backups/` first. * **No nesting:** a folder cannot be added inside, around, or on top of another synced folder, on either side of the pair. * **Ignored by default:** `node_modules`, `.next`, `dist`, `build`, `out`, `target`, `.venv`, `venv`, `__pycache__`, `.turbo`, `coverage`, `*.log`, `.DS_Store`, `._*`, `.tox`, `.nox`, `.mypy_cache`, `.pytest_cache`, `.ruff_cache`, `.ipynb_checkpoints`, `.pnpm-store`, `.parcel-cache`, `.nuxt`, `.svelte-kit`, `.cache`, `.gradle`, `.terraform`. Adjust per project with `--ignore` and `--unignore`. * **`.env` files:** a session `prized setup` creates ignores `.env` and `.env.*` (`.env.example` still syncs); credential files move through setup's secrets question. `prized sync add` by hand syncs them unless you pass `--ignore .env`. See [Bring your environment](https://prized.dev/docs/environment#env-files). * **Files larger than 256 MB are skipped;** `sync add` and `prized sync ls` name every skipped path. If the first sync cannot be set up, `sync add` fails with the reason and keeps the project configured; the daemon retries on its own, `prized sync ls` shows it as `error`, and `prized sync rm myproject` drops it. A first sync can take a couple of minutes, since the whole folder travels, `.git` included; watch the transfer rate on setup's progress line before assuming something is stuck, trim what travels with `--ignore`, or use `--clone` so the box fetches the history from `origin`. ## Conflicts: the box wins [#conflicts-the-box-wins] Sync runs two-way-resolved and the box wins every conflict: agents on the box write at high rate, so a local edit that races one yields. This matters most for a folder synced in place, since the folder you have always edited is now the yielding side. `--prefer mac` flips that per project; `--mode two-way-safe` surfaces conflicts instead of resolving them. ## Git over sync [#git-over-sync] Commit, rebase, and push from a shell on the box. Reading from your machine (history, diffs, blame in `~/Prized`) is always safe; committing from your machine works only while the box side is quiet, since two commits landing at once can lose the local one. ## Offline [#offline] Agents, builds, and servers keep running while your laptop is offline; sync and mirrored ports catch up on reconnect. To edit offline in a project an agent is also writing to, pause the project first, or the box's versions win on reconnect. ## Everyday commands [#everyday-commands] | Command | What it does | | ------------------------------ | -------------------------------------------------------------------------- | | `prized sync ls` | List projects with their state and conflicts. | | `prized sync flush myproject` | Force a full sync cycle and wait for it. Useful before switching machines. | | `prized sync pause myproject` | Stop syncing until you resume. `--all` works too. | | `prized sync resume myproject` | Resume a paused project. | | `prized sync repair myproject` | Recover a halted session with a safe re-scan. No deletions propagate. | > A session that halted after a folder was deleted at its root refuses to resume, since that would propagate the mass delete. Use `repair`. ## Remove a project [#remove-a-project] ```bash prized sync rm myproject ``` This stops the sync and leaves files on both sides. Two flags go further; both are moves, never `rm -rf`, and both name the exact path in the confirm: * **`--delete-local`** moves the local folder to the Trash after you type `delete-local`; for a project synced in place that is your own checkout. * **`--delete-box`** moves the box folder aside after you type `delete-box`; it is refused when anything else still syncs that folder or one inside it. > Two box paths that spell the same folder differently count as the same folder, `~/code/app` and `/home/ubuntu/code/app` included. ## Several boxes [#several-boxes] Sync commands follow the box you aim them at: the default box, or `--box mango` (or `PRIZED_BOX=mango`) for a box connected with `prized setup --box mango`. Two boxes cannot sync a project under the same name; the second `prized sync add` asks for `--name`. See [Boxes](https://prized.dev/docs/boxes#several-boxes). ## Under the hood [#under-the-hood] Sync runs on Mutagen. Prized installs and manages its own pinned copy and ignores any on your PATH; `prized doctor` fetches it if it is missing. --- # Mount > See the whole box in Finder as a local volume, served live over the tunnel. Nothing is copied; the mount uses no disk space on your Mac. URL: https://prized.dev/docs/mount > Mount is experimental and macOS-only: it is built on the stock macOS NFS client, so on Linux and Windows the command refuses and points at [sync](https://prized.dev/docs/sync) and `prized ssh` instead. A dropped connection unmounts rather than reconnecting. ## Usage [#usage] ```bash prized mount # the configured box prized mount mango # any box on your account prized mount --dir /var/log --read-only # one directory, read-only prized mount --detach # in the background; the command returns prized unmount # stop the mount and detach the volume ``` `prized mount` wakes the box if it sleeps, mounts its home directory at `~/Prized/boxes/`, and opens it in Finder, where it also appears under Locations as `.localhost`. The command stays in the foreground; Ctrl-C, closing the terminal, or ejecting in Finder unmounts. | Flag | What it does | | ---------------- | ------------------------------------------------------------ | | `--detach`, `-d` | Serve the volume from a background process and return. | | `--dir` | Export this box directory instead of the home directory. | | `--at` | Mount somewhere other than `~/Prized/boxes/`. | | `--read-only` | Serve a read-only view. | | `--no-open` | Skip opening the mounted folder in Finder. | | `--mount-opt` | Extra NFS mount options, comma-separated, appended verbatim. | ## In the background [#in-the-background] ```bash prized mount --detach # mounted prized-1 at ~/Prized/boxes/prized-1 in the background (pid 4242) prized unmount # unmounted ~/Prized/boxes/prized-1 ``` `prized mount --detach` hands the volume to a background process and returns once the folder is mounted; the mount outlives the terminal. `prized unmount [BOX]` stops that process and detaches the folder, and works on a foreground mount in another terminal too. One mount per box: a second `prized mount` reports where it is mounted and the pid serving it. A background mount logs to `~/Library/Logs/prized/mount-.log`; `--json` works with `--detach` and `prized unmount`. ## Mount or sync? [#mount-or-sync] | You want | Use | | --------------------------------------------------- | ----- | | a working copy on your Mac, fast for builds and git | sync | | to browse, preview, or grab anything on the box | mount | A [synced project](https://prized.dev/docs/sync) is a mirrored folder pair: local disk, one project, fast. A mount is a live window over the whole box with a network round trip per uncached operation: browsing, Quick Look, and editing a file feel fine; builds or `git status` on a large repo do not. ## How it works [#how-it-works] The stock macOS NFS client points at a server the CLI runs on 127.0.0.1, whose backend is an SFTP session to the box over the same tunnel `prized ssh` uses. Nothing kernel-side is installed, no sudo is needed, and your Mac holds file bytes only in memory. > That local server does not authenticate its clients, so on a Mac you share with other user accounts treat a mount as readable by them: export a narrower `--dir`, add `--read-only`, and `prized unmount` when you are done. If the box suspends or the connection drops, the volume is unmounted so Finder never hangs; run `prized mount` again to remount. ## Troubleshooting [#troubleshooting] * **"already mounted at ... (pid N)"**: the box is being served elsewhere; `prized unmount ` stops it. * **"mountpoint is not empty"**: a mount left by a previous run; `umount ~/Prized/boxes/` and try again. * **"Operation not permitted" listing the folder in a terminal**: grant your terminal Full Disk Access (System Settings > Privacy and Security), or use Finder. * **"secure sftp helper failed (update prized-agent on the box)"**: the box's agent predates the mount helper; update it and try again. --- # Ports > Start a server on the box and open it on your laptop as localhost, with no flags or config. Mirroring is on by default and takes a few seconds. URL: https://prized.dev/docs/ports ## How it works [#how-it-works] The agent on the box watches for listening TCP ports. When one appears, the daemon on your machine opens the same port on 127.0.0.1 and tunnels it to the box. Servers that bind only to the box's loopback (Vite, most dev servers) are relayed by the agent, and websockets work. On Windows the daemon runs while you are signed in. ```bash title="you@box ~/code/myproject" npm run dev # ready on http://localhost:3000 # two seconds later, localhost:3000 works on your machine ``` ## Inspect and toggle [#inspect-and-toggle] | Command | What it does | | ---------------------------- | -------------------------------------------------------------------- | | `prized ports ls` | Every box listener with its process and mirror state. | | `prized ports toggle 3000` | Flip a port off or back on. The preference persists across restarts. | | `prized proxy box-3fa9 5432` | Forward one port from any box to `127.0.0.1:5432` until Ctrl-C. | An interactive `prized ssh` session to a non-default box forwards that box's ports for the life of the session. ## Limits [#limits] * **TCP only.** UDP is not forwarded. * **Ports 3000 through 9999** are auto-mirrored. The range and per-port allow and never lists live in the config's `[ports]` section. * **Never mirrored:** ports below 1024, port 22, and the agent's port 7377; 5900 is off by default. Anything else outside the range needs an allow-list entry or a manual `prized proxy`. * **A mirrored port is open to your whole machine,** like `ssh -L` or `docker -p 127.0.0.1:`; on a machine you share with other accounts, treat it as if the service were running there unprotected. * **Local wins.** If something on your machine already listens on a port, the mirror waits, `prized proxy` refuses it, and an `ssh` session skips it; nothing binds underneath your own server. * **Every request crosses the network.** An app that makes hundreds of database round trips per page should run next to its database on the box, with only the app port mirrored. ## A second box [#a-second-box] Each box's daemon mirrors that box's ports: a box connected with `prized setup --box mango` gets `prized --box mango ports ls` and `prized --box mango ports toggle 3000`, automatic mirroring included ([Several boxes](https://prized.dev/docs/boxes#several-boxes)). Two boxes listening on the same port collide on your machine, so the second mirror shows as `conflicted` in `ports ls`; toggle the port off on one box, or add it to `[ports].never` in that box's config. --- # Sessions > Your shell runs on the box. A network drop or a closed lid ends the connection but not the session; reconnect and you are where you left off, running processes included. URL: https://prized.dev/docs/sessions ## Connect [#connect] ```bash prized ssh ``` This lands in a persistent tmux session named `main`; run it again from anywhere and you attach to the same one. A suspended box wakes on connect. On Windows it runs the OpenSSH client that ships with Windows; mosh and dragging files in are [not on Windows yet](https://prized.dev/docs/cli#windows). | Command | What it does | | -------------------------- | ------------------------------------------------------------------------------------------- | | `prized ssh -s agents` | Attach to, or create, a named session. Letters, digits, hyphens, and underscores; no dots. | | `prized ssh --ls` | List sessions. | | `prized ssh --kill agents` | Close a session. | | `prized ssh --raw` | A plain shell without tmux. | | `prized ssh -- make test` | Run one command and get its exit code back. | | `prized agents run codex` | Start a coding agent in a detached session, no laptop attached. See [Agents](https://prized.dev/docs/agents). | > Sessions survive disconnects and closed lids. What they survive on a pause depends on the box size, and a busy session holds off [auto-pause](https://prized.dev/docs/boxes#auto-pause); see [Pause and resume](https://prized.dev/docs/boxes#pause-and-resume). ## Mosh for flaky networks [#mosh-for-flaky-networks] ```bash brew install mosh # macOS; on Linux: sudo apt install mosh prized mosh ``` The same tmux session over UDP: keystrokes echo instantly, and the connection survives IP changes and laptop sleep. The box needs no public port; the traffic uses the SSH tunnel. ## Drag files in [#drag-files-in] Drag a file from Finder (or your file manager) onto a `prized ssh` or `prized mosh` window and it lands on the box: the CLI uploads it over the tunnel into a fresh folder under `~/.prized/drops/` and pastes the box path instead of the local one. Drop a screenshot into Claude Code's prompt this way and the agent can open it. * **Size:** one file or several per drop, up to 512 MB; drops older than 7 days are cleaned out. * **Only a paste that is nothing but local file paths is rewritten.** If an upload fails, the original local path is pasted and the tmux status line says why. * **Off** for one session with `--no-drop-upload`, or for good with `drop_upload = false` under `[ssh]` in the config. ## Editors and other tools [#editors-and-other-tools] * **Your local editor on the synced folder.** Saves to `~/Prized/myproject` land on the box in under a second. * **Anything that speaks SSH.** `prized setup` installs an SSH config stanza, so `ssh box-3fa9`, Cursor and VS Code Remote-SSH, scp, rsync, and git remotes all reach the box by hostname. The ChatGPT desktop app rides the same stanza ([Codex remote](https://prized.dev/docs/codex)); Claude Desktop rides it with a restricted device key Prized installs for you ([Claude Desktop remote](https://prized.dev/docs/claude-desktop)). --- # Run commands and files > Run a command on your box and get its exit code back, leave one running and check on it later, and copy files in either direction, from the CLI or from any script that holds a CLI token. Nothing to install on the box. URL: https://prized.dev/docs/exec ## Run a command [#run-a-command] ```bash prized exec -- make test ``` The command runs on the box under `sh -c` in your home directory; stdout, stderr, and the exit code come straight back, and your local stdin is connected. A suspended box wakes first. A box name before the `--` picks a box other than the configured one. | Command | What it does | | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `prized exec -- npm test` | Run in your home on the default box; the exit code is `npm test`'s. | | `prized exec box-a7f3 --cwd app -- npm test` | Run on a named box, in `~/app` (`--cwd` is relative to your home, or absolute). | | `prized exec --timeout 10m -- ./build.sh` | Kill the command after 10 minutes (TERM, then KILL 5 s later): exit 124, and stderr ends with a `prized: command timed out after 600s (SIGTERM)` line. No limit by default. | | `prized exec --env CI=1 --env NODE_ENV=test -- npm test` | Add environment variables (repeatable, up to 32). | | `prized exec --json -- make` | Capture instead of stream: one object with `exitCode`, `signal`, `timedOut`, `stdout`, `stderr`. | * The words after `--` are joined and handed to `sh -c`, so quote as for ssh: `prized exec -- 'cd app && npm test'` runs both halves on the box. The shell is a plain `sh`, not your login shell; tools that only your `.bashrc` puts on PATH need `prized exec -- bash -lc 'npm test'`. * Here `--timeout` is the command's time limit, not the network timeout the other commands use the flag for. * Exit code: the remote command's, or ssh's 255 when the box was unreachable. With `--json`, `ok` says whether the command ran and `exitCode` what it returned; `timedOut` is true only when the time limit ended the command, so a command that exits 124 on its own is `exitCode: 124, timedOut: false`. ## Detached processes [#detached-processes] ```bash prized exec --detach -- ./train.sh # started p_3f9c0a1b2d4e (pid 41213) in /home/ubuntu ``` `--detach` starts the command and returns at once with a process id. The process outlives your terminal and the ssh session; its output goes to log files on the box. | Command | What it does | | ------------------------------------- | --------------------------------------------------------------------------------------------- | | `prized exec --detach -- CMD` | Start `CMD` detached (`--cwd` and `--env` apply) and print its id. | | `prized exec --status p_3f9c0a1b2d4e` | Running or exited, the exit code or signal, start and finish times, and the tail of each log. | | `prized exec --status p_… --tail 1m` | How much of each log to show: bytes, or `4k`, `1m` (default `16k`, cap `1m`). | | `prized exec --kill p_3f9c0a1b2d4e` | Stop it: TERM to its process group, then KILL if it is still there after 5 s. | | `prized exec --ps` | Every detached process on the box, running or finished. | `--status`, `--kill` and `--ps` never wake a suspended box (they exit 5 and say so); running a command does. Everything about a detached process lives in one directory on the box, so `ls`, `tail -f`, and your own scripts see what the CLI sees: | File in `~/.prized/processes//` | Contents | | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `cmd`, `cwd` | The command line as given, and the absolute directory it ran in. | | `pid` | The process id (also its process group id). | | `boot_id` | The box's boot id at start. After a reboot (a paused box resumes from one) the pid is not trusted: the process reads as `lost` and is never signalled. | | `started_at` | Start time, RFC 3339 UTC. | | `stdout.log`, `stderr.log` | The output, appended as it happens. | | `exit` | Written when the command ends: the exit code, or 128 + signal. | > Log directories are never deleted for you. `rm -r ~/.prized/processes/p_…` on the box when you are done with one. ## Copy files [#copy-files] ```bash prized cp ./notes.md :notes.md ``` `prized cp` works like scp: exactly one side is on the box, written `BOX:PATH`, and a bare `:PATH` means the configured box. Paths on the box are relative to your home there unless absolute. Files are written whole (a temp file, then a rename), so an interrupted copy never leaves a half-written target; modes are preserved, and symlinks are skipped and counted, a symlink named as `SRC` included: name the file it points at instead. A hard link (a second name for a file that may live outside the tree, which pnpm and Nix trees are made of) is skipped and counted the same way, and refused when named as `SRC`; copy it to a new name to send it. A download writes only under `DST`: an entry that would land outside it stops the copy, and so does a target directory that is itself a symlink; name the directory it points at. | Command | What it does | | ------------------------------------ | --------------------------------------------------------- | | `prized cp ./file.txt :` | Copy a file into your home on the box, keeping its name. | | `prized cp -r ./site box-a7f3:www` | Copy a directory tree (`-r` is required for directories). | | `prized cp box-a7f3:logs/app.log ./` | Copy a file from the box into the current directory. | | `prized cp -r :proj/dist ./dist` | Copy a directory tree from the box. | For a live view of the whole box in Finder rather than copies, see [Mount](https://prized.dev/docs/mount). ## The edge API [#the-edge-api] Everything above is also HTTP, for scripts and agents that hold a CLI token and have no `prized` installed. The token comes from **Dashboard → Workspace → CLI tokens**; the base URL is `edge.url` in `GET /api/v1/me`, and every route sits under `$EDGE/v1/box/{box}/` (the box's name or id) with the token as the bearer. ```bash export PRIZED_TOKEN=dcp_… EDGE=$(curl -s -H "Authorization: Bearer $PRIZED_TOKEN" https://api.prized.dev/api/v1/me | jq -r .edge.url) ``` Paths are read the way `prized cp` reads them, and every answer echoes the real path it touched. Only `POST /exec` wakes a suspended box; the other routes answer `box_not_running`. The routes for commands, detached processes, and files are on [API reference: The edge](https://prized.dev/docs/api#the-edge); the box's coding agent has its own, described on [Prompt an agent remotely](https://prized.dev/docs/remote-agents#the-edge-api). ## Errors [#errors] A failed request answers `{"ok": false, "error": {"code", "message"}}`. Every code and what it means is on [API reference: Errors](https://prized.dev/docs/api#errors). ## Limits [#limits] Every cap for runs, files, and the edge API, the request rate included, is on [Limits](https://prized.dev/docs/limits#commands-and-files). --- # Desktop > Every box can show a full Linux desktop, or a single Chrome window, in your browser. It streams over VNC inside the private tunnel; nothing is opened to the internet. URL: https://prized.dev/docs/desktop ## Open [#open] On the box page, open the **Desktop** tab. Pick **Desktop** for a 1920x1080 XFCE desktop or **Browser** for one Chrome window, then click open. From the terminal: ```bash prized desktop prized browser --url https://example.com ``` A desktop is up in a few seconds; a box created before September 2, 2026 installs the packages on its first open (about two minutes, once). | Command | What it does | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `prized desktop [BOX]` | Start the desktop, forward its VNC port to `127.0.0.1`, and open the dashboard tab. Runs until Ctrl-C; wakes a suspended box. | | `prized browser [BOX]` | The same with one Chrome window. `--profile NAME` picks the [profile](#browser-profiles), `--url URL` the start page. | | `--no-open` | Print the `vnc://127.0.0.1:PORT` address and the password for a native VNC client instead of opening the dashboard. | | `--stop` | Stop the desktop (or browser) on the box and exit. Never wakes a paused box. | | `--json` | Print `{"ok":true,"box":"…","mode":"desktop","localPort":…,"password":"…","dashboardUrl":"…"}` once the forward listens, open nothing, keep forwarding. | ## What you get [#what-you-get] * **Desktop:** XFCE with a terminal, a file manager and Chrome, on a fixed 1920x1080 screen scaled to your window. `xdotool` and `wmctrl` are installed for scripts. * **Browser:** one maximized Chrome window and nothing else. Sign in to a site once and tools on the box can reuse the profile. * **Keyboard and mouse** work as in a native window. The tab's header has full screen and two clipboard buttons, paste to the box and copy from it. * **The desktop keeps running when you close the tab.** Open it again and you land on the same windows. **Stop desktop** in the tab, or `prized desktop --stop`, ends it. ## Browser profiles [#browser-profiles] A profile is Chrome's user data directory at `~/.prized/browser/` on the box (`default` when you name none): sign-ins, cookies and extensions persist across restarts and ride a [snapshot](https://prized.dev/docs/boxes#snapshots). Names use letters, digits, `-` and `_`, up to 64 characters. * Starting the browser with a different profile replaces the running window. * Starting it again with the same profile and a URL opens that page as a new tab. ## Pause and resume [#pause-and-resume] The desktop is processes, not disk: Nano through Small boxes hibernate and bring it back as it was; Medium and larger stop clean, so the tab starts a fresh desktop on resume. Files, packages and profiles survive either way ([Pause and resume](https://prized.dev/docs/boxes#pause-and-resume)). > A running desktop may count as activity for [auto-pause](https://prized.dev/docs/boxes#auto-pause) even with nobody connected. If you rely on auto-pause, stop the desktop when you are done. ## Security [#security] * **Loopback only.** The VNC server listens on the box's loopback; nothing new is reachable from the internet. * **A random password per start**, kept only on the box in a file only your user can read. The dashboard uses it for you; `--no-open` and `--json` print it, the default output shows only the forwarded `vnc://` address. * **The VNC port** (5910 for the desktop, 5911 for the browser) appears in `prized ports` like any listener, but needs no mirroring: the desktop command forwards it itself. ## Limits [#limits] * VNC, not video: a responsive desktop for editors, terminals and browsing, not full-motion video. No audio. * Fixed 1920x1080, scaled to your window. No GPU on any box size. * No GUI automation agent. For scripts, `DISPLAY=:10 xdotool …` targets the desktop, `DISPLAY=:11` the browser window. * One desktop and one browser window per box. ## Troubleshooting [#troubleshooting] | Symptom | What to do | | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | `desktop install failed` | On the box, `cat ~/.prized/desktop/install.log` names the cause (network, or a held apt lock); open the tab again. | | A black screen after connecting | The session is still starting; if it stays black, **Stop desktop** and open again. | | `the box refused the VNC password` | A stale password after a restart; open the tab again. | | No Chrome window in browser mode | `cat ~/.prized/desktop/browser-session.log` on the box; a profile in use by another Chrome blocks a second one. | | Shortcuts go to your own browser | Use full screen; Cmd/Ctrl+W, Cmd/Ctrl+T and the like always stay with your browser. | | `prized desktop` says the box is unreachable | Same tunnel as `prized ssh`: run `prized doctor`. | --- # Agents > Start a coding agent, close the laptop, and let it work. An agent is an ordinary process on a machine you already pay for, so nothing it does is metered. URL: https://prized.dev/docs/agents ## Start an agent [#start-an-agent] ```bash prized agents run claude ``` This starts Claude Code in a detached tmux session named `claude` on the box, in your synced project, and prints the reattach command; closing the lid changes nothing. If the session already exists, `run` says so instead of starting a duplicate. | Flag | What it does | | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `--attach` | Land in the session as it starts. Otherwise reattach later with `prized ssh -s claude`. | | `--dir` | Working directory. Defaults to your only synced project; a [clone](https://prized.dev/docs/sync#add-a-project) is not one, so pass `--dir ~/code/`. | | `--session` | Session name, for a second concurrent run of the same agent. | | `-- ARGS` | Everything after `--` goes to the agent. | > A working agent holds off [auto-pause](https://prized.dev/docs/boxes#auto-pause): its output and CPU count as activity. What a pause does to the process depends on the box size; see [Pause and resume](https://prized.dev/docs/boxes#pause-and-resume). ## The four agents [#the-four-agents] | Agent | On the box | Runs on the box | | ----------------------- | ------------------------------------------ | --------------- | | Claude Code (`claude`) | Preinstalled. | Yes | | Codex CLI (`codex`) | Preinstalled. | Yes | | Cursor (`cursor`) | Installed on demand (`--install-cursor`). | Yes | | ChatGPT app (`chatgpt`) | Drives `codex` over SSH from your machine. | No | `chatgpt` is a connection, not a process on the box: its tasks stop when your machine sleeps, so `prized agents run chatgpt` refuses; run `prized agents run codex` for a session that survives a closed laptop. Setting the app up is [Codex remote](https://prized.dev/docs/codex). ## See what is set up [#see-what-is-set-up] ```bash prized agents ls ``` One row per agent: installed and signed in on your machine and on the box, MCP servers found, and where it is running. Read-only; takes `--json`. ## Hand credentials to the box [#hand-credentials-to-the-box] ```bash prized agents handoff claude codex ``` Copies the named agents' sign-in and MCP config to the box, listing what would travel and asking first; `prized setup` runs the same step. Re-run it after changing MCP config. `--gh` adds your GitHub CLI token and git identity; `--install-cursor` installs Cursor's CLI first. What travels per tool is in [Credentials](https://prized.dev/docs/credentials). ## Drive it remotely [#drive-it-remotely] ```bash prized prompt --provider claude "fix the failing tests and run them again" ``` `prized prompt` runs the agent headless on the box, streams its events to your terminal, and exits with the verdict; `prized events` and `prized interrupt` follow and stop a run, and the same surface is an HTTP API. See [Prompt an agent remotely](https://prized.dev/docs/remote-agents). ## Several agents at once [#several-agents-at-once] One session per agent by default (`claude`, `codex`, `cursor`), each in its own project via `--dir`. For two runs of the same agent, name the sessions: ```bash prized agents run claude --session claude-api --dir ~/code/api prized agents run claude --session claude-web --dir ~/code/web prized ssh --ls ``` ## Bring your own agent [#bring-your-own-agent] The box is a normal Linux machine, so any CLI agent runs on it. Pick `other` in setup's agent step and it prints this pattern with your box's name filled in: 1. `prized ssh` opens the box, waking it if needed. 2. Install the agent's CLI in that shell. 3. Sign in there. The credential stays on the box disk, once per box; Prized never sees it. 4. Run it detached: `prized ssh -s myagent` opens a named session to start it in, or `tmux new-session -d -s myagent 'the-agent'` from any box shell. Reattach with `prized ssh -s myagent`. For an agent that wants an API key, prefer a [Connector](https://prized.dev/docs/connectors) over a key in a file: the box gets a placeholder and the real value never touches its disk. The handoff and `prized agents run` do not know the agent; sessions work the same. ## Review from your machine [#review-from-your-machine] For a synced project, the agent's edits stream into the folder on your machine as it works, so you read the diff in your own editor. This is why [sync](https://prized.dev/docs/sync#conflicts-the-box-wins) lets the box win conflicts: a stray local save should not revert an agent's work. A clone has no mirror; review it over git. > An agent on your machine can do the Prized setup itself: paste the prompt from [Agent setup](https://prized.dev/docs/agent-setup). Every CLI command speaks JSON for scripts; see [CLI](https://prized.dev/docs/cli#for-agents-and-scripts). --- # Prompt an agent remotely > Send Claude Code or Codex on your box a prompt from your machine or from any script, follow what it does as it happens, and interrupt it. The run lives on the box, not in a terminal, so nothing depends on your laptop staying open. URL: https://prized.dev/docs/remote-agents ## Prompt the agent [#prompt-the-agent] ```bash prized prompt --provider claude "fix the failing tests in app/ and run them again" ``` This starts a headless run of Claude Code on the box (`claude -p`, in your home directory), streams its events to your terminal, and exits when the agent is done. Nothing runs in a tmux session; the first prompt installs three small helper scripts on the box and nothing else. | Command | What it does | | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | `prized prompt --provider claude "…"` | Run Claude Code on the default box and follow it. `--provider codex` runs Codex. | | `prized prompt box-a7f3 --provider codex --cwd app "…"` | Run on a named box, in `~/app` (`--cwd` is relative to your home, or absolute). | | `prized prompt --provider claude --model opus --effort high "…"` | Pick the model and the reasoning effort (`low`, `medium`, `high`). | | `prized prompt --provider claude --continue "…"` | Continue the provider's newest session on the box instead of starting fresh. | | `prized prompt --provider claude --auto "…"` | Let the agent edit and run commands without approvals. See [Approvals](#approvals). | | `prized prompt --provider claude --detach "…"` | Start the run and print its id; follow it later with `prized events`. | | `prized prompt --provider claude --queue "…"` | When a run is in progress, queue this prompt behind it instead of refusing. | | `cat brief.md \| prized prompt --provider claude -` | Read the prompt from stdin. | | `prized prompt ls` | Every run on the box, newest first (at most 50), with its state. | | `prized events --follow` | Follow the run in progress (or show the newest); `--run pr_…` picks one, `--after N` starts after event N. | | `prized interrupt` | Stop the run in progress (TERM to its process group, KILL after 5 s); `--run pr_…` stops a specific one. | * Output: the agent's text plainly, each tool call and result as one dim line, and a last line with the run's verdict and event count; `-v` adds the provider's system events. * Exit code: 0 when the run ends cleanly, 1 when the provider reports an error, exits non-zero, or the run was interrupted, 130 on Ctrl-C, which also interrupts the run on the box (`prized events --follow` leaves the run alone on Ctrl-C). * The prompt travels to the box as a file, never on a command line, so it is not in the box's process list or shell history. A suspended box wakes for `prized prompt`; `prized events`, `prized prompt ls` and `prized interrupt` do not wake one. ## Sign in first [#sign-in-first] The agent runs as you, on the box, with the sign-in already there: hand your credentials over once with `prized agents handoff claude codex`. `prized prompt` refuses with `provider_not_signed_in` when the box has no sign-in for the provider (Claude Code: `~/.claude/.credentials.json` or `CLAUDE_CODE_OAUTH_TOKEN` in a login shell; Codex: `~/.codex/auth.json`). What travels is in [Credentials](https://prized.dev/docs/credentials); the interactive alternative is [Agents](https://prized.dev/docs/agents). ## Approvals [#approvals] By default the agent can edit files but not act freely: Claude Code runs with `--permission-mode acceptEdits` (commands it wants to run are denied, since a headless run cannot ask you), and Codex runs in its `workspace-write` sandbox. `--auto` lifts both (`claude --dangerously-skip-permissions`, `codex --dangerously-bypass-approvals-and-sandbox`), which is what "run the tests and fix them" needs. > `--auto` means no approvals at all: the agent runs any command and edits any file your account on the box can, with your credentials and synced projects in reach. Use it for work you would let the agent do unattended, and interrupt it when it strays. ## Continuity [#continuity] Every run records the provider's session id, and `--continue` starts the next run with `--resume ` (Claude Code) or `codex exec resume ` (Codex), so "now also update the docs" works; the newest session of the same provider on the box is the one continued, and `prized prompt ls --json` shows each run's `sessionId` and `resumedFrom`. With no earlier session on the box, `--continue` refuses (exit 5, `no_session_to_continue` on the API) rather than quietly starting fresh. ## One run at a time, and a queue [#one-run-at-a-time-and-a-queue] A box runs one prompt at a time. A second `prized prompt` while one is in progress answers `prompt_in_progress`; with `--queue` the text is queued and runs as soon as the current run ends, continuing its session. Two commands on the box itself do the same, so a setup script or any shell there can drive the agent: ```bash queuePrompt "then run the linter and fix what it finds" stopAgent ``` `queuePrompt` appends to the queue (from its arguments, or stdin); `stopAgent` interrupts the run in progress. Both live in `~/.prized/bin`, on the PATH of login shells after the first prompt. A queued prompt with no run in progress waits for the next `prized prompt` to finish; an interrupted run does not drain the queue. A prompt queued with `--queue` keeps its own provider, model, effort, working directory and `--auto`; one queued with `queuePrompt` inherits them from the run it follows. A queued prompt of the same provider continues the session of the run it follows (with `--continue`, the provider's newest session when it runs); a different provider starts fresh. ## Where a run lives on the box [#where-a-run-lives-on-the-box] Everything about a run is a directory on the box, so it survives a pause, rides a snapshot, and can be read with `ls` and `tail -f`: | File in `~/.prized/prompts//` | Contents | | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `prompt.txt`, `meta.json` | The prompt as given; provider, model, reasoning effort, and whether `--auto` was on. | | `cmd`, `next` | The provider command line for this run, and the one a continuation uses. | | `cwd`, `pid`, `started_at`, `exit` | The working directory, the process group id, the start time, the exit code when done. | | `events.jsonl`, `stderr.log` | The provider's raw event stream (`claude --output-format stream-json`, `codex exec --json`) and its stderr. | | `session`, `resume`, `parent` | The provider's session id once seen; for a continued run, the session it resumed; for a queued one, the run it followed. | | `interrupted` | Present when the run was stopped through `prized interrupt` or `stopAgent`. | `~/.prized/prompts/active` names the run in progress; `~/.prized/prompts/queue/` holds queued prompts, one directory each. > Run directories are never deleted for you. `rm -r ~/.prized/prompts/pr_…` on the box when you are done with one. ## JSON and JSON Lines [#json-and-json-lines] `--json` on `prized prompt` prints the started run as one object and does not follow. `--jsonl` follows and prints one normalized event per line: this is the one command, with `prized events --jsonl`, that prints more than one JSON document, because a live stream has no end to wait for. ```json {"seq": 7, "type": "tool_use", "text": "Let me run the suite.", "tool": {"name": "Bash", "input": {"command": "npm test"}}, "raw": {…}} ``` | Field | Meaning | | ------ | -------------------------------------------------------------------------------------------------------------------------------- | | `seq` | The event's line number in `events.jsonl`, from 1; paging and `--after` use it (a page holds up to 200 events or 1 MiB). | | `at` | The provider's timestamp when the line carries one. | | `type` | `system`, `assistant`, `tool_use`, `tool_result`, `result`, `error`, or `raw` for a line the mapping does not know. | | `text` | The readable content: the agent's text, a command, a tool's output, the final answer, an error (capped at 8 KiB). | | `tool` | For tool events, `{name, input}`; Codex's are `command_execution`, `file_change`, `mcp_tool_call` (`server/tool`), `web_search`. | | `raw` | The provider's line as written. | A run object (`prized prompt --json`, `prized prompt ls --json`, `prized events --json`) has `id, provider, model, reasoningEffort, auto, status (running, done, failed, interrupted), exitCode, pid, cwd, startedAt, finishedAt, sessionId, resumedFrom, parent, eventCount, prompt`. ## The edge API [#the-edge-api] The same surface is HTTP for scripts that hold a CLI token: `POST /prompts` starts or queues a run, `GET /prompts` lists them, `GET /prompts/{runId}/events` pages the events or streams them as Server-Sent Events (`follow=1`, or `Accept: text/event-stream`; a stream holds one of the token's concurrent request slots until the run ends), and `POST /prompts/{runId}/interrupt` stops one. Every route is under `$EDGE/v1/box/{box}/` with the token as the bearer, found as for [running commands](https://prized.dev/docs/exec#the-edge-api); only `POST /prompts` wakes a suspended box. Routes, bodies and answers are on [API reference: Prompts and events](https://prized.dev/docs/api#prompts-and-events). ## Errors [#errors] A failed request answers `{"ok": false, "error": {"code", "message"}}`. On top of the shared edge codes, these routes add `invalid_provider`, `prompt_required`, `invalid_request`, `prompt_in_progress`, `provider_not_signed_in`, `provider_not_installed` and `no_session_to_continue`, described on [API reference: Errors](https://prized.dev/docs/api#errors). From the CLI a run in progress is exit 6 (`CONFLICT`) and the rest exit 5 (`CHECK_FAILED`) with the hint. ## Limits [#limits] Every cap for prompts, runs, and events is on [Limits](https://prized.dev/docs/limits#prompting-an-agent-remotely). ## Compared with `prized agents run` [#compared-with-prized-agents-run] [`prized agents run`](https://prized.dev/docs/agents) starts the agent interactively in a tmux session you attach to and approve things in. `prized prompt` is the headless form: one prompt in, a stream of events out, a verdict at the end, and an API for scripts. Both use the same sign-in on the box and keep working when your laptop is closed. --- # Codex remote > Add your box as an SSH host in the ChatGPT desktop app and Codex runs its tasks on the box, with every edit syncing back to your Mac as it lands. One command sets the connection up. URL: https://prized.dev/docs/codex The ChatGPT desktop app can point Codex at an SSH host, and every task in that project then reads, writes, and runs commands there. `prized setup` covers everything the app needs: Codex signed in on the box, and the box listed in `~/.ssh/config`. If you skipped chatgpt during setup, run it once: ```bash prized setup --agent chatgpt ``` ## Add the box in the app [#add-the-box-in-the-app] In the ChatGPT desktop app, open **Settings > Connections** (under Coding). `ssh box-3fa9` from a terminal confirms the connection first. 1. **Click Add** under "SSH connections from this Mac". The Connections page in the ChatGPT desktop app settings, with an Add button under "SSH connections from this Mac" 2. **Tick the box and click Add.** The dialog lists the `Host` aliases from `~/.ssh/config`, so the box shows up under its hostname. The Add SSH Connection dialog listing the box by its hostname, with a checkbox and an Add button 3. **Check it shows Connected.** The saved connection appears with its toggle on; the app has started Codex on the box. The saved connection with a green Connected status next to the box's hostname Then pick a project folder on the box (a synced project lives at `~/code/`); the edits stream into your Mac's copy as they land. See [Agents](https://prized.dev/docs/agents). ## From your phone [#from-your-phone] The ChatGPT mobile app connects to the box directly, no Mac in the loop. Its Add SSH host form asks for a host, port, username, and password; the box page gives you all four. 1. Open the box in the [dashboard](https://prized.dev/dashboard), find **Phone access** under Access, and generate the password; it is shown once. 2. In the ChatGPT app, open **Settings > Connections > Add SSH host** and paste the four values. The password is a long random credential for this one box, checked by Prized's SSH gateway, which wakes the box and opens a terminal-only session over the private tunnel. The box itself stays closed to the internet. * **One password per box.** Generating a new one replaces the old; Revoke kills it. Deleting the box, removing the member who created it, or turning phone access off for the workspace revokes it too. * **Connecting wakes a paused box.** The first session after a wake takes a little longer. * **Terminal sessions only.** Port forwarding is refused on a phone session; use `prized ports` on your Mac for that. * **Idle connections close** after about fifteen minutes with no task running, and a handful can be open per box at once. The app reconnects on its own. * **Codex must be signed in on the box**, which `prized setup --agent chatgpt` already did. OpenAI gates mobile SSH hosts by rollout and app version, so check Settings > Connections if you do not see the form. Tasks on an SSH host cannot hand off to Codex's own cloud environments. ## Two caveats (desktop connections) [#two-caveats-desktop-connections] * **Wake the box first.** The desktop app connects with plain `ssh`, which does not wake a suspended box. Resume it from the dashboard or run `prized ssh` once, then connect. The phone path wakes the box itself. * **The app stops when your Mac sleeps.** For an agent that keeps working with the laptop closed, start Codex on the box with `prized agents run codex`; see [Agents](https://prized.dev/docs/agents). --- # Claude Desktop remote > Point the Claude desktop app at your box over SSH. The connection rides Prized's tunnel and a restricted device key that Prized installs for you, so there is nothing to configure on the box. URL: https://prized.dev/docs/claude-desktop ## Why this needs no setup [#why-this-needs-no-setup] Claude Desktop connects with its own SSH client, which honors your SSH config but does not present the SSH certificates Prized's terminal `ssh` authenticates with. Prized closes that gap: `prized login` registers this machine's SSH public key with your workspace, and every box accepts it as a restricted fallback. Terminal `ssh` never uses the fallback. ## Connect the box [#connect-the-box] 1. Log in once on this machine (`prized login`, part of the normal [quickstart](https://prized.dev/docs/quickstart)). Boxes pick up the device key within seconds. 2. In Claude Desktop, add the box as an SSH host by its hostname, for example `box-3fa9`. The stanza Prized installs supplies the tunnel and the key; if the app asks for an identity file, use `~/.config/prized/id_prized`. ```bash ssh box-3fa9 # confirms the connection from a terminal first ``` ## What the fallback key can do [#what-the-fallback-key-can-do] The key the app authenticates with is deliberately narrower than a certificate: | | Fallback key (desktop apps) | Certificate (terminal `ssh`) | | ----------------------------------- | --------------------------- | ---------------------------- | | Run commands, transfer files (SFTP) | yes | yes | | Interactive terminal (PTY) | no | yes | | Port forwarding | no | yes | | Works outside your Prized tunnel | no | no | The key is accepted only from inside your authenticated Prized tunnel, so a copy of the key file is useless on its own. ## Removing a device [#removing-a-device] Delete the device's CLI token, or remove the member from the [workspace](https://prized.dev/docs/workspaces); every box drops the key within seconds. A device silent for 45 days ages out on its own and re-registers when it next connects. ## Caveats [#caveats] * **Wake the box first.** The app's plain `ssh` does not wake a suspended box. Resume it from the dashboard or run `prized ssh` once, then connect. * **The app stops when your machine sleeps.** For an agent that keeps working with the laptop closed, run Claude Code on the box in a [session](https://prized.dev/docs/sessions) instead. * **Password prompt?** The box has not synced the key yet: make sure `prized login` ran here, give it a minute, retry; `prized doctor` checks the rest. --- # Boxes > A box is a dedicated Linux machine in the size you pick, from Nano to Extra Large. Create and manage boxes from the CLI or the dashboard; connect to them with the CLI. URL: https://prized.dev/docs/boxes ## Create [#create] ```bash prized box new # a fruit name, the size your balance runs comfortably prized box new mango --size Small --ttl 4h --wait ``` Create from the CLI (flags under [CLI: box new](https://prized.dev/docs/cli#box-new)) or the dashboard's [New box](https://prized.dev/docs/dashboard#new-box) page. A new box boots in about a minute; `--wait` blocks until it is running. Sizes mix freely, each at its own rate ([Billing](https://prized.dev/docs/billing)); a size the balance could not run for four hours cannot be picked unless [overage](https://prized.dev/docs/billing#overage) is on. `prized box ls` lists your boxes; `prized box info mango` shows one in full. What each size is made of is on [Machines](https://prized.dev/docs/machines). ## States [#states] | State | Meaning | | ------------ | ------------------------------------------------------------------------------- | | running | Up and reachable. | | provisioning | Being created or booted. About a minute. | | suspended | Paused. Compute is stopped; the disk stays. | | waking | Coming back from a pause. | | deep sleep | Suspended over 14 days: the disk is parked as a snapshot. Waking takes minutes. | | resizing | Changing size. Returns to running. | | migrating | Moving to another region; see [Regions](#regions). | | degraded | The box stopped reporting; it reboots itself. | ## Pause and resume [#pause-and-resume] ```bash prized box pause mango prized box resume mango --wait ``` The disk always survives a pause: files, tools, and git state stay put, and billing drops to the sleep rate at once. Running processes depend on the size: | Size | On pause | | ------------------ | ----------------------------------------------------------------------------------------------------------- | | Nano through Small | **Memory snapshot** (hibernate). Processes, tmux sessions, and half-finished builds resume where they were. | | Medium and larger | **Clean stop.** Files survive; processes restart on resume. | `prized ssh` wakes a suspended box on connect. A box's [desktop](https://prized.dev/docs/desktop) follows the same table. ## Auto-pause [#auto-pause] Set per box: **Auto-pause** on the box's dashboard page (30 minutes to 24 hours), `prized box auto-pause mango 2h` (up to 7 days), or `prized box new --auto-pause 45m`. The box pauses itself once nothing has used it for that long. Off by default on a plan; a box started on the [free credit](https://prized.dev/docs/billing#free-credit) gets one hour unless its create says otherwise. * **Counts as use:** typing or output in any terminal session, sustained CPU (builds, agents, test loops), sustained network traffic. A forgotten shell prompt does not. * **The 30 minute floor** is how long the box waits before calling itself idle; the box page shows the idle time so far. * **It is exactly a pause:** sleep rate, disk kept, processes per the size table. The event list shows it as `reconciler:auto_pause`. * **A workspace policy can cap the window** ([auto-pause floor](https://prized.dev/docs/teams#policies)); [quiet hours](https://prized.dev/docs/teams#quiet-hours) pause the whole workspace nightly. ## Pause deadline [#pause-deadline] ```bash prized box new ci-run --ttl 2h # pauses two hours from now prized box extend mango --ttl 12h # set, or push back, the deadline prized box extend mango --no-deadline ``` The other pause rule: a time. The box pauses itself then, busy or idle (a resize or move in flight finishes first). Set it with `--ttl` at create, `prized box extend` later, or **Pause at** on the box page; 5 minutes to 30 days. * **One pause, not a schedule.** The deadline clears when it fires or when the box wakes. * **Extending replaces.** A new `--ttl` counts from now; `--no-deadline` removes it. * **It needs a running box.** Setting one on a paused box is refused; clearing is always allowed. * **It stacks with auto-pause.** Whichever comes first pauses the box. * **It is exactly a pause**, shown in the event list as `reconciler:pause_deadline`. ## Resize [#resize] ```bash prized box resize mango Medium ``` The box stops, changes size, and starts again, files intact, metering at the new rate from that moment. Any size your balance can keep running is allowed. * **Sizing up** grows the disk during the same restart. Rarely a just-grown disk must wait about six hours before growing again; the resize still lands and the space appears on the next restart. * **Sizing down** keeps the disk you have (disks never shrink), which keeps costing its own sleep rate. * **A box mid-change cannot be resized**: refused while creating, resizing, or moving. Suspended boxes resize fine and take the new size when they next start. * **A box with a bigger disk than its size normally has** is priced with that disk, so the resize check quotes what you will actually pay. ## Regions [#regions] New boxes are created in US West (Oregon); boxes can also run in US West (N. California). Move with the box page's **Move region** button or `prized box move mango california` (`prized box new --region california` creates in Oregon and moves after the first boot). * **The box stays up while the disk copies**, then goes offline for a few minutes and comes back in the new region with its name, files, tools, vars, and secrets. * **The public IP changes**; `prized ssh` follows the box. * **Metering continues at the normal rate** throughout, and a size costs the same in both regions. * **Only a running box moves**, one move at a time. * **A move that cannot finish** leaves the box in its original region on its original disk. * **Four moves per box in 24 hours**, counting only moves that ran; moving needs a funded balance. ## Snapshots [#snapshots] Snapshot a box's disk without stopping it, restore into a new box, fork a running box, save named templates, or set a schedule. Take one before risky changes. See [Snapshots](https://prized.dev/docs/snapshots). ## Infinite storage (alpha) [#infinite-storage-alpha] Some boxes keep their files in a storage pool that grows on demand instead of on a fixed disk. A box like this shows an **Alpha - infinite storage** label and reports how much it is using rather than a disk size. You write to it exactly like a local disk: the same `/home`, the same Docker, the same tools, with space added behind the scenes as you fill it. The alpha is opt-in and enabled per workspace by the Prized team. While a box is on it, disk snapshots, forks, templates, and region moves are paused for that box; everything else works as usual. Ask us if you want a box on it. If the storage service on such a box fails, the box reboots itself and is back in about a minute. Files you saved are safe; writes from the last few seconds before the failure can be lost, as on a laptop that loses power. ## Environments [#environments] An environment is what a new box inherits: repos, a setup script, env vars, secret files, and which credentials the box may receive. Pick one at create time or mark a default; every save is a new version, and a running box keeps its version until you upgrade it. See [Environments](https://prized.dev/docs/environments). ## Several boxes [#several-boxes] Every box has a hostname: a fruit name unless you pick one (lowercase letters, digits, and hyphens, up to 32 characters). `prized ssh mango` connects to any box. The box `prized setup` connected first (or `prized login`, when your account had exactly one) is the **default box**: what every command means when you name none. Every other box gets a config and a daemon of its own. ```bash prized setup --box mango # connect a second box: its own config and daemon, then the usual setup steps prized --box mango status # aim any command at it PRIZED_BOX=mango prized sync ls # the same from the environment ``` * **Adding a box.** `prized setup --box mango` writes `boxes/mango.toml`, starts its daemon, and runs the normal setup flow; `prized init --box mango` only connects. With no default box yet, either makes the box the default. * **Aiming commands.** `--box NAME` (or `PRIZED_BOX`) works on every command, by hostname or, for `setup` and `init`, box id. `--config PATH` overrides everything and is the escape hatch, not the everyday way. * **Changing the default.** `prized init --force --box mango` rewrites the default config (backed up first) and restarts the default daemon. A box with its own file is refused; `prized --box mango daemon uninstall` and delete `boxes/mango.toml` first. * **Same-named projects.** Two boxes cannot sync a project under one name; the second `prized sync add` asks for `--name`. * **The same port on two boxes** shows as `conflicted` in `prized ports ls`; toggle it off on one box, or list it under `[ports].never` in that box's config. | For a box named `mango` | Where | | ----------------------- | ------------------------------------------------------------------------------------------------------ | | Config | `~/.config/prized/boxes/mango.toml`, the same schema as `config.toml` | | Agent token | `~/.config/prized/boxes/mango.token` | | WireGuard key | `~/.config/prized/wg-mango.key` | | Daemon socket and state | `~/Library/Application Support/prized/boxes/mango/prizedd.sock` (macOS and Linux) | | Service | launchd `com.hudware.prizedd.mango`, systemd `prizedd-mango.service`, or the `PrizedDaemon-mango` task | | Log | `prizedd-mango.log` in the usual [log folder](https://prized.dev/docs/cli#files) | On Windows these sit under `%LOCALAPPDATA%\Prized\boxes\`. ## Delete [#delete] ```bash prized box delete mango # asks you to type the name; -y skips the prompt ``` > Deleting a box terminates the machine and erases its disk. Anything you synced is already on your machine. The disk's last state is kept for 7 days under **Recently deleted** on the Snapshots page; restoring it undoes the delete. See [Data retention](https://prized.dev/docs/data-retention), including zero data retention, which turns the undo window off. --- # Machines > What a box is made of: sizes and disks, Ubuntu 24.04 on a dedicated x86_64 virtual machine, what comes preinstalled, and the Prized processes that run beside your work. URL: https://prized.dev/docs/machines ## Sizes [#sizes] The live shape and price table is at https://prized.dev/pricing. A box is one dedicated virtual machine in the size you pick; the size decides the vCPUs and memory, the disk it is born with, and the hourly rate. Pick it by label or short name (`prized box new --size Small`; `xs`, `s`, `m`, `l`, `xl`, or the ids `nano`, `micro`, `lite`, `flow`, `pro`, `max`, `ultra` all work) or from the create form; change it later with a [resize](https://prized.dev/docs/boxes#resize). Rates and the plan ladder are on [Billing](https://prized.dev/docs/billing). * **Nano and Micro are burstable**, sized for editors, agents, and small dev servers rather than sustained full-CPU load; Extra Small and up are fixed-performance. * **Memory decides what a pause keeps.** Nano through Small hibernate and resume where they were; Medium and larger stop clean ([Pause and resume](https://prized.dev/docs/boxes#pause-and-resume)). * **The [free credit](https://prized.dev/docs/billing#free-credit) starts Nano through Small,** one box at a time. Medium and larger come with a plan; a box already started on the credit can be resized within the same four sizes. * **No GPU on any size.** ## Disk [#disk] One encrypted volume is the root and the only disk: your home directory, Docker images, and the system all live on it. | Fact | Detail | | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Type | SSD (EBS gp3) at baseline performance, always encrypted; snapshots inherit the encryption | | Size | The Disk column above, 40 GB on a Nano to 2 TB on an Extra Large | | Growing | Sizing up grows the disk during the same restart ([Resize](https://prized.dev/docs/boxes#resize)) | | Shrinking | Never. A box that kept a bigger disk than its size keeps paying that disk's sleep rate, and a snapshot restores only into a size whose disk holds it ([Restore](https://prized.dev/docs/snapshots#restore-into-a-new-box)) | | Paused | The disk stays, at the sleep rate. After 14 days paused it is parked as a snapshot: deep sleep ([States](https://prized.dev/docs/boxes#states)) | | Snapshots | Block-level, incremental images of this disk, taken while the box runs ([Snapshots](https://prized.dev/docs/snapshots)) | ## Operating system [#operating-system] Ubuntu 24.04 LTS on x86\_64, fully upgraded when the box image was built. It is your machine: the login user has passwordless `sudo`, is in the `docker` group, and can `apt install` whatever is missing. * **Your login user is your first name**: lowercased, accents folded, leading digits dropped, letters, digits, and hyphens only, up to 31 characters (`José` signs in as `jose`). When nothing usable survives, or the name collides with a system account such as `git`, the user is `doppel`; a box restored or forked from a snapshot keeps its source box's user. `prized box info` shows it. * **Sandboxes work.** `bwrap` carries the AppArmor grant it needs, so the sandboxes Claude Code and Codex run commands in work; Ubuntu's restriction on unprivileged user namespaces otherwise stays on. ## Preinstalled software [#preinstalled-software] Baked into the box image, so a fresh box has it from first boot. Nothing is signed in: your agents' credentials come from [your machine](https://prized.dev/docs/credentials). | What | On the box | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------ | | Docker Engine, Compose, Buildx | `docker`, `docker compose`, `docker buildx`; the daemon starts at boot, and your user is in `docker`, so no `sudo` | | Claude Code, Codex CLI | `claude`, `codex`; per user, each updates itself in place | | GitHub CLI | `gh` | | Compilers and build tools | `gcc`, `g++`, `make`, `pkg-config`, `git` | | Python | `python3`, `pip`, `venv` | | Shell tools | `tmux`, `mosh`, `rg`, `fd`, `jq`, `htop`, `rsync`, `sqlite3`, `zip`, `unzip`, `zstd`, `curl`, `wget` | | Desktop stack | XFCE, Xvfb, x11vnc, Google Chrome, `xdotool`, `wmctrl`; nothing starts until you open the [desktop](https://prized.dev/docs/desktop) | | Sandboxing | `bwrap` (bubblewrap), with the AppArmor profile above | | Sync agent | Mutagen's agent, fetched on the first `prized sync add` or `prized setup` ([Sync](https://prized.dev/docs/sync)) | Not preinstalled: Node.js, Go, Rust, Homebrew, databases, and the rest of a working environment. The environment step of `prized setup` installs the versions your machine has ([Bring your environment](https://prized.dev/docs/environment)); for a first boot that repeats across boxes, use an [environment](https://prized.dev/docs/environments). ## Architecture and virtualization [#architecture-and-virtualization] | Fact | Detail | | --------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | CPU architecture | x86\_64 (amd64) only; no arm64 sizes. `prized env` rebuilds or re-pulls arm64-only Docker images for amd64 | | Virtualization | One virtual machine per box with its own kernel; not a container, not a shared host. Docker inside works as on any Linux machine | | Nested virtualization | Off. KVM is not available inside a box | | GPU | None | | Cloud credentials | None. A box carries no cloud role, so nothing you run finds provider credentials on it | Each size maps to one EC2 instance type: | Size | Instance type | | ----------- | ------------------------ | | Nano | `t3a.small` (burstable) | | Micro | `t3a.medium` (burstable) | | Extra Small | `m6a.large` | | Small | `m6a.xlarge` | | Medium | `m6a.2xlarge` | | Large | `m6a.4xlarge` | | Extra Large | `m6a.8xlarge` | ## Regions [#regions] | Region | AWS id | In the CLI | | -------------------- | ----------- | --------------------- | | US West (Oregon) | `us-west-2` | `oregon`, the default | | US West (California) | `us-west-1` | `california` | A box lives in one region and can move ([Regions](https://prized.dev/docs/boxes#regions)). The rate for a size is the same in both, and a snapshot stays in the region its box was in. ## What runs on the box [#what-runs-on-the-box] Beside your own processes, a fresh box runs a small set of Prized services, ordinary systemd units you can inspect (`systemctl status prized-agent`). | Process | What it does for you | What it can see | | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | `prized-agent`, as your login user | Lists listening ports and their processes, samples CPU, memory, disk, and network, lists tmux sessions, judges idleness for [auto-pause](https://prized.dev/docs/boxes#auto-pause), relays loopback-only ports for [mirroring](https://prized.dev/docs/ports), and updates itself | A read-only view of the system and the process table; it never pauses the box itself | | `prized-secretsd`, root, loopback only | The [secrets broker](https://prized.dev/docs/credentials#secrets-for-agents): swaps a placeholder for the real value on requests to the hosts you allowlisted | Secret values in memory only; idle until a secret is mounted | | `sshd`, root, tunnel only | Your [sessions](https://prized.dev/docs/sessions), sync, mount, `prized exec`, and every SSH-speaking tool | Accepts Prized's short-lived certificates, the device keys registered for you, and any key in `~/.ssh/authorized_keys` | | The tunnel, WireGuard and its websocket fallback | The two ports open to the internet, `udp/51820` and `tcp/443` | Encrypted tunnel traffic only | On demand: the Mutagen agent while a project syncs, the desktop stack while a desktop is open, and helper scripts the first [remote prompt](https://prized.dev/docs/remote-agents) installs. What reaches Prized from the box is telemetry (vitals, listening ports, top processes, versions), never your files, terminal output, secret values, or agents' sign-ins ([Security](https://prized.dev/docs/security), [Data retention](https://prized.dev/docs/data-retention#telemetry-retention-windows)). ## Networking [#networking] * **Inbound:** only the tunnel's two ports, on the box's own public IPv4 address. SSH is never on the internet; you reach it, and every port you run, through the tunnel ([Ports](https://prized.dev/docs/ports), [Sessions](https://prized.dev/docs/sessions), [Credentials](https://prized.dev/docs/credentials#the-machine-itself)). * **Public ports:** none. A box cannot open a port to the internet; to share a running server, mirror it to your machine and share from there. * **Outbound:** not filtered. * **Public IP:** not stable. It changes on a pause and on a [region move](https://prized.dev/docs/boxes#regions); `prized ssh` follows the box. --- # Snapshots > Point-in-time images of a box's disk. Take one by hand or on a schedule, restore it into a new box, fork a running box in one step, and save named templates new boxes start from. URL: https://prized.dev/docs/snapshots ## What a snapshot captures [#what-a-snapshot-captures] A snapshot is an image of the box's whole disk at one instant, taken while the box keeps running; the box is never paused for it. * **Crash-consistent.** It holds what had reached the disk at that instant, like a machine that lost power; anything still in memory (an editor buffer, a database's unflushed page) is not in it. Save or flush first if a moment matters. * **Block level, incremental.** The first snapshot of a disk copies every used block; later ones store only what changed since, so frequent snapshots of a quiet box are cheap. * **Independent of the box.** Deleting the box does not delete its snapshots, and restoring one never changes the box it came from. ## Kinds [#kinds] Every snapshot has a kind that says why it exists; the Snapshots page and `prized snapshot ls` show it. | Kind | Who takes it | Counts toward the limit | Goes away | | --------- | --------------------------------------------------- | ----------------------- | ---------------------------------------------------------------- | | manual | You, from the dashboard or `prized snapshot create` | yes | When you delete it | | template | You, by naming a snapshot | yes | When you delete it (dropping the name turns it back into manual) | | fork copy | `prized fork` or the Fork button | no | By itself, once the new box is running | | automatic | The schedule you set on a box | no | By itself, past the box's keep count or after keep x interval | Fork copies are transient: not listed by default, never restored or named, gone once the new box is running. `prized snapshot ls --kind fork` shows them while they exist; `prized fork --keep-snapshot` keeps the copy as a manual snapshot instead. ## Take a snapshot [#take-a-snapshot] ```bash prized snapshot create mango --name pre-upgrade prized snapshot ls ``` The command returns as soon as the snapshot is registered; it shows `creating` until the disk copy completes, then `available` (minutes for a big disk the first time, faster after that). One snapshot per box at a time: a second request while one is in flight is refused with `snapshot_in_progress`. The dashboard does the same from the Snapshots page or the Snapshot action on the box page. ## Restore into a new box [#restore-into-a-new-box] ```bash prized snapshot restore pre-upgrade --name mango-2 ``` Restore never rolls a box back in place: it creates a new box whose disk is the snapshot, in the snapshot's size and region, with the source box's login user (the home directory on that disk belongs to it). `--tier` picks any size whose disk holds the snapshot; a smaller disk is refused with the numbers. A restore is a create, so the box limit and the balance rules of a new box apply. ## Fork a box [#fork-a-box] ```bash prized fork mango --name mango-2 --wait ``` A fork is a snapshot and a restore in one call, while the source keeps running. The new box keeps the source's size, region, login user, and auto-pause setting; `--tier` picks another size whose disk holds the copy. The dashboard's Fork action on the box page does the same and opens the new box's page. * **Minutes, not seconds.** The new box waits for the disk copy, then launches and boots; `--wait` prints each step, otherwise the box page shows progress. * **A wait that never ends is a failure.** A disk copy still not done after 30 minutes marks the new box failed with the reason (delete it; the source is untouched). A very large disk's first snapshot can take longer than that: take a manual snapshot first, since later ones are incremental and fast. * **The copy is crash-consistent**, like any snapshot, and temporary: it deletes itself once the new box is running (or has failed for good) and never counts toward the snapshot limit. `--keep-snapshot` keeps it as a manual snapshot, where it does count. * **A fork is a create.** The box limit and the balance rules of a new box apply; if either refuses, nothing is created. ## Templates [#templates] A template is a snapshot with a reusable name. New boxes start from it by name, from the create form's Start from picker or the CLI. ```bash prized snapshot template mango web-stack # snapshot mango now and name it prized snapshot template snp_1a2b3c web-stack # or name an existing snapshot by its id prized snapshot restore web-stack --name api-2 prized snapshot untemplate web-stack # drop the name; the snapshot stays ``` * **Box or snapshot id.** The first argument is a box name (a fresh snapshot is taken and named) or a snapshot id starting with `snp_`; a snapshot's own name is not accepted there. * **Saving a name again moves it.** The name goes to the newer snapshot and the previous holder turns back into a plain manual snapshot, kept, not deleted. * **Templates count toward the snapshot limit** like manual snapshots. Naming an automatic snapshot turns it into a counted one and is refused when the limit is full; a fork copy cannot be named. * **A template is ready once its snapshot is `available`**; creating a box from one still being taken is refused with `snapshot_in_progress`. ## Automatic snapshots [#automatic-snapshots] ```bash prized box auto-snapshot mango --every 6h --keep 5 prized box auto-snapshot mango --every off ``` With a schedule set, the box's disk is snapshotted whenever the newest automatic snapshot is older than the interval, and the newest `keep` are kept. The box page has the same control under Automatic snapshots. * **When they run.** While the box is running or paused; a box in deep sleep is skipped until it has a disk again, and a manual snapshot in flight defers the scheduled one. * **Retention.** At most `keep` per box, each expiring after keep x interval (every 6 hours keeping 5 is a 30 hour window). Switching the schedule off stops new ones; those already taken still expire, except one a new box is still launching from. * **Outside the limit.** Automatic snapshots never count toward the 20 snapshot limit, but need a funded balance or an active plan like any snapshot. Names are `auto--`, in UTC. ## Limits [#limits] Every cap, the 20-snapshot count included, is on [Limits](https://prized.dev/docs/limits#snapshots). ## CLI [#cli] Every command takes `--json` and prints one object. | Command | What it does | | ---------------------------------------------------------------- | ------------------------------------------------------------------------------- | | `prized snapshot create [--name N] [--description D]` | Snapshot the box's disk now | | `prized snapshot ls [--box B] [--kind K]` | List snapshots, newest first; `--kind fork` shows transient fork copies | | `prized snapshot restore [--name NEWBOX] [--tier T]` | A new box from a snapshot or template; `--tier` is any size whose disk holds it | | `prized snapshot delete [-y]` | Delete a snapshot (asks first) | | `prized snapshot template ` | Name a snapshot by id, or snapshot a box now and name it | | `prized snapshot untemplate ` | Drop a template name; the snapshot stays | | `prized fork [--name] [--tier] [--keep-snapshot] [--wait]` | A new box from a copy of the box's disk | | `prized box auto-snapshot --every 6h\|off [--keep N]` | Set or clear the box's snapshot schedule | Exit codes follow the [CLI contract](https://prized.dev/docs/cli#for-agents-and-scripts): a busy box, a taken name, or the box limit is `CONFLICT` (6); an unknown box or snapshot, a size that cannot hold the disk, and the snapshot limit are `CHECK_FAILED` (5). Every route, body, and refusal code is on the [API reference](https://prized.dev/docs/api#snapshots). --- # 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. URL: https://prized.dev/docs/environments ## What a box receives [#what-a-box-receives] Pick an environment at create time (the dashboard form, or `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 | `~/` (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 | `~/`, owned by your box user, mode 0600 unless you set another | Env vars and secret files are kept live: changes reach the box within seconds, and new login shells see them. Repos and the setup script run at first boot only. Secret files travel over the box's authenticated channel, never in the launch configuration, and the dashboard and CLI show only their path and size. ## The default environment [#the-default-environment] Nothing is a default until you mark one: "Make it the default for new boxes" when creating, "Make default" on its card, or `prized environments default `. A box created without naming an environment then takes it; `none` (dashboard) or `environment: null` (API) opts out. 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 a new description does not. A running box keeps the version it was created from until you upgrade it. A name is unique within the workspace regardless of case (`prod` and `Prod` are one name), so `--environment` on the CLI always means the same environment for everyone. ```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 ` (or **Upgrade** on the environment or a box's page) pins boxes to the latest version: * **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** is skipped (named in `skipped`) and keeps its version. Each environment keeps up to 50 versions; older ones no live box pins are pruned. Deleting an environment leaves its boxes on the version they carry. ## Safe for third parties [#safe-for-third-parties] A box built 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 the environment, 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 | | 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, but a safe environment cannot carry secret files (`safe_environment_has_secret_files`), and a restricted box never receives any: an environment's secret files are left out of a restricted box's setup at create and on every upgrade, whichever environment it runs. Restricted is one-way for a box: 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, a directory under your home (default: the repo name), an optional setup script run inside the clone, and a **blocking** flag: blocking repos clone before the others and before the environment's setup script. Clones are plain https, so no credential rides a URL into `.git/config`. 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. An existing directory is left alone. ## Vars and secret files [#vars-and-secret-files] Env vars are non-secret by definition: shell-identifier names, single-line values, shown in the dashboard and CLI. Put anything sensitive in a [secret](https://prized.dev/docs/connectors) or a secret file: a body of at most 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] 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. Environment plus per-box vars must fit the launch budget of about 16 KiB compressed, checked as rendered for a snapshot restore; 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 ` | Channels, repos, vars, secret files (path, mode, size), versions with box counts. JSON: `{"environment": {...}}` | | `prized environments new [--description] [--setup-script FILE] [--default]` | Create; `--default` marks it the default for new boxes | | `prized environments default ` | Make it the default for new boxes | | `prized environments rm ` | Delete (asks; `-y` skips); boxes keep their version | | `prized environments set --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 K=V` / `rm-var K` | Set or remove one env var | | `prized environments set-file --from FILE\|- [--mode 0600]` / `rm-file ` | Set a secret file from a local file or stdin, or remove it | | `prized environments add-repo owner/repo [--branch] [--dir] [--setup-script FILE] [--blocking]` / `rm-repo owner/repo` | Add, replace, or remove a repo | | `prized environments upgrade [--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 and are scoped to your workspace. Bodies are JSON. | 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. --- # Credentials > Prized does not store your dev credentials. setup hands your agents' sign-ins from your machine straight to the box over the SSH tunnel; the control plane never sees them, and they stay on the box. URL: https://prized.dev/docs/credentials ## Hand them over from your machine [#hand-them-over-from-your-machine] ```bash prized setup # or, for the agents alone: prized agents handoff claude codex ``` If you are signed in to a coding agent on your machine, the handoff moves that sign-in to the box over the SSH tunnel; the control plane never sees, stores, or proxies a credential. Consent is per concern: `prized setup` asks once for the agents it found, `prized agents handoff` asks per agent and lists the exact files; the GitHub token and git identity go along when gh is signed in here (`--no-gh` skips; `--non-interactive` runs and `prized agents handoff` need `--gh`); the Cursor installer (`--install-cursor`) needs its own yes. | Tool | What travels | | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Claude Code | Your machine's sign-in (the macOS Keychain item or `~/.claude/.credentials.json`) is copied to `~/.claude/.credentials.json` on the box, 0600, with your MCP servers and settings; since the box refreshes it from then on, one side will eventually ask you to `/login` again. Not signed in locally, or `--relogin`: the handoff runs `claude setup-token` (one browser step; needs a Pro, Max, or Team plan) and keeps the year-long token in `~/.config/prized/claude.token` for later boxes. | | Codex | `~/.codex/auth.json` and `config.toml` (which holds the MCP servers). | | Cursor | MCP and CLI config. Sign-in happens on the box: Cursor's credentials live in your system keychain, which Prized never reads, so `agent login` prints a URL to open. | | GitHub | Your `gh` token, piped to `gh auth login --with-token` (never on a command line), plus your git name and email if the box has none. | | Other CLIs | With `--env-secrets` (or a yes to the secrets question): the project's `.env` files, CLI sign-in files (`~/.aws`, `~/.config/gcloud`, `~/.kube/config`, ...), and secret-looking env vars from your shell files, written 0600. See [Bring your environment](https://prized.dev/docs/environment). | A restricted box (safe for third parties, or from an [environment](https://prized.dev/docs/environments#safe-for-third-parties) with that switch on) never receives your credentials: the handoff and setup's agent, secrets and data steps refuse with `CHECK_FAILED` (exit 5). They also stop when the box's status cannot be checked: signed out is the usual `AUTH` (exit 8, run `prized login`); offline, or a box that is not in your workspace, is `CHECK_FAILED` (exit 5). Nothing is copied until it can be; `prized setup` still syncs the project and runs its other steps, and skips the credential ones with the reason on their rows. MCP servers whose own sign-in cannot travel are reported as needing re-auth, with the command to run. The handoff is a one-shot copy; re-run it after changing MCP config. ### What the report means [#what-the-report-means] | Result | Meaning | | -------------- | ----------------------------------------------------------------------------------------- | | `ok` | The tool answers that it is signed in; for GitHub, as the same account your machine uses. | | `needs-reauth` | The material landed but the check disagreed, could not run, or found a different account. | | `manual` | Nothing to transfer; the printed steps sign you in on the box. | | `needs-rerun` | Skipped on purpose (the agent was running); re-run the handoff afterwards. | ### Symlinks and order [#symlinks-and-order] The handoff never writes a credential through a symlink, since on a box with a synced folder that could land it in your repo. A symlinked directory (`~/.codex`, `~/.cursor`, `~/.claude`, or a parent) stops the transfer and names the path; a symlinked file is replaced by a real file. Installs happen before any credential lands, and the GitHub token goes last. On your machine, only real files travel: a config file that is itself a symlink (`~/.codex/auth.json` pointing elsewhere) is left out, and inside `~/.claude/agents` and `~/.claude/commands` a symlink is followed only while it stays inside that folder. What was left out is named in the consent prompt and the report. `CODEX_HOME` and `CLAUDE_CONFIG_DIR` are honored when they name a directory inside your home; anything else is ignored and said so. ## Or sign in on the box [#or-sign-in-on-the-box] ```bash prized ssh gh auth login claude ``` One command per tool, in a shell on the box; disks persist, so it is once per box. This is what the handoff falls back to when there is nothing to transfer. ## Secrets for agents [#secrets-for-agents] For API keys an agent should use but never read, the dashboard has **Secrets**. Paste a value once; it is encrypted at rest and cannot be read back. Processes on the box see a placeholder, and when one calls an HTTPS host you allowlisted, the box's broker swaps the real value into the request headers on the way out; plain HTTP is refused. A secret with no grants is available to every box in the workspace except a contractor's; grant it to boxes or members and only those boxes can mount it. Each secret also shows which boxes spent it against which hosts, how often, and when (never a value). Both are on [Teams: Scoped secrets](https://prized.dev/docs/teams#scoped-secrets). Provider presets are [Connectors](https://prized.dev/docs/connectors). ## The machine itself [#the-machine-itself] Every box is its own machine, reached only through your encrypted tunnel; how that tunnel works, what the box exposes, and what the control plane can and cannot see are in [Security and trust](https://prized.dev/docs/security). ## What Prized itself stores [#what-prized-itself-stores] `prized login` writes a CLI token and an SSH identity to `~/.config/prized` on your machine; the token authenticates you to the control plane. Login also registers the identity's public key with your workspace, so your boxes accept it as a restricted, tunnel-only fallback for SSH clients that cannot use certificates ([Claude Desktop remote](https://prized.dev/docs/claude-desktop)). The private key never leaves your machine, and your dev credentials never leave your machines. **Dashboard → Workspace → CLI tokens** retires a machine's token, which signs it out and drops its key from your boxes ([Workspaces: CLI tokens](https://prized.dev/docs/workspaces#cli-tokens)). While signed in, the CLI reports which commands ran (name, version, OS, duration, exit code), never arguments, paths, or file contents. Opt out under [CLI: usage telemetry](https://prized.dev/docs/cli#usage-telemetry). --- # Connectors > Mount brokered credentials for ClickHouse, OpenAI, Anthropic, GitHub, or Slack into a dev box without writing the real value to its disk. URL: https://prized.dev/docs/connectors ## Connect a service [#connect-a-service] Open **Connectors** in the dashboard, choose a provider, pick a box, and paste the credential. Prized creates an encrypted secret allowlisted to the provider's hosts and mounts it under the conventional env name. A connector is a workspace secret, so a [contractor](https://prized.dev/docs/teams#roles)'s role does not include it. | Connector | Default env name | Allowed host | | ---------- | --------------------- | ------------------------------ | | ClickHouse | `CLICKHOUSE_PASSWORD` | The HTTPS endpoint you enter | | OpenAI | `OPENAI_API_KEY` | `api.openai.com` | | Anthropic | `ANTHROPIC_API_KEY` | `api.anthropic.com` | | GitHub | `GITHUB_TOKEN` | `api.github.com`, `github.com` | | Slack | `SLACK_BOT_TOKEN` | `slack.com` | The box sees a placeholder in that variable. The broker substitutes the real value only when an HTTPS request sends the placeholder unchanged in a header to the allowed host; plain HTTP is refused. ## ClickHouse [#clickhouse] Enter the HTTPS endpoint (for example `https://abc.clickhouse.cloud:8443`) and the password. Keep the endpoint, username, and database in your project's normal non-secret config; the connector mounts only the password, and the secret it creates is granted to the box you picked alone: widen it from **Dashboard → Workspace → Secrets** when another box should have it. ```bash curl -sS "$CLICKHOUSE_URL/?database=$CLICKHOUSE_DATABASE" -H "X-ClickHouse-User: $CLICKHOUSE_USER" -H "X-ClickHouse-Key: $CLICKHOUSE_PASSWORD" --data-binary 'SELECT 1' ``` ## Client compatibility [#client-compatibility] The client must send the env value unchanged in an HTTPS request header, as most bearer-token SDKs do. A client that transforms the value first (a Basic-auth base64 string, say) defeats the substitution: prefer a raw token header, or store the transformed header value as the credential when the upstream accepts it. ## Rotate or remove [#rotate-or-remove] Connectors use the same broker as **Secrets**. Rotate or delete the credential under **Secrets**; the box's **Secrets** tab shows and removes its env binding. --- # CLI > Every prized command, flag, and exit code, on macOS, Linux, and Windows. Add --help to any command; every non-interactive command also takes --json and prints exactly one JSON object. URL: https://prized.dev/docs/cli ## Commands [#commands] Each group below lists its commands, their flags, and the object `--json` prints. On success that object is `{"ok": true, ...}` with the payload inline; the failure shape and the exit codes are under [For agents and scripts](#for-agents-and-scripts). ### Sign in and set up [#sign-in-and-set-up] `prized login` opens the dashboard to mint a CLI token, stores it in `~/.config/prized` (`%LOCALAPPDATA%\Prized` on Windows), sets up your SSH identity, and registers this machine's device key ([Claude Desktop remote](https://prized.dev/docs/claude-desktop)). With no config yet and exactly one box on the account it also connects that box; otherwise it leaves that to `prized setup`. Flags: `--token` (a CLI token from the dashboard or a single-use install token; the only login form `--json` accepts), `--token-stdin`, `--url`. JSON: `{"ok": true, "email", "workspace", "boxes", "box", "url"}`. `prized setup [PATH]` sets this machine up to work on your box, safe to re-run: a repairing health check, then the project to sync, your environment, the agents, and a detached session. `prized setup auto` skips the auto-or-manual question ([Quickstart](https://prized.dev/docs/quickstart#4-set-up)). | Flag | Meaning | | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `--project`, `--no-project` | The project to sync (a name or a path), or none. | | `--copy`, `--copy-once`, `--clone` | Copy and keep syncing, copy once with no sync, or `git clone` on the box; the default syncs in place. | | `--merge` | Merge into an existing folder on the box instead of refusing. | | `--env`, `--env-secrets`, `--env-data`, `--no-env` | The [environment](https://prized.dev/docs/environment) groups to bring; data is never covered by auto or `-y`. | | `--env-workers N` | How many environment items install at once (default 8); `1` installs them in order. | | `--agent NAME`, `--no-agents`, `--save` | Which agents to hand off (`claude`, `codex`, `cursor`, `chatgpt`); `--save` records the pick. | | `--gh`, `--no-gh`, `--install-cursor`, `--relogin` | The GitHub token and git identity; Cursor's installer; a fresh claude token instead of this machine's sign-in. | | `--start`, `--no-start`, `--attach`, `--no-attach`, `--session NAME` | The detached session setup starts for the picked agent; `--attach` opens it in this terminal instead of printing the reattach command. | | `--box NAME` | The box to set up; one other than the default gets its own config and daemon ([Several boxes](https://prized.dev/docs/boxes#several-boxes)). | | `--dry-run`, `--non-interactive` | Print everything a run would do and change nothing; never prompt. | JSON: `{"ok": true, "steps": [{"id", "status": "done|skipped|failed", "duration"}], "state": "READY|DEGRADED", "env"}`. `prized init` adopts a box on your account (config, SSH stanza, daemon); `login` and `setup` run it for you. `--box NAME` connects another box with its own `boxes/.toml` and daemon; `--force` makes it the default instead, rewriting `config.toml` (backed up first) and refusing a box that already has its own file; `--user NAME`, `--no-daemon`, `--non-interactive`. `prized env scan|plan|migrate` is the environment step on its own: list what your machine has, print the questions, apply them. Flags: `--project PATH`, `--advisor`/`--no-advisor`, `--non-interactive` ([Bring your environment](https://prized.dev/docs/environment)). ### Box lifecycle [#box-lifecycle] Every action works from the CLI and the [dashboard](https://prized.dev/dashboard); what each does to the machine is on [Boxes](https://prized.dev/docs/boxes). * **Naming a box.** `BOX` is a hostname (`mango`), a box id, or an alias: `current` (the last box this machine created) or `self` (the box the command runs on). With no `BOX`, the box you set up is meant. An unknown name exits 5 and lists your boxes. * **Sizes.** Tier id or public name: `nano` (Nano), `micro` (Micro), `lite` (Extra Small), `flow` (Small), `pro` (Medium), `max` (Large), `ultra` (Extra Large). Prices: [Billing](https://prized.dev/docs/billing). On the [free credit](https://prized.dev/docs/billing#free-credit), Nano through Small, one box at a time; a bigger size is refused with exit 5, a second box with exit 6, until a plan is live. * **Windows.** `--ttl` and `--auto-pause` take a duration with a unit: `45m`, `4h`, `1h30m`, `2d`. #### box new [#box-new] ```bash prized box new # a fruit name, the size your balance runs comfortably prized box new mango --size Small --ttl 4h --auto-pause 45m --wait ``` Returns as soon as the control plane has the box; it boots in about a minute and becomes `current`. | Flag | Meaning | | --------------------- | -------------------------------------------------------------------------------------------------------------- | | `NAME` | Lowercase letters, digits, and hyphens, starting and ending with a letter or digit, up to 32 characters. | | `--tier`, `--size` | The size (the two flags mean the same thing). | | `--ttl WINDOW` | A [pause deadline](https://prized.dev/docs/boxes#pause-deadline), 5m to 30d from now. | | `--auto-pause WINDOW` | [Idle auto-pause](https://prized.dev/docs/boxes#auto-pause), 30m to 7d. | | `--environment NAME` | Apply a saved [environment](https://prized.dev/docs/boxes#environments) at first boot, by name or `env_` id. | | `--from-snapshot ID` | Restore a [snapshot](https://prized.dev/docs/boxes#snapshots) into the new box; its size is the snapshot's. | | `--region REGION` | `us-west-2` (Oregon, the default) or `us-west-1` (California), a [move](https://prized.dev/docs/boxes#regions) after first boot. | | `--wait` | Poll until the box is running (5 minute budget), then print the `prized ssh` line. | A taken name, the box count, or a balance that cannot run the box exit 6 (`CONFLICT`); a size not on your plan, a bad name, or a window out of bounds exit 5. #### The rest of the box group [#the-rest-of-the-box-group] Each prints `{"ok": true, "box": {...}}` with [the box object](#the-box-object) unless noted. | Command | What it does | | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `box ls [--all] [--filter STATES]` | NAME, STATE, SIZE, REGION, AUTO-PAUSE, PAUSE-AT, CREATED; `--all` adds terminated boxes. JSON: `{"boxes"}`. | | `box info BOX` | State, size, region, tunnel IP, login user, disk, pause settings, last vitals, last eight events. JSON adds `events` and `pausesSurvived`. | | `box pause BOX`, `box resume BOX [--wait]` | Aliases `stop`, `wake` ([Pause and resume](https://prized.dev/docs/boxes#pause-and-resume)); `--wait` polls until running. Idempotent; a resume the balance cannot cover exits 5. | | `box delete BOX [-y]` | Permanent: machine and disk gone. Asks you to type the name; `--json` needs `-y` (exit 2 without). | | `box rebind BOX` | Binds the ssh alias for a name to the box that wears it now. An alias is bound to the box it first connected to; if the box was recreated, or its freed name was taken by another box, `ssh BOX` and the prized commands refuse the name until you rebind it on purpose. | | `box resize BOX SIZE` (or `prized resize SIZE`) | Restarts the box at the new size, metered at the new rate from then on ([Resize](https://prized.dev/docs/boxes#resize)). Refusals exit 5. JSON adds `from`, `to`. | | `box move BOX REGION` | Moves the box ([Regions](https://prized.dev/docs/boxes#regions)); `desiredRegion` is set at once, `region` changes when the move lands. | | `box extend BOX --ttl WINDOW \| --no-deadline` | Sets, replaces, or clears the [pause deadline](https://prized.dev/docs/boxes#pause-deadline); setting one needs a running box (exit 5), clearing always works. | | `box auto-pause BOX WINDOW\|off` | Sets the [idle window](https://prized.dev/docs/boxes#auto-pause) (30m to 7d, or bare minutes) or switches it off. | | `box auto-snapshot BOX --every 6h\|off [--keep 5]` | Sets or clears a [snapshot schedule](https://prized.dev/docs/snapshots#automatic-snapshots): every 1 to 168 hours, keeping 1 to 10. | #### The box object [#the-box-object] ```json { "id": "5d0a…", "hostname": "mango", "observedState": "running", "desiredState": "running", "tier": "flow", "instanceType": "m6a.xlarge", "region": "us-west-2", "desiredRegion": "us-west-2", "loginUser": "hudson", "boxIp": "10.77.3.1", "diskGb": 100, "autoPauseMin": 45, "pauseAt": "2026-09-01T18:30:00Z", "lastHeartbeatAt": "2026-09-01T15:17:52Z", "createdAt": "2026-08-29T12:00:00Z", "vitals": {"cpu_pct": 12, "mem_pct": 41, "disk_pct": 63, "idle_for_sec": 720} } ``` `autoPauseMin`, `pauseAt`, `diskGb`, `lastHeartbeatAt`, and `vitals` are `null` when unset or unknown. Timestamps are RFC 3339 UTC. ### Snapshots [#snapshots] `prized snapshot create BOX [--name NAME] [--description TEXT]` snapshots a disk without stopping the box; `snapshot ls [--box BOX] [--kind KIND]` lists them; `snapshot restore SNAPSHOT [--name NAME] [--tier SHAPE]` launches a new box from a snapshot or template; `snapshot delete SNAPSHOT` retires one; `snapshot template SNAPSHOT NAME` names a snapshot (or a box, snapshotting it first) as a template and `snapshot untemplate NAME` drops the name. `prized fork BOX [--name NAME] [--tier SHAPE] [--keep-snapshot] [--wait]` makes a new box from a copy of a running box's disk. Behaviour, kinds, and limits: [Snapshots](https://prized.dev/docs/snapshots). ### Environments [#environments] `prized environments` (alias `envs`) manages what a new box inherits: `ls`, `info`, `new`, `default`, `rm`, `set`, `set-var`/`rm-var`, `set-file`/`rm-file`, `add-repo`/`rm-repo`, `upgrade`. Every command and its JSON is on [Environments](https://prized.dev/docs/environments). ### Secrets [#secrets] `prized secrets` is who a workspace secret is available to and what it was spent on; values are set and rotated in the dashboard, never here ([Scoped secrets](https://prized.dev/docs/teams#scoped-secrets)). | Command | What it does | | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `secrets ls [--box BOX]` | NAME, SCOPE, HOSTS, MOUNTS, ROTATED; with `--box`, only what that box may mount. | | `secrets grant NAME --box BOX \| --member EMAIL` | Make the secret available to one box, or to every box a member owns. The first grant narrows a workspace-wide secret to its grants. | | `secrets revoke NAME --box BOX \| --member EMAIL` | Take a grant back; the box loses the secret within seconds. Revoking the last grant makes the secret workspace-wide again. | | `secrets grants NAME` | Who has it: KIND, TO, SINCE. | | `secrets usage NAME` | Which boxes spent it against which hosts: BOX, HOST, FIRST, LAST, COUNT (a floor). | `grant` and `revoke` take exactly one of `--box` and `--member` (exit 2 otherwise); an unknown secret, box, member, or grant exits 5; a `409` exits 6. JSON mirrors the columns. ### Connect [#connect] `prized ssh [BOX] [SESSION] [-- CMD...]` connects in a persistent tmux session, wakes a suspended box first, and, for a box other than your default, forwards its ports for the life of the session ([Sessions](https://prized.dev/docs/sessions)). Flags: `-s, --session NAME` (default `main`), `--ls`, `--kill NAME` (asks unless `-y`), `--raw` (no tmux), `--no-forward`, `--no-drop-upload`. Interactive forms reject `--json` (exit 2); after connecting, the exit code is ssh's or the remote command's. JSON: `--ls` `{"sessions": [{"name", "windows", "created", "attached"}]}`, `--kill` `{"killed"}`. Every command that reaches the box over SSH (`ssh`, `exec`, `setup`, `doctor`, the agent handoffs) runs the OpenSSH client from the system directories (`/usr/bin`, or `System32\OpenSSH` on Windows), never one found on `PATH`. An ssh installed elsewhere is named with `bin = "/path/to/ssh"` under `[ssh]` in the config. `prized mosh [BOX] [SESSION]` is the same session over mosh (needs mosh installed locally; not on Windows yet), with `-s`, `--raw`, `--no-forward`, `--no-drop-upload`. `prized up` starts the box and reconnects everything from cold, then prints the `status` block; `--resume` resumes paused syncs, `--wait-timeout` (default `5m`) bounds the wait. `prized proxy BOX PORT[:LOCALPORT]` forwards one box port to localhost until interrupted. ### Run commands and files [#run-commands-and-files] `prized exec [BOX] [flags] -- CMD...` runs one command on the box and exits with its exit code; `--cwd`, `--timeout`, and `--env K=V` shape the run, `--detach` starts it and returns an id, and `--status ID`, `--ps`, `--kill ID` inspect or stop detached runs without waking a paused box. JSON: `{"ok": true, "exitCode", "stdout", "stderr"}`. `prized cp [-r] SRC DST` copies files scp-style: `box:path` names a path on a box, `:path` one on your box. Both are on [Run commands and files](https://prized.dev/docs/exec); both report what they did (never output or contents) to the [audit log](https://prized.dev/docs/teams#audit-log) as a **reported** row. ### Desktop [#desktop] `prized desktop [BOX]` opens the box's desktop in the dashboard and forwards its VNC port until interrupted; `--no-open` prints the `vnc://` address and password, `--stop` ends it. `prized browser [BOX]` is the same with one Chrome window (`--profile`, `--url`). Starting either reports a `desktop.start` row (mode and profile, never the password) to the audit log. See [Desktop](https://prized.dev/docs/desktop). ### Sync [#sync] `prized sync add PROJECT | add --local PATH` mirrors a folder between the box and this machine ([Sync](https://prized.dev/docs/sync)). Flags: `--local PATH` (a folder of your own, in place, instead of `~/Prized/`), `--remote PATH` (default `~/code/`), `--name NAME`, `--prefer box|local` (default `box`), `--mode MODE` (default `two-way-resolved`), `--ignore` and `--unignore`, `--create` (make the remote folder), `--paused`, `--merge` (allow a non-empty local folder). `sync ls` lists sessions with state and problems; `pause`, `resume`, `flush` take a name or `--all` (`flush --timeout`, default `60s`); `repair NAME` recreates a halted session; `rm NAME` removes one (`--delete-local`, `--delete-box` also move the files aside). JSON: `add` `{"project"}`; `ls` `{"projects", "degraded"?}`; `repair` `{"repaired"}`; `rm` `{"removed", "files": "keep|trash_local|trash_box"}`. ### Mount [#mount] `prized mount [BOX]` mounts a box in Finder as a live volume, foreground until interrupted, or `-d, --detach` to serve it from a background process; macOS only ([Mount](https://prized.dev/docs/mount)). Flags: `--dir PATH` (default the home directory), `--at PATH` (default `~/Prized/boxes/`), `--read-only`, `--mount-opt OPTS`, `--no-open`. `prized unmount [BOX]` stops a mount, foreground or background. JSON: mount `{"box", "mountpoint", "pid", "detached", "log"}`; unmount `{"box", "mountpoint", "unmounted"}`. ### Ports [#ports] `prized ports ls` lists the box's listeners and their mirror state; `prized ports toggle PORT` switches one mirror on or off ([Ports](https://prized.dev/docs/ports)). JSON: `ls` `{"ports": [{"port", "label", "process", "state", "reason"}]}`; `toggle` `{"port", "state", "reason"}`. ### Agents [#agents] `prized agents ls` shows one row per coding agent: installed and signed in here and on the box, MCP servers, where it runs. `prized agents handoff [AGENT...]` copies the named agents' sign-in and MCP config to the box (`--gh`, `--install-cursor`, `--relogin`). `prized agents run AGENT` starts an agent detached in a tmux session (`--session`, `--dir`, `--attach`, `-- ARGS`). `prized agent upgrade [--version V]` upgrades the box agent, which normally updates itself. See [Agents](https://prized.dev/docs/agents). ### Prompt an agent remotely [#prompt-an-agent-remotely] `prized prompt [BOX] --provider claude|codex [flags] TEXT` runs the box's coding agent headless on one prompt and follows its events until it answers. Flags: `--model`, `--effort low|medium|high`, `--cwd`, `--continue` (resume the provider's newest session), `--queue` (run after the run in progress), `--detach` (start and return the id), `--auto` (no approvals); `-` reads the prompt from stdin. `prized prompt ls [BOX]` lists runs; `prized events [BOX] [--run ID] [--follow] [--after N]` shows or follows one; `prized interrupt [BOX] [--run ID]` stops the run in progress. Only `prompt` wakes a box. Exit: 0 when the run ends cleanly, 1 when it failed or was interrupted, 130 on Ctrl-C. JSON: `prompt --json` prints the started run and does not follow; `prompt --jsonl` and `events --jsonl` print one normalized event per line; `prompt ls` `{"runs", "active"}`; `events` `{"run", "events", "next", "finished"}`; `interrupt` `{"id", "wasRunning", "signal"}`. Shapes: [Prompt an agent remotely](https://prized.dev/docs/remote-agents). ### Team [#team] `prized team status` is what the team is doing now, `prized team log` what happened; neither needs a box config ([Teams](https://prized.dev/docs/teams)). ```bash prized team status prized team log --since 7d --kind exec,prompt --member ana@acme.com ``` `team status` prints every live box (BOX, OWNER, STATE, IDLE, SESSIONS, SPEND TODAY), then every member (MEMBER, ROLE, BOXES, SESSIONS, SPEND (CYCLE), LAST ACTIVE), then a totals line; a contractor gets their own boxes. JSON: the fields of [`GET /api/v1/team/status`](https://prized.dev/docs/api#team). `team log` prints the [audit log](https://prized.dev/docs/teams#audit-log) as TIME, MEMBER, KIND, BOX, DETAIL, ORIGIN (`server` or `reported`); contractors exit 5. Flags: `--since WINDOW` (default `24h`), `--kind KINDS`, `--box BOX`, `--member EMAIL`, `--limit N` (default 100, at most 1000). JSON: `{"events": [{"id", "at", "kind", "origin", "actorKind", "actorUserId", "actorTokenId", "boxId", "detail"}], "nextBefore"}`. ### Health and updates [#health-and-updates] `prized status` is box, sync, ports, and daemon state at a glance; `--watch 2s` keeps it open. JSON: `{"box", "sync", "ports", "daemon", "update": {"available"}}`, where `box.transport_path` is `udp`, `ws` (the port 443 fallback), or empty while the tunnel is down; an unreachable box prints `"ok": false` and exits 3. `prized doctor` is the full health check; it applies safe repairs by default, marks them `(fixed)`, and prints only checks that failed, warned, or were fixed ([Troubleshooting](https://prized.dev/docs/troubleshooting)). Flags: `--no-fix`, `--strict` (warnings fail), `-v` (every check), `--bundle` (a diagnostics tarball; nothing leaves your machine); `--rotate-token`, `--fetch-token`, `--forget-hostkey`, `--pin-hostkey K`: agent token and host key maintenance. JSON: `{"summary": {"pass", "warn", "fail", "skip", "fixed"}, "checks": [{"id", "status", "detail", "hint", "fixed"}]}`. `prized version` prints client, daemon, and agent versions for the box in use (`--box NAME` for another). `prized update` updates prized and the daemon in place from signed releases (`--check` only reports, `--channel` picks one); it also runs by itself ([Updates](#updates)). `prized daemon install|uninstall|start|stop|restart|status|logs` manages the daemon of the box in use (launchd on macOS, systemd --user on Linux, a Scheduled Task on [Windows](#windows)). `prized mutagen ensure` fetches Prized's managed Mutagen; the installer, `setup`, and `doctor` run it for you. `prized ops status OP [--wait]` prints or polls a deletion operation (`dop_…`) the API returned ([Deletion operations](https://prized.dev/docs/data-retention#deletion-operations)). ### Completions [#completions] ```bash prized completion zsh > "${fpath[1]}/_prized" # zsh prized completion bash > /etc/bash_completion.d/prized prized completion fish > ~/.config/fish/completions/prized.fish ``` Tab then completes commands, flags, box names on every command that takes a `BOX` (cached for 15 seconds; offline or signed out, nothing completes and nothing complains), sizes on `resize`, regions on `move`, and windows on `auto-pause`. ## Global flags [#global-flags] | Flag | What it does | | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | `--json` | Exactly one JSON object on stdout. Interactive commands reject it; never inferred from a pipe. | | `-y, --yes` | Assume yes for confirmations. | | `-q, --quiet` | Suppress non-essential output. | | `-v, --verbose` | Verbose output; repeat for wire detail on stderr. | | `--timeout` | Per-request network timeout, default 10s. | | `--no-color` | Disable ANSI color; `NO_COLOR` is honored too. | | `--box NAME` | The box to act on (`PRIZED_BOX`): the default box, or another connected with `prized setup --box` ([Several boxes](https://prized.dev/docs/boxes#several-boxes)). | | `--config PATH` | The config file to use (`PRIZED_CONFIG`). Overrides `--box`; an escape hatch, not the way to a second box. | ## For agents and scripts [#for-agents-and-scripts] A failing `--json` command prints: ```json { "ok": false, "error": { "code": "CHECK_FAILED", "message": "…", "hint": "…" } } ``` `error.code` maps 1:1 to the exit code, and both are a stable contract: | Exit | `error.code` | Meaning | | ---- | ----------------------- | ---------------------------------------------------------------------------------------------------------- | | 0 | | Success. | | 1 | `INTERNAL`, `AWS_ERROR` | Internal or unexpected error. | | 2 | `USAGE` | A bad flag, an unknown size or region, a window without a unit, `box delete --json` without `-y`. | | 3 | `BOX_UNREACHABLE` | Box unreachable, or a `--wait` that ran out its five minutes. | | 4 | `DAEMON_UNAVAILABLE` | Daemon required but unavailable. | | 5 | `CHECK_FAILED` | A precondition failed; the `hint` says what to do. Unknown boxes, sizes not on your plan, refused changes. | | 6 | `CONFLICT` | Already exists or cannot fit: a taken box name, the box count, a balance that cannot run the box, a port. | | 7 | `PARTIAL` | A batch partly failed: some items of a `--all` run or an `agents handoff` succeeded, others did not. | | 8 | `AUTH` | Not signed in or token rejected; run `prized login`. | | 9 | `MISSING_DEP` | A dependency is missing; `prized doctor` usually fixes it. | | 10 | `DECLINED` | You declined a confirmation. | | 130 | | Interrupted. | * **Two exceptions.** `prized ssh` and `prized exec` exit with the remote command's code; `prized prompt --jsonl` and `prized events --jsonl` print one object per event. * **`--json` is opt-in.** Piping does not switch it on, so `prized box ls | head` still prints the table. * **The same token drives the [edge API](https://prized.dev/docs/exec#the-edge-api)** and every route on the [API reference](https://prized.dev/docs/api), with no `prized` installed. * **Sign in without a browser.** `prized login --token dcp_…` takes a CLI token from the dashboard; in a script use `--token-stdin`, since a command line is visible to other programs. Retire one under [CLI tokens](https://prized.dev/docs/workspaces#cli-tokens). * **A whole lifecycle in a script.** `prized box new ci-$RUN --size Small --ttl 2h --wait --json`, work over `prized ssh ci-$RUN -- make test`, then `prized box delete ci-$RUN -y --json`; the deadline is the safety net if the script dies. ## Files [#files] | Path | What lives there | | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `~/.config/prized/config.toml` | The config of the default box: box name, sync projects, port policy, preferred agents (`[agents]`). | | `~/.config/prized/boxes/.toml` | The config of every other box, same schema, with its agent token beside it (`.token`). | | `~/.config/prized/` | CLI token, SSH identity, and per-box certificates. | | `~/.local/state/prized/current-box` | The box `current` names. | | `~/Prized/` | The default local twin of synced projects. | | `~/.ssh/config.d/prized` | The SSH stanza that makes `ssh box-3fa9` work everywhere; prized's own commands read it directly, so they work even when `~/.ssh/config` does not apply its Include line. Each alias is bound to the box it first connected to (`prized box rebind` moves it). | | `~/Library/Logs/prized/` | Daemon logs (`~/.local/state/prized/` on Linux). | On Windows everything but the sync folder and the SSH stanza lives under `%LOCALAPPDATA%\Prized` (`state\`, `cache\`, `logs\`, `mutagen\`, `boxes\`, `bin\`). An install from before the rename keeps its `doppel` names, paths, and `DOPPEL_*` variables; `PRIZED_*` wins when both are set. ## Windows [#windows] The CLI runs on Windows 10 1809 or later and Windows 11, x64 and arm64, from PowerShell or Windows Terminal; the [quickstart](https://prized.dev/docs/quickstart#2-install-the-cli) has the install line. `prized ssh` uses the OpenSSH client that ships with Windows. * **The daemon is a Scheduled Task**, `PrizedDaemon` (`PrizedDaemon-` for a box connected with `--box`), started at logon, no administrator needed. `daemon stop` and `daemon restart` end it hard, so sessions open through it drop; `prized daemon logs` reads its log. * **Updates rename, then replace.** A running `.exe` cannot be overwritten, so an update moves the old one aside and the next start removes the leftover. * **No ControlMaster.** Windows OpenSSH has no connection sharing, so `prized setup` opens one connection per command; a few seconds slower. * **Not on Windows yet:** `prized mosh`, `prized mount` and `unmount`, drag-and-drop upload in `prized ssh`, and `prized env` (setup skips the environment step and says so). * **Unsigned binaries.** The Windows executables are not Authenticode-signed; see [Troubleshooting](https://prized.dev/docs/troubleshooting#windows). WSL is Linux to Prized: install with the `curl` line inside the distribution. ## Updates [#updates] The CLI keeps itself current: after a command finishes, at most once every fifteen minutes, it checks for a newer release and installs the same signed, checksum-verified download `prized update` performs; the next command prints a one-line note. The daemon restarts onto the new version as soon as no SSH session runs through it (a live `prized ssh`, a mount, or an external SSH client holds it; sync, port forwards, and mosh do not); `prized version` shows a pending restart, `prized daemon restart` does it now. With [several boxes](https://prized.dev/docs/boxes#several-boxes), each box's daemon restarts on its own, the next time a command reaches that box. Turn it off with `auto = false` under `[update]` in the config, or `PRIZED_NO_AUTO_UPDATE` in your environment. Homebrew installs are never touched (`brew upgrade prized` stays in charge), and from-source builds never self-replace. `prized update` is the manual path; `--check` only reports. A CLI below the minimum the API supports exits 5 with a `prized update` hint until you update. Releases before 0.7.0 do not update themselves; the dashboard names a machine still on one until `prized update` runs there. ## Usage telemetry [#usage-telemetry] While you are signed in, the CLI records which command ran, its version, OS, duration, and exit code, and sends those counts in occasional batches. It never records arguments, flags, paths, or file contents, and sends nothing while you are signed out. When a `prized setup` step fails, the batch also carries one word from a fixed list naming which check failed, never the message you saw. Turn it off either way: ```toml # in ~/.config/prized/config.toml [telemetry] enabled = false ``` ```bash # or per shell export PRIZED_NO_TELEMETRY=1 ``` This switch covers the CLI only. For the website and dashboard, see [Privacy](https://prized.dev/docs/privacy). --- # API reference > Every HTTP route a CLI token can call. The control plane at api.prized.dev/api/v1 manages boxes, snapshots, environments, secrets, policies, the audit log and team status; the edge runs commands, moves files, prompts the box's agent and stops its desktop. No SDK and no OpenAPI document yet; this page is the contract. URL: https://prized.dev/docs/api ## Two hosts, one token [#two-hosts-one-token] The control plane, `https://api.prized.dev/api/v1`, manages boxes, snapshots, environments, secrets, operations, the audit log, policies, team status and your account; it takes a box's `id` (a uuid) and answers errors as `{"error": "code"}`, sometimes with `message`. The edge, `$EDGE/v1/box/{box}/`, runs commands, moves files, prompts the agent and stops the desktop; it takes a box's name or id and answers errors as `{"ok": false, "error": {"code", "message"}}`. Both take the same bearer; read the edge's address from `edge.url` in `GET /api/v1/me` rather than assuming one. ```bash export PRIZED_TOKEN=dcp_… API=https://api.prized.dev/api/v1 AUTH="Authorization: Bearer $PRIZED_TOKEN" EDGE=$(curl -s "$API/me" -H "$AUTH" | jq -r .edge.url) ``` ## Authentication [#authentication] Every request carries `Authorization: Bearer dcp_…`, a CLI token from **Dashboard → Workspace → CLI tokens** (`prized login --token dcp_…` signs a machine in with one). Mint one per script or agent and name it, so it can be retired on its own; revoking takes effect on its next request (401) and closes the tunnels that device opened ([Workspaces: CLI tokens](https://prized.dev/docs/workspaces#cli-tokens)). * **What a token can do.** It acts as you in the workspace it was minted in: every box, snapshot, environment and secret there (a [contractor](https://prized.dev/docs/teams#roles)'s token reaches only the boxes they own and no secrets). Minting credentials, membership and roles, policies, the audit export, billing, and closing the workspace are dashboard-only; those routes answer `401 unauthorized` to any bearer, so a leaked token cannot widen itself. * **Other credentials.** The dashboard's short-lived terminal tickets are accepted on the box read, wake and connect routes and on the edge's exec, files and prompt routes, for their one box only; monitor tickets and phone passwords are refused everywhere. * **The version header.** The CLI sends `X-Prized-CLI-Version`; one below the minimum answers `426 cli_update_required` with `minVersion`. Scripts should not send it. ## Conventions [#conventions] * **JSON in, JSON out.** Send `Content-Type: application/json`. Bodies are capped at 64 KiB (128 KiB on the secrets routes; 2 MiB on environment create, patch, files and repos); over the cap, or not JSON, is `400 bad_request`. A `204` has no body. * **Ids.** Control-plane routes take the box's `id`, never its name; `GET /api/v1/boxes` maps names to ids. Snapshots are `snp_…`, environments `env_…`, secrets `sec_…`, grants `sgr_…`, operations `dop_…`. An unknown, malformed, or foreign id is `404 not_found`, with nothing written. * **202 means in progress.** Creating, waking, pausing, resizing, moving, forking, restoring and deleting a box answer `202` with the row as written: `desiredState` has moved and `observedState` follows over seconds to minutes. Poll `GET /api/v1/boxes/{id}`, or `GET /api/v1/operations/{id}` for a delete. Setting writes answer `200` or `201`. * **Errors.** Codes are stable words; messages are for people. Refusals that need numbers carry them: `insufficient_balance` has `balanceMicros`, `requiredMicros`, `burnMicrosPerHour`; `snapshot_limit` has `limit`; `box_not_running` has `state`. * **Idempotency.** No route takes an idempotency key. A `POST /api/v1/boxes` retried after a lost response creates a second box; give it a `name` and the retry answers `409 name_taken` instead. Deletes are idempotent, and wake or suspend on a box already in that state is a `202` no-op. * **Limits.** The control plane's limits are counts: `409 box_limit`, `429 snapshot_limit`, `402 secret_limit`, `429 customer_session_limit` on the CLI's tunnel sessions. The edge admits 8 concurrent requests and 20 per second per token and per box, then answers `429 rate_limited` with `Retry-After` ([Limits](https://prized.dev/docs/limits#commands-and-files)). * **Times and money.** Timestamps are RFC 3339 UTC. Balances are in micro-dollars (`1000000` is $1). * **Stability.** This API is what the CLI and the dashboard use, and the box object is the one `prized … --json` prints. Fields and routes are added under `/v1` without notice; ignore fields you do not know. The CLI's own tunnel and housekeeping routes are not a scripting surface and are not listed. ## Account [#account] | Route | Answer | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GET /api/v1/me` | `customer` (`id`, `email`, `name`, `githubLogin`, `status`, `onboardingStep`, `onboardingCompletedAt`, `createdAt`), `user` (who the token acts as: `email`, `name`, `role`; `null` for a token with no user), `workspace` (`id`, `name`, `zeroDataRetention`, `closingAt`, `purgeAfter`), `billing` (`balanceMicros`, `burnMicrosPerHour`, `runwayHours`, `warnBelowHours`, `planAmountUsd`), `boxes` (every box, terminated included; a contractor's own), `edge.url` | `customer.id` is the workspace id. `box` (the oldest live box in `boxes`) is deprecated; read `boxes`. ## Boxes [#boxes] | Route | Body | Answer | | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | `GET /api/v1/boxes` | | `200 {boxes}`, newest first, terminated ones included (filter on `observedState`) | | `POST /api/v1/boxes` | `{name?, tier?, ttlMinutes?, autoPauseMin?, environment?, env?, restricted?, fromSnapshotId?, fromTemplate?}` | `202 {box}` with `observedState: "requested"`; poll until `running` | | `GET /api/v1/boxes/{id}` | | `200 {box, state, vitals, events, job, pausesSurvived, environment, envVars, restricted}` | | `PATCH /api/v1/boxes/{id}` | `{autoPauseMin?, ttlMinutes? \| pauseAt?, autoSnapshotHours?, autoSnapshotKeep?, quietHoursExempt?}` or, alone, `{env}` | `200 {box}` | | `DELETE /api/v1/boxes/{id}` | | `202 {box, operation}`; see [Operations](#operations) | | `POST /api/v1/boxes/{id}/wake` | | `202 {box}`, `desiredState: "running"` | | `POST /api/v1/boxes/{id}/suspend` | | `202 {box}`, `desiredState: "suspended"` | | `POST /api/v1/boxes/{id}/resize` | `{target_tier}` | `202 {box}` with the new `tier`; the box restarts | | `POST /api/v1/boxes/{id}/region` | `{region}` (`us-west-2` or `us-west-1`) | `202 {box}` with `desiredRegion` set; `region` changes when the move lands | | `POST /api/v1/boxes/{id}/fork` | `{name?, tier?, keepSnapshot?}` | `202 {box, snapshot}` | | `POST /api/v1/boxes/{id}/template` | `{name, description?}` | `201 {snapshot}`: a template of this box, taken now | | `GET /api/v1/boxes/{id}/metrics` | query `from`, `to` (epoch seconds or `…Z` ISO 8601; default the last hour, clamped to 30 days) | `200 {v, res, from, to, buckets, series, fs, ifaces}`; `res` is 30, 300 or 3600 seconds by span | | `GET /api/v1/boxes/{id}/procs-at` | query `t` (required) | `200 {v, at, procs, ports}`: the process and port snapshot nearest `t`, within five minutes, else `404` | **The box object** is the one on [CLI: The box object](https://prized.dev/docs/cli#the-box-object), plus `autoSnapshotHours`, `autoSnapshotKeep`, `environmentId`, `environmentVersionNo`, `envVars` (`[{name, value}]`), `restricted`, `ownerUserId` (the member who created it) and `quietHoursExempt`. `desiredState` is `running`, `suspended` or `terminated`; `observedState` is one of `requested`, `provisioning`, `bootstrapping`, `running`, `suspending`, `suspended`, `waking`, `deep_sleep`, `resizing`, `migrating`, `degraded`, `provision_failed`, `terminating`, `terminated` ([Boxes: States](https://prized.dev/docs/boxes#states)). **Creating.** `name` is a hostname (lowercase letters, digits and hyphens, starting and ending with a letter or digit, 1 to 32 characters; a fruit without one). `tier` is a size id (`nano`, `micro`, `lite`, `flow`, `pro`, `max`, `ultra`); without one the control plane picks what the balance runs comfortably (a Small while the workspace runs on the [free credit](https://prized.dev/docs/billing#free-credit) alone, which also starts only `nano` through `flow`: the rest answer `409 tier_unavailable` with `allowed` naming the sizes it does). A create on the free credit that omits `autoPauseMin` gets 60; `null` keeps auto-pause off. `ttlMinutes` (5 to 43200) is a [pause deadline](https://prized.dev/docs/boxes#pause-deadline), `autoPauseMin` (30 to 10080, or `null`) the [idle window](https://prized.dev/docs/boxes#auto-pause). `environment` is an id or name, `null` for none, absent for your default; `env` is `{NAME: "value"}`; `restricted` makes the box [safe for third parties](https://prized.dev/docs/environments#safe-for-third-parties) (a contractor's box always is). `fromSnapshotId` or `fromTemplate` (one, not both) restores instead of booting fresh; `tier` may then be any size whose disk holds the snapshot. **The detail answer.** `events` is the last 50 state changes as `{id, fromState, toState, reason, actor, at}`; `job` is the lifecycle change in flight (`op`, `step`, `attempt`, `lastError`, `nextRetryAt`) or `null`; `vitals` is the last heartbeat (`cpu_pct`, `mem_pct`, `disk_pct`, `idle_for_sec`, `listeners_count`, `agent_version`); `environment` is `{id, name, versionNo, latestVersionNo}` or `null`. **Patching** takes one family per request: the pause settings and snapshot schedule together (`autoPauseMin`; `ttlMinutes` or `pauseAt`, `null` to clear, only one of the two; `autoSnapshotHours` 1 to 168 or `null`; `autoSnapshotKeep` 1 to 10), or `{env}` alone, which replaces the per-box vars (100 names, 64 KiB in all). A deadline needs a running box (`409 box_not_running`); clearing one always works. `quietHoursExempt` keeps the box running through [quiet hours](https://prized.dev/docs/teams#quiet-hours) and is the owner's alone (`403 not_owner`); a member's `autoPauseMin` above the workspace's [auto-pause floor](https://prized.dev/docs/teams#policies) is `403 policy_denied`. | HTTP | `error` | When | | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 400 | `bad_request`, `invalid_name`, `invalid_template`, `invalid_tier`, `invalid_env`, `invalid_auto_pause`, `invalid_pause_at`, `invalid_auto_snapshot`, `environment_too_large` | The body is off; `message` names the window or the rule | | 400 | `resize_rejected`, `move_rejected` | A box mid-transition, a fifth move in a day, a region the box cannot use | | 403 | `restore_tier_not_allowed` | The size is off your plan or its disk cannot hold the snapshot | | 403 | `policy_denied`, `not_owner` | A [workspace policy](https://prized.dev/docs/teams#policies) refused a member (`rule` is `memberSpendCapUsd`, `memberBoxLimit`, `memberMaxTier`, `autoPauseFloorMin` or `contractor_restricted`); a quiet-hours exemption from anyone but the owner | | 404 | `not_found` | No such box, snapshot, template or environment in this workspace, or a box a contractor does not own | | 409 | `box_limit`, `name_taken`, `insufficient_balance`, `tier_unavailable`, `workspace_closing`, `wake_rejected`, `snapshot_in_progress`, `box_not_running` | In order: the plan's box count (1 while the workspace runs on the [free credit](https://prized.dev/docs/billing#free-credit) alone, with `message` and `limit`), a live box with that name, a balance short of four hours, a size the plan or the free credit refuses (`message` says which; `allowed` lists the free credit's sizes), a closing workspace, a wake the balance refuses, a template still being taken, a deadline on a paused box | ## Operations [#operations] | Route | Answer | | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | `GET /api/v1/operations/{id}` | `200 {operation}`: `id`, `kind` (`box_delete`, `snapshot_delete`), `targetId`, `status`, `requestedAt`, `completedAt`, `error` | A delete answers with one of these; `status` runs `pending`, `processing`, `completed`, or `failed` with `error`. Finished operations stay readable for 30 days. What a delete keeps and removes is on [Data retention](https://prized.dev/docs/data-retention#deletion-operations). ## Snapshots [#snapshots] | Route | Body | Answer | | ---------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------ | | `GET /api/v1/snapshots` | query `transient=1` to include fork copies | `200 {snapshots}`, newest first | | `POST /api/v1/snapshots` | `{boxId, name, description?}` | `201 {snapshot}` with `status: "creating"`; poll the list until `available` | | `DELETE /api/v1/snapshots/{id}` | | `202 {snapshot, operation}` | | `POST /api/v1/snapshots/{id}/restore` | `{name?, tier?}` | `202 {box}`, a new box from the snapshot | | `POST /api/v1/snapshots/{id}/template` | `{name}` | `200 {snapshot, previous}`; `previous` is the snapshot that held the name, or `null` | | `DELETE /api/v1/snapshots/{id}/template` | | `200 {snapshot}`, back to a plain snapshot | A snapshot is `{id, name, description, status, kind, templateName, expiresAt, boxId, sourceBoxHostname, tier, region, sizeGb, error, createdAt}` (`status`: `creating`, `available`, `failed`, `deleting`; `kind`: `manual`, `template`, `fork`, `auto`, `final`); other fields may disappear. Names are 1 to 64 characters of letters, digits, `.`, `_` and `-`, starting and ending with a letter or digit. Forking and templating a box are under [Boxes](#boxes); what each kind means is on [Snapshots](https://prized.dev/docs/snapshots). Refusals: `409 snapshot_in_progress`, `409 no_volume`, `409 insufficient_balance`, `409 name_taken`, `409 box_limit`, `409 snapshot_unavailable` (a failed, deleting, or transient fork snapshot as a template), `429 snapshot_limit`, `403 restore_tier_not_allowed`. A `POST /api/v1/boxes` with `fromTemplate` is `404` when no template wears the name. ## Environments [#environments] | Route | What it does | | -------------------------------------------------------------- | -------------------------------------------------------------------------------- | | `GET /api/v1/environments` | `200 {environments}` | | `POST /api/v1/environments` | Create; `201 {environment}` | | `PATCH /api/v1/environments/{id}` | Change any field; a config change 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 for new boxes | | `POST /api/v1/environments/{id}/toggles` | The safe-for-third-parties switch and the three credential channels | | `POST /api/v1/environments/{id}/vars`, `DELETE …/vars/{name}` | Set or remove one env var | | `PUT /api/v1/environments/{id}/files`, `DELETE …/files?path=` | Set or remove one secret file | | `POST /api/v1/environments/{id}/repos`, `DELETE …/repos?repo=` | Add or remove one repo | | `POST /api/v1/environments/{id}/upgrade` | Pin live boxes to the latest version; `200 {upgraded, skipped, latestVersionNo}` | Bodies, the environment object, limits and error codes are on [Environments: API](https://prized.dev/docs/environments#api). ## Secrets [#secrets] Brokered credentials: the box sees a placeholder, and the broker substitutes the real value only in HTTPS requests to the allowed hosts ([Credentials](https://prized.dev/docs/credentials#secrets-for-agents), [Connectors](https://prized.dev/docs/connectors)). | Route | Body | Answer | | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | `GET /api/v1/secrets` | query `box={id}` for only the ones that box may mount | `200 {secrets}`, never a value | | `POST /api/v1/secrets` | `{name, value, description?, hosts?, grants?}` — `grants` is a list of `{boxId}`, `{userId}` or `{email}`, the scope the secret is born with, committed with it | `201 {secret}`; `404` (a grant's box), `404 member_not_found` | | `PATCH /api/v1/secrets/{id}` | `{value?, description?, hosts?}` (at least one) | `200 {secret}`; a new value keeps the placeholder and stamps `lastRotatedAt` | | `DELETE /api/v1/secrets/{id}` | | `204`; its mounts, grants and usage go with it | | `POST /api/v1/secrets/{id}/grants` | `{boxId}`, `{userId}` or `{email}` (one of the three) | `201 {grant}`; an existing grant answers `201` again | | `DELETE /api/v1/secrets/{id}/grants/{grantId}` | | `204` | | `GET /api/v1/secrets/{id}/usage` | | `200 {uses}`: `[{boxId, hostname, host, firstAt, lastAt, count}]`, up to 1,000 rows | | `GET /api/v1/boxes/{id}/secrets` | | `200 {mounts}` | | `POST /api/v1/boxes/{id}/secrets` | `{secretId, envName}` to mount an existing secret, or `{name, value, envName, hosts?, description?}` to create and mount in one step | `201 {mount}` | | `DELETE /api/v1/boxes/{id}/secrets/{mountId}` | | `204` | A secret is `{id, name, description, hosts, placeholder, mountCount, scope, grants, createdAt, updatedAt, lastRotatedAt}`, where `scope` is `workspace` (no grants: every box whose owner is not a contractor may mount it) or `granted` (only the boxes named, and the boxes owned by the members named) and `grants` is `[{id, boxId, hostname, userId, email, createdAt}]`; a mount is `{id, envName, secret: {id, name, placeholder, hosts}}`. `name` is `UPPER_SNAKE_CASE` (up to 128 characters), `value` up to 16 KiB, `hosts` up to 32 entries, each a hostname or a `*.` wildcard; `envName` is a shell identifier. The grant rule is on [Teams: Scoped secrets](https://prized.dev/docs/teams#scoped-secrets). | HTTP | `error` | When | | ---- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | 400 | `bad_request`, `invalid_name`, `invalid_host`, `invalid_env_name` | The body is off | | 402 | `secret_limit` | The workspace's secret count is full | | 403 | `secret_not_granted`, `not_allowed` | A mount of a secret the box may not have (the create-and-mount form too; nothing is committed); a contractor on any secrets route or box mount | | 404 | `not_found`, `member_not_found` | No such secret, grant or box (a terminated box included); a grant to a user id or email that is not a current member | | 409 | `name_taken`, `env_name_taken`, `box_restricted` | A secret with that name, a mount on that variable, a box that is [safe for third parties](https://prized.dev/docs/environments#safe-for-third-parties) | ## Policies [#policies] | Route | Answer | | -------------------------------- | ------------------------------------------------------------------------------------------- | | `GET /api/v1/workspace/policies` | `200 {policy, updatedBy, updatedAt}`; owners and members; a contractor is `403 not_allowed` | `policy` is `{memberSpendCapUsd, memberBoxLimit, memberMaxTier, autoPauseFloorMin, quietHours, auditRetentionDays}`, each `null` while off except `auditRetentionDays` (90 by default); `quietHours` is `{start, end, days, allDayDays, timezone}`. Changing them is `PATCH /api/v1/workspace/policies` from a dashboard session only: the body is the keys to change, `null` clears one (`400 invalid_policy` with `field` and `message`, `403 not_owner`, `409 workspace_closing`). A create, wake, resize or fork a policy refuses answers `403 policy_denied` with `rule` and `message`. Bounds and what each rule does are on [Teams: Policies](https://prized.dev/docs/teams#policies). ## Audit log [#audit-log] | Route | Body or query | Answer | | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GET /api/v1/audit` | query `since`, `until` (RFC 3339 UTC or epoch seconds), `kind` (comma list), `box` (id), `actor` (user id), `origin` (`server` or `client`), `before` (a row id cursor), `limit` (1 to 1000, default 100) | `200 {events, nextBefore}`, newest first; `nextBefore` is `null` on the last page | | `GET /api/v1/audit?format=csv` | the same filters | `text/csv` as an attachment `audit-YYYYMMDD-HHMMSS.csv`, columns `id,at,kind,origin,actor_kind,actor_user_id,actor_token_id,box_id,detail`, every row of the range up to 50,000 | | `POST /api/v1/audit` | `{kind, boxId, detail?}` | `201 {id}`: a client report, from a CLI token or a terminal ticket | An event is `{id, at, kind, origin, actorKind, actorUserId, actorTokenId, boxId, detail}`; `actorKind` is `user`, `cli`, `ticket`, `mobile` or `system`. Owners and members read; a contractor is `403 not_allowed`. Only `exec`, `file.upload`, `file.download`, `prompt` and `desktop.start` may be reported; `detail` takes 16 keys of up to 40 characters with strings of up to 512, the body 64 KiB, and a ticket may report only on its own box. Kinds, details and what a reported row means are on [Teams: Audit log](https://prized.dev/docs/teams#audit-log). | HTTP | `error` | When | | ---- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | | 400 | `invalid_time`, `invalid_kind`, `invalid_box`, `invalid_actor`, `invalid_origin`, `invalid_cursor`, `invalid_limit`, `invalid_format` | The named query parameter is off; `message` says the shape | | 400 | `invalid_kind`, `bad_request` | A report of a kind clients may not claim, or a body that is off | | 403 | `not_allowed` | A contractor reading, or a monitor ticket reporting | | 404 | `not_found` | A report naming another workspace's box, or a ticket naming a box other than its own | ## Team [#team] | Route | Answer | | ------------------------- | ----------------------------------------------------------------------- | | `GET /api/v1/team/status` | `200 {now, cycle: {start, end}, scope, boxes, members, totals, recent}` | `scope` is `workspace`, or `own` for a contractor (their boxes only, and `members` empty). A box is `{id, hostname, tier, desiredState, observedState, ownerUserId, ownerName, ownerEmail, idleForSec, cpuPct, memPct, lastHeartbeatAt, sessions, lastPromptAt, lastExecAt, spendTodayMicros, spendCycleMicros}` (`idleForSec`, `cpuPct` and `memPct` are `null` on a paused box); a member is `{userId, name, email, role, boxes, sessions, spendCycleMicros, lastActiveAt}`; `totals` is `{spendTodayMicros, spendCycleMicros, running, sessions}`; `recent` is the newest 50 audit events. What a session, today and the cycle mean is on [Teams: Team activity](https://prized.dev/docs/teams#team-activity). ## The edge [#the-edge] Every route below is under `$EDGE/v1/box/{box}/`, where `{box}` is the box's name or id, with the same bearer; each request is checked against the control plane, so a revoked token dies on its next request. `POST /exec` and `POST /prompts` wake a suspended box; every other route answers `409 box_not_running` instead. Paths are read the way `prized cp` reads them: relative paths and `~/…` are under your home, absolute paths are taken as is, and every answer echoes the real path it touched. Every successful answer carries `ok: true` and a `type`. A failed one is `{"ok": false, "error": {"code", "message"}}`, with the codes under [Errors](#errors) below; a known path with the wrong method is `405` with `Allow`, an unknown path `404 not_found`, in the same envelope. ### Commands [#commands] | Route | Body or query | Answer | | ---------------------------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `POST /exec` | `{command, cwd?, timeoutSeconds? (1 to 600, default 30), env? (up to 32), detached?}` | `200 exec.result`: `exitCode, signal, stdout, stderr, stdoutTruncated, stderrTruncated, timedOut, startedAt, finishedAt, cwd` | | `POST /exec` with `detached: true` | the same | `200 exec.started`: `processId, pid, cwd, startedAt, logPath` | | `GET /exec` | | `200 exec.list`: `processes[]` of `processId, pid, running, lost, exitCode, signal, startedAt, command` | | `GET /exec/{processId}` | query `tail` (bytes; default 16384, max 1048576) | `200 exec.status`: the list fields plus `finishedAt, cwd, stdout, stderr, stdoutTruncated, stderrTruncated` | | `DELETE /exec/{processId}` | | `200 exec.killed`: `processId, wasRunning, signal` (`TERM`, or `KILL` when TERM was not enough) | The command runs under `sh -c` in your home (or `cwd`) as your box user. `timedOut` is set only when the time limit ended the command, so a command that exits 124 by itself is `exitCode: 124, timedOut: false`. A synchronous run that outgrows 1 MiB per stream keeps the last 1 MiB and sets the `Truncated` flag; use a detached run and `/download` on its log for the whole thing. The on-box process directory is on [Run commands and files](https://prized.dev/docs/exec#detached-processes). ### Files [#files] | Route | Body or query | Answer | | --------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GET /files` | query `path` | `200 file.read`: `path, encoding (utf8 or base64), size, mode, content`; over 1 MiB answers `413` and points at `/download` | | `PUT /files` | `{path, content, encoding? (utf8 or base64), mode? ("0644")}` | `200 file.written`: `path, size` | | `GET /download` | query `path` | The file as `application/octet-stream` with `Content-Length`, or a directory as `application/x-tar` with `X-Prized-Content: directory`; `X-Prized-Path` carries the real path | | `PUT /upload` | query `path`, `mode?`; the raw file as the body | `200 file.uploaded`: `path, size` | Size caps are on [Limits: Commands and files](https://prized.dev/docs/limits#commands-and-files). ### Prompts and events [#prompts-and-events] | Route | Body or query | Answer | | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `POST /prompts` | `{provider (claude or codex), prompt (up to 512 KiB), model?, reasoningEffort? (low, medium, high), cwd?, continue?, queue?, auto?}` | `202 prompt.started`: `run`; or `202 prompt.queued`: `queued, active` | | `GET /prompts` | | `200 prompt.list`: `runs[]` newest first (at most 50), `active` (the run in progress, or `null`) | | `GET /prompts/{runId}` | | `200 prompt.status`: `run` with the full `prompt`, `stderr` (last 4 KiB), `stderrTruncated` | | `GET /prompts/{runId}/events` | query `after` (a `seq`, default 0), `limit` (1 to 1000, default 200; a page also stops at 1 MiB) | `200 prompt.events`: `run, events[], next, finished`; pass `next` as the following `after` | | `GET /prompts/{runId}/events?follow=1` | or `Accept: text/event-stream` | Server-Sent Events: `event: prompt` per event, `event: done` with `{run, next}` when the run has ended, `event: error` with `{code, message}` if the box stops answering, a `: keep-alive` comment while nothing happens | | `POST /prompts/{runId}/interrupt` | | `200 prompt.interrupted`: `id, wasRunning, signal` | | `POST /prompts/interrupt` | | The same, for the run in progress | A run is `{id, provider, model, reasoningEffort, auto, status (running, done, failed, interrupted), exitCode, pid, cwd, startedAt, finishedAt, sessionId, resumedFrom, parent, eventCount, prompt}`; an event is `{seq, at, type, text, tool: {name, input}, raw}` ([Prompt an agent remotely: JSON and JSON Lines](https://prized.dev/docs/remote-agents#json-and-json-lines)). A stream holds one of the token's concurrent request slots until the run ends. Approvals, continuity, the queue and the run directory on the box are on [Prompt an agent remotely](https://prized.dev/docs/remote-agents). ### Desktop [#desktop] | Route | Body | Answer | | -------------------- | ------------------------------------------------ | ----------------------------------------------------- | | `POST /desktop/stop` | `{mode?}` (`desktop`, the default, or `browser`) | `200 {ok: true, box, mode}`; never wakes a paused box | This one route predates the edge envelope: its errors are the bare `{"error": "code"}` form, with `unauthorized` (a `dcp_` token only), `not_found`, `bad_request`, `box_not_running`, `box_unreachable`, `desktop_stop_failed`, `resolve_failed` and `control_plane_unavailable`. The desktop stream itself is a dashboard WebSocket; from a script use `prized desktop --no-open`, which prints a `vnc://` address and the password ([Desktop](https://prized.dev/docs/desktop)). ### Errors [#errors] Every edge route answers a failure as `{"ok": false, "error": {"code", "message"}}` with one of these codes: | HTTP | `error.code` | Meaning | | ---- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 401 | `unauthorized` | No bearer, a token the control plane rejected, or a credential of another class (a monitor ticket, a phone password) | | 404 | `not_found` | No such box on this account, process, run, path (or parent directory), or route | | 405 | `method_not_allowed` | A known route with the wrong method; `Allow` lists the right ones | | 409 | `box_not_running` | Any route but `POST /exec` and `POST /prompts` on a suspended box; the message says how to wake it | | 400 | `invalid_request` | The body is not the documented shape: bad JSON, an empty command, a bad env name or mode, a bad `model`, a `reasoningEffort` outside low/medium/high, a newline in `cwd` | | 400 | `invalid_timeout` | `timeoutSeconds` outside 1 to 600 | | 400 | `invalid_path` | A path with a NUL or newline byte, a directory where a file was expected, or no path | | 403 | `permission_denied` | The box's account cannot read or write that path | | 413 | `payload_too_large` | Over the inline (1 MiB) or transfer (1 GiB) cap | | 429 | `rate_limited` | Too many requests for this token or box; `Retry-After` says when | | 502 | `box_unreachable` | The box could not be reached, or did not wake in time | | 502 | `exec_failed` | The ssh session or the on-box script failed; the message carries the box's own words | | 5xx | `control_plane_unavailable` | The control plane could not be reached to check the token | The prompt routes add: | HTTP | `error.code` | Meaning | | ---- | ------------------------ | ------------------------------------------------------------------------------ | | 400 | `invalid_provider` | `provider` is not `claude` or `codex` | | 400 | `prompt_required` | `prompt` is missing or blank | | 409 | `prompt_in_progress` | A run is in progress; the message names it. Queue behind it, or interrupt it | | 409 | `provider_not_signed_in` | The provider has no sign-in on the box; run `prized agents handoff ` | | 409 | `provider_not_installed` | The provider's CLI is not on the box's login PATH | | 409 | `no_session_to_continue` | `continue: true` with no earlier run of the provider that recorded a session | ## Examples [#examples] Create a box and wait for it to run: ```bash BOX=$(curl -s -X POST "$API/boxes" -H "$AUTH" -H "Content-Type: application/json" -d '{"name":"ci-42","tier":"flow","ttlMinutes":120}' | jq -r .box.id) until [ "$(curl -s "$API/boxes/$BOX" -H "$AUTH" | jq -r .box.observedState)" = running ]; do sleep 5; done ``` Run a command on it, upload a file, download a directory: ```bash curl -s -X POST "$EDGE/v1/box/ci-42/exec" -H "$AUTH" -H "Content-Type: application/json" -d '{"command":"make test","cwd":"app","timeoutSeconds":300}' | jq '{exitCode, timedOut}' curl -s -X PUT "$EDGE/v1/box/ci-42/upload?path=data/train.bin" -H "$AUTH" --data-binary @train.bin curl -s "$EDGE/v1/box/ci-42/download?path=app/dist" -H "$AUTH" -o dist.tar ``` Prompt the agent and follow it (a box with a run in progress answers `prompt.queued` with no `run`; pass `queue: true`, or interrupt it first): ```bash RUN=$(curl -s -X POST "$EDGE/v1/box/ci-42/prompts" -H "$AUTH" -H "Content-Type: application/json" -d '{"provider":"claude","prompt":"fix the failing tests in app/ and run them again","cwd":"app","auto":true}' | jq -r .run.id) curl -sN "$EDGE/v1/box/ci-42/prompts/$RUN/events" -H "$AUTH" -H "Accept: text/event-stream" ``` Delete the box and wait for the operation: ```bash OP=$(curl -s -X DELETE "$API/boxes/$BOX" -H "$AUTH" | jq -r .operation.id) until [ "$(curl -s "$API/operations/$OP" -H "$AUTH" | jq -r .operation.status)" = completed ]; do sleep 5; done ``` ## Not yet [#not-yet] * **No SDK and no OpenAPI document.** The routes above with `curl` or your language's HTTP client are the whole interface; the CLI's `--json` output is the other machine interface ([CLI: For agents and scripts](https://prized.dev/docs/cli#for-agents-and-scripts)). * **No webhooks.** Poll `GET /api/v1/boxes/{id}` or `GET /api/v1/operations/{id}`; the push channels are the prompt event stream and the [audit export](https://prized.dev/docs/teams#scheduled-export), which carries audit rows only. * **No API keys apart from CLI tokens.** A token is a signed-in machine with your full standing in the workspace; there is no read-only or per-box token. Mint one per script and revoke it when the script is retired. * **No idempotency keys**, see [Conventions](#conventions). --- # Dashboard > Every screen in the signed-in app at prized.dev/dashboard, what each control does, and where a setting lives. The feature pages explain the behaviour; this one says where to click. URL: https://prized.dev/docs/dashboard ## Layout [#layout] One sidebar and one page at a time. The sidebar lists the screens below (Cmd/Ctrl+B collapses it) and ends with the account row: the workspace name and your email, with **Switch workspace** and **Sign out**; on the [free credit](https://prized.dev/docs/billing#free-credit) it counts down what is left. The top bar carries the breadcrumb and **New box**, which rests as a **+** and shows its label when you point at it. A banner crosses the top when money needs attention ([When we tell you](https://prized.dev/docs/billing#when-we-tell-you)), while the workspace is closing (with **Reopen**), or when a signed-in machine runs a CLI too old to [update itself](https://prized.dev/docs/cli#updates). ## Screens [#screens] | Screen | Route | What you can do | Details | | ------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | | Boxes | `/dashboard` | See every box, its state and vitals; copy its `prized ssh` command | [Boxes](https://prized.dev/docs/boxes) | | New box | `/dashboard/boxes/new` | Pick a size, name, template, environment and per-box vars; create | [Create](https://prized.dev/docs/boxes#create) | | Box: Overview | `/dashboard/boxes/` | Pause, resume, resize, move region, snapshot, fork, template, auto-pause, deadline, env vars, phone access, delete; the event log | [Boxes](https://prized.dev/docs/boxes) | | Box: Terminal | `/dashboard/boxes//terminal` | A shell in the box's tmux session | [Sessions](https://prized.dev/docs/sessions) | | Box: Desktop | `/dashboard/boxes//desktop` | A Linux desktop or one Chrome window, streamed | [Desktop](https://prized.dev/docs/desktop) | | Box: Monitor | `/dashboard/boxes//monitor` | Live and historical cpu, memory, network, disk, ports and processes | [Telemetry retention](https://prized.dev/docs/data-retention#telemetry-retention-windows) | | Box: Files | `/dashboard/boxes//files` | Browse, edit, upload, download, rename and delete files | [Files](#files) below | | Box: Secrets | `/dashboard/boxes//secrets` | Mount and unmount the workspace secrets available to this box | [Secrets tab](#secrets-tab) below | | Snapshots | `/dashboard/snapshots` | Take, restore, delete, name templates; undo a box delete | [Snapshots](https://prized.dev/docs/snapshots) | | Environments | `/dashboard/environments` | Create and edit what new boxes inherit; upgrade boxes | [Environments](https://prized.dev/docs/environments) | | Filesystem | `/dashboard/filesystem` | Pick a running box to browse | [Files](#files) below | | Connectors | `/dashboard/connectors` | Mount a provider credential into a box | [Connectors](https://prized.dev/docs/connectors) | | Secrets | `/dashboard/secrets` | Create, rotate, grant and delete brokered secrets; see what each was spent on | [Secrets for agents](https://prized.dev/docs/credentials#secrets-for-agents) | | Workspace | `/dashboard/workspace` | Name, icon, domain, invite link, members and roles, CLI tokens, data and privacy, close, erase | [Workspaces](https://prized.dev/docs/workspaces) | | Activity | `/dashboard/workspace/activity` | What the team is doing now: running boxes, attached sessions, spend, recent actions | [Teams: Team activity](https://prized.dev/docs/teams#team-activity) | | Policies | `/dashboard/workspace/policies` | The rules that bind members: spend and box caps, largest size, auto-pause floor, quiet hours, audit retention | [Teams: Policies](https://prized.dev/docs/teams#policies) | | Audit | `/dashboard/workspace/audit` | Who did what; filter it, download a CSV, set up the scheduled export | [Teams: Audit log](https://prized.dev/docs/teams#audit-log) | | Billing | `/dashboard/billing` | Plan, balance, top-ups, overage, what each box burns, activity, the Stripe portal | [Billing](https://prized.dev/docs/billing) | | Settings | `/dashboard/settings` | Your name, sign-in providers, browser sessions | [Settings](#settings) below | ## Boxes [#boxes] `/dashboard` is a grid of cards, oldest first: name, state, shape (vCPU, RAM and disk), vitals, and the box's `prized ssh ` command (click to copy). Click a card to open the box. ## New box [#new-box] **New box**, the **+** in the top bar, opens `/dashboard/boxes/new`. | Control | What it does | | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Size ladder | A size your balance cannot keep running is blocked with the reason, unless the plan offer is checked ([Starting and resizing boxes](https://prized.dev/docs/billing#starting-and-resizing-boxes)). | | Name | A fruit name is suggested. No rename later. | | Start from | A [template](https://prized.dev/docs/snapshots#templates), once you have one: the box boots that disk at the template's size. | | Environment | Your default is preselected; **none** gives the box no repos, script, vars or files ([Environments](https://prized.dev/docs/environments)). | | Env vars for this box | `NAME=value`, one per line ([Per-box vars](https://prized.dev/docs/environments#per-box-vars)). | | Restricted (safe for third parties) | Closes every credential channel to the box, permanently ([Safe for third parties](https://prized.dev/docs/environments#safe-for-third-parties)); a [contractor](https://prized.dev/docs/teams#roles) sees it on and locked. | | Plan offer | Owners see a checked **Switch to the $X/mo plan** when the box would outgrow the plan; uncheck it to stay. With no plan and an empty balance the button reads **Continue to checkout**. | | Create box | A taken name, the box limit, too little balance, or a [workspace policy](https://prized.dev/docs/teams#policies) is refused with the reason. A closing workspace shows no ladder. | ## The box page [#the-box-page] Six tabs: **Overview**, **Terminal**, **Desktop**, **Monitor**, **Files**, **Secrets**; the header shows the name, state, last heartbeat and the `prized ssh` chip. ### Overview [#overview] A card per topic, each row one setting: its name, a line saying what it does, and its control. **Machine** leads with the readout — the cpu, memory and disk meters, then size, disk, region and created. | Card | Row | Control | | --------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Power | Pause / Resume | One button whose verb follows the state. | | Power | Auto-pause | Off, or 30 minutes to 24 hours idle, with how long the box has been idle; a member's menu stops at the [auto-pause floor](https://prized.dev/docs/teams#policies). | | Power | Pause at | No deadline, 1, 4, 12 or 24 hours, or a custom window (`90m`, `36h`, `3d`), then **Set**; hidden while paused. | | Power | Quiet hours | **Exempt** (owner only), shown while the workspace has [quiet hours](https://prized.dev/docs/teams#quiet-hours). | | Environment | Version | The pinned environment and version, and **Upgrade to vN** when a newer one exists. | | Environment | Variables | **Edit** opens the per-box `NAME=value` list. | | Environment | Restricted | Shown only on a restricted box; it is a fact, not a control. | | Size and region | Resize | A size card, then **Resize** (or **Resize and switch for $X today** when the size outgrows the plan). | | Size and region | Move region | A region, shown with more than one and off while a move is in flight. | | Backups | Snapshot, Save as template, Fork | A name, then the button; for a fork, an optional name, a size whose disk holds the copy, and whether to keep the copy as a snapshot. | | Backups | Automatic snapshots | A schedule from every hour to every week, and how many to keep. | | Access | Phone access | **Set up** shows host, port and username; **Generate password** shows it once, **Regenerate password** replaces it, **Revoke** removes it ([From your phone](https://prized.dev/docs/codex#from-your-phone)). | | Danger zone | Delete box | Type `delete `; the undo is **Recently deleted** on Snapshots ([Deleting a box](https://prized.dev/docs/data-retention#deleting-a-box)). | A refusal shows under the cards in the server's own words. **Events** closes the page: the last 50 state changes with reason, actor and time. What each action does is on [Boxes](https://prized.dev/docs/boxes) and [Snapshots](https://prized.dev/docs/snapshots). ### Terminal [#terminal] The box's tmux session `main`, the same one `prized ssh` opens; closing the tab loses nothing, and opening it wakes a paused box ([Sessions](https://prized.dev/docs/sessions)). Select to copy (Cmd+C on a Mac, Ctrl+Insert or the right-click menu elsewhere); Ctrl+C goes to the shell. ### Desktop [#desktop] Pick **Desktop** or **Browser**, then **Open desktop** or **Open browser** ([Desktop](https://prized.dev/docs/desktop)). ### Monitor [#monitor] Ranges: **live**, **1h**, **6h**, **24h**, **7d**, **30d**; the stored ranges show averages and a max band, paused spans shaded, lifecycle events as ticks. Panels: cpu, memory, network, disk, ports · tmux, processes. Click a chart to pin a moment and the ports and processes panels show the nearest stored snapshot. The monitor never wakes a paused box; retention is on [Data retention](https://prized.dev/docs/data-retention#telemetry-retention-windows). ### Files [#files] A file browser as the box's login user; it needs a running box and never wakes one. Each row has **download**, **rename** and **delete**; a text file within the inline cap opens in an editor, a bigger one shows its head read-only, a binary file offers download. Caps are on [Limits](https://prized.dev/docs/limits#commands-and-files). ### Secrets tab [#secrets-tab] Each mount with **Unmount**, then a secret picker, an env name and **Mount**. The picker lists only the secrets [available to this box](https://prized.dev/docs/teams#scoped-secrets); a mount whose grant was revoked shows **no longer granted** until you unmount it. Mounts need a running box, and a restricted box refuses them. ## Snapshots [#snapshots] **New snapshot** picks a box, a name and a description; the table lists every snapshot with kind, box, size, status and age. Per row: **Restore** (an optional name for the new box), **Make template** or **Rename**, and **Delete** then **Confirm delete**. The **templates** list offers **new box from this** and **Drop name**; **recently deleted** holds the undo copies of deleted boxes with **Restore** and **Delete now** ([Snapshots](https://prized.dev/docs/snapshots)). ## Environments [#environments] One card per environment with **Make default**, **Edit**, and **Delete** (type `delete `). **New environment** asks for a name, description, base size, env vars, a setup script, and **Make it the default for new boxes**. **Edit** opens sections that save on their own: General, the credentials a box may receive (**Safe for third parties**, or **Secret mounts**, **Device keys**, **Phone access** one at a time), env vars, secret files (shown as path, mode and size only), repos, and Versions with **Upgrade N boxes to vX** ([Environments](https://prized.dev/docs/environments)). ## Connectors [#connectors] One card per provider; **Connect** asks for a running box, the credential, the env name and, for ClickHouse, the HTTPS endpoint ([Connectors](https://prized.dev/docs/connectors)). ## Secrets [#secrets] **Add secret** asks for a name, the value, the allowed hosts, a description, and **Available to** (all boxes, selected boxes, or selected members' boxes). Per row: **Rotate** (the placeholder stays, so mounted boxes keep working), **Delete** then **Confirm delete**, and **Details** (grants with **Revoke**, **grant a box**, **grant a member**, and the usage table). Values are write-only once saved ([Secrets for agents](https://prized.dev/docs/credentials#secrets-for-agents), [Teams: Scoped secrets](https://prized.dev/docs/teams#scoped-secrets)). ## Workspace [#workspace] Owner-only controls say so to members; while the workspace is closing every control is read-only. | Card | Rows | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Workspace | **Icon** (**Upload**, **Remove**, **Use domain favicon**), **Name** and **Save**, **Domain** (**Claimed**, then **Auto-join** and **Restrict membership**), **Invite link** (**Copy**, **New link**) ([Create one](https://prized.dev/docs/workspaces#create-one), [Invite people](https://prized.dev/docs/workspaces#invite-people)) | | Members | A role select (**member** or **contractor**) and **Remove** for the owner; **Leave** on your own row ([Remove someone](https://prized.dev/docs/workspaces#remove-someone)) | | CLI tokens | Every signed-in machine (a contractor's own) with last use, CLI version and whether it registered an SSH key; **Revoke** per row, **Revoke all** ([CLI tokens](https://prized.dev/docs/workspaces#cli-tokens), [Credentials Prized issues](https://prized.dev/docs/security#credentials-prized-issues)) | | Data and privacy | **Zero data retention**: **Turn on** (typed confirmation) or **Turn off** ([Zero data retention](https://prized.dev/docs/data-retention#zero-data-retention)) | | Danger zone | **Close workspace** and **Erase now**, each confirmed by typing the workspace's name ([Closing a workspace](https://prized.dev/docs/data-retention#closing-a-workspace)) | ## Activity [#activity] **Now** (boxes running, sessions attached, spend today and this cycle), **Boxes** (each live box with owner, state, idle time, sessions, last prompt and exec, spend), **Members** (not shown to contractors), and **Recent** (the newest 50 audit rows); terms are on [Teams: Team activity](https://prized.dev/docs/teams#team-activity). ## Policies [#policies] The owner edits, members read. Each rule saves on its own, and its value is its switch: clear the field (or pick **Any size**) and save to turn a rule off. | Card | Rows | | ------------- | --------------------------------------------------------------------------------------------- | | Member limits | **Spend per member**, **Boxes per member**, **Largest size per member**, **Auto-pause floor** | | Quiet hours | **On**, then the nightly window, the weekday and all-day checkboxes, and a timezone | | Audit log | **Retention**, in days | The rules themselves are on [Teams: Policies](https://prized.dev/docs/teams#policies). ## Audit [#audit] | Control | What it does | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Filters | Member, box, kind (grouped by area), and range: last 24 hours, 7 days (the default), 30 days, 90 days, or everything kept | | The table | When, member with the credential kind beneath, action, box, detail; client-reported rows carry a muted **reported** label; **Load more** pages 100 at a time | | Download CSV | Every row of the current filters and range, up to 50,000 | | Export | Owner only: a URL and a write-only secret, with a status line showing the last delivery and error | Kinds, origins and the export contract are on [Teams: Audit log](https://prized.dev/docs/teams#audit-log). ## Billing [#billing] Everyone reads this page; only the owner sees the controls. **Plan**: the current plan and renewal date, a nudge when your boxes fit a smaller plan or outgrow this one, and the plan cards (**Choose**, **Upgrade now** or **Switch next cycle**, **Cancel plan**, **Resume this plan**). **Balance**: the balance and how long it lasts, **Add credit once** for a [top-up](https://prized.dev/docs/billing#topping-up), and the [Overage](https://prized.dev/docs/billing#overage) checkbox. Then **What you're burning** (every live box and its hourly rate; a contractor's own), **Recent activity** (the last 50 ledger lines), and **Manage in Stripe** for the card, invoices and receipts ([Billing](https://prized.dev/docs/billing#choosing-and-changing-the-plan)). ## Settings [#settings] Your account, not the workspace: **Profile** (**Name** and **Save**, plus your email and photo, both from your sign-in provider), **Sign-in methods** (Google and GitHub: **Connect** links another provider by its verified email, **Disconnect** unlinks one, never the last), and **Sessions** (**Sign out** per browser, or **Sign out other sessions**). CLI tokens live under [Workspace](#workspace); the sign-in model is on [Identity and access](https://prized.dev/docs/security#identity-and-access). ## Where a setting lives [#where-a-setting-lives] | Setting | Where | | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | Box name | New box, at create; no rename later | | Auto-pause, pause deadline | Box page, Overview, **Auto-pause** and **Pause at** | | Size, region | Box page, Overview, **Size and region** | | Per-box env vars, environment version | Box page, Overview, **Environment** (**Variables**, **Version**) | | Snapshot schedule | Box page, Overview, **Automatic snapshots** | | Phone SSH password | Box page, Overview, **Phone access** | | Secrets mounted on a box | Box page, **Secrets** tab | | Quiet hours exemption for a box | Box page, Overview, **Quiet hours** (owner) | | Who a secret is available to, what it was spent on | Secrets, **Details** | | Default environment, safe for third parties | Environments | | Workspace name, icon, domain, invite link, members | Workspace | | Member roles | Workspace, **Members** (owner) | | Spend and box caps, largest size, auto-pause floor, quiet hours, audit retention | Policies (owner) | | Audit export | Audit, **Export** (owner) | | CLI tokens and device SSH keys | Workspace, **CLI tokens** | | Zero data retention, close, erase | Workspace, **Data and privacy** and **Danger zone** | | Plan, top-ups, overage, card, invoices | Billing (owner) | | Sign-in providers, browser sessions | Settings | | CLI usage telemetry | Not in the dashboard: [CLI: usage telemetry](https://prized.dev/docs/cli#usage-telemetry) | | Dashboard analytics | Your browser's Do Not Track setting: [Privacy](https://prized.dev/docs/privacy#turning-it-off) | --- # Workspaces > One account for the whole team; boxes, balance, and secrets shared by everyone in it. URL: https://prized.dev/docs/workspaces ## What a workspace is [#what-a-workspace-is] A workspace is the account your boxes, balance, plan, and secrets belong to. Owners and members see the same boxes and spend the same balance; a contractor sees only the boxes they own. Every sign-in belongs to at least one workspace; a solo account is a workspace of one. | Role | In short | | ---------- | ------------------------------------------------------------------------------------------------------------------------------- | | Owner | Everything, including billing, the domain, the invite link, roles, policies, and removing members. No policy binds the owner. | | Member | Every box, secret, snapshot and environment; reads the audit log and policies; cannot change billing. Policies bind members. | | Contractor | Only the boxes they own and the CLI tokens they created; no audit log, member list, policies, workspace secrets, or connectors. | The owner switches anyone else between member and contractor from **Dashboard → Workspace → Members**; ownership never changes hands. Roles in full, policies, and everything else a team gets are on [Teams](https://prized.dev/docs/teams). ## Create one [#create-one] Your first sign-in creates a workspace, named after your company domain or after you; rename it from **Dashboard → Workspace**. Accounts from before workspaces existed became a workspace each; nothing was merged. With a company address, the owner can claim the email domain on the same page, with two switches: * **Any user with an @yourdomain email can join without approval.** New sign-ins on that domain land in your workspace instead of getting an empty one. Only a provider-verified address counts; a personal mail provider (Gmail, Outlook, and the like) can never be claimed. * **Restrict membership to only users with an @yourdomain email.** The invite link admits only addresses on the domain. A domain belongs to one workspace. If a colleague claimed it first with joining open, your first sign-in lands you in their workspace; `prized login` then prints `✓ signed in as you@yourdomain.com · Acme (shared)`, so a shared workspace never looks like being signed in as someone else. ## Invite people [#invite-people] **Dashboard → Workspace** shows the invite link. Anyone who opens it and signs in becomes a member; the owner can then make them a [contractor](https://prized.dev/docs/teams#roles). **New link** replaces it, and the old one stops working immediately. Teammates on a claimed domain with joining open need no link. ## Switch between workspaces [#switch-between-workspaces] Someone in more than one workspace picks the current one from the account menu at the bottom of the sidebar; everything there, CLI tokens included, is scoped to that workspace. ## Remove someone [#remove-someone] **Dashboard → Workspace → Members → Remove** (owner only). Their dashboard access ends at once, and every credential they created for this workspace is revoked with them: CLI tokens (so `prized` on their machine stops working), unused install links, and any [phone access password](https://prized.dev/docs/codex#from-your-phone) they generated. The invite link is replaced too, so the copy they had cannot let them back in. Boxes, secrets, and the balance stay. A removed member is not let back in by the domain, even with joining open; only a link shared after the removal re-admits them. A member can leave from their own row (this does not replace the link); the owner cannot leave. ## CLI tokens [#cli-tokens] **Dashboard → Workspace → CLI tokens** lists every machine signed in to this workspace: its name, when it was last used, the CLI version it last reported, and whether it registered an SSH key for [Claude Desktop remote](https://prized.dev/docs/claude-desktop). The token itself is shown once at sign-in and only its hash is stored. **Revoke** retires one machine on its next request: `prized` there stops working until you run `prized login` again, its tunnel closes, and its SSH key leaves your boxes within seconds. **Revoke all** does the same for every token at once, including yours: the right move for a stolen laptop or a token pasted somewhere public. Owners and members see every machine; a contractor sees only the ones signed in under their own account. The owner can revoke any token, a member or contractor only the ones under their own sign-in, and a token from before workspaces existed only the owner. Revoking a token touches no boxes, secrets, or balance and removes nobody; to end someone's access entirely, remove them. ## Close or erase the workspace [#close-or-erase-the-workspace] **Dashboard → Workspace → Close workspace** (owner only) pauses every box, cancels the plan immediately, revokes every token and key, removes other members, and deletes everything 30 days later unless you reopen from the banner. **Erase now** skips the window. Both, and what is retained afterwards, are on [Data retention](https://prized.dev/docs/data-retention#closing-a-workspace). --- # Teams > What a shared workspace gets on top of one account: roles, policies that bind members, quiet hours, an audit log with a scheduled export, secrets scoped to boxes or members, and a live activity page. URL: https://prized.dev/docs/teams A [workspace](https://prized.dev/docs/workspaces) is one account for a team. This page is what the team gets on top of it. Every rule here is off until the owner turns it on, and nothing here changes a workspace of one. ## Roles [#roles] | Role | Can | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Owner | Everything: billing, the domain and invite link, roles, policies, quiet-hours exemptions, the audit export, removing members. No policy binds the owner; ownership never moves. | | Member | Every box, secret, snapshot and environment; reads the audit log, the activity page and the policies. Policies bind members. | | Contractor | A member fenced to the boxes they own. Policies bind contractors too. | The owner changes a role from **Dashboard → Workspace → Members**; each change is a `member.role` audit row. Demoting a member to contractor revokes the phone passwords and browser tickets they minted for boxes they do not own and closes their tunnels to those boxes. A contractor sees and reaches only the boxes they created; another member's box reads as not found everywhere, and so do its snapshots: a contractor launches from, restores, forks and names templates of their own boxes' snapshots only, and a template name held by a snapshot they cannot see is not theirs to move (`409 template_taken`). They can list environments and upgrade their own boxes, but not create, edit or delete one (`403 not_allowed`). Their new boxes are always **Restricted (safe for third parties)**, so none of your credentials reach them ([Safe for third parties](https://prized.dev/docs/environments#safe-for-third-parties)); an API create with `"restricted": false` is `403 policy_denied`, rule `contractor_restricted`. The audit log, the members list, the policies and workspace secrets answer `403 not_allowed`, and a secret reaches their box only when [granted](#scoped-secrets) to it or to them. A box belongs to the member who created it; boxes Prized creates for you belong to the owner (`ownerUserId` on the box object). ## Policies [#policies] **Dashboard → Workspace → Policies**: the owner edits, members read, contractors see `Your role does not include the workspace policies.` Every rule is off by default and binds members and contractors, never the owner, except quiet hours, which pause every box. | Rule | Key | Range | What it does | | ----------------------- | -------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------- | | Spend per member | `memberSpendCapUsd` | $1 to $1,000,000 a cycle, whole dollars | Caps what one member's boxes draw in a billing cycle. Checked on create, wake and resize. | | Boxes per member | `memberBoxLimit` | 1 to 100 | Caps a member's live boxes (running or paused). Checked on create, fork and wake. | | Largest size per member | `memberMaxTier` | Any size | The biggest size a member may create or resize to. | | Auto-pause floor | `autoPauseFloorMin` | 30 minutes to 7 days | Members' boxes must auto-pause within this long of going idle; longer or off is refused. | | Quiet hours | `quietHours` | One window a day, per weekday | Pauses every running box inside the window; see [Quiet hours](#quiet-hours). | | Audit log retention | `auditRetentionDays` | 7 to 365 days, default 90 | How long [audit rows](#audit-log) are kept. | A refused action answers `403 policy_denied` with `rule` and `message`; the CLI prints the message (exit 5), the dashboard shows it beside the control. The spend cycle is the month ending at your plan's period end, or the UTC calendar month without a plan; spend is attributed to the box's owner while the workspace pays one bill. The box limit refuses a wake only when the member is already over it. Under a floor, a new box with no auto-pause setting takes the floor, a box from before the floor is paused as if it had it, and a member's **Auto-pause** menu stops there. `GET /api/v1/workspace/policies` reads the rules; changing them is dashboard-only and writes a `policy.update` row ([API reference](https://prized.dev/docs/api#policies)). ## Quiet hours [#quiet-hours] A window each day in which every box in the workspace is paused, the owner's included: `start` and `end` as `HH:MM`, the weekdays it applies to, the weekdays that are quiet all day, and one IANA timezone. A window that ends before it starts crosses midnight and belongs to the day it started on. * **A box woken inside the window** stays up 15 minutes, then is paused again. * **Exempting a box.** The owner marks it **Exempt** under **Quiet hours** on its box page (`PATCH /api/v1/boxes/{id}` with `{"quietHoursExempt": true}`; anyone else is `403 not_owner`). * **In the log.** Each pause is a `box.pause` row with reason `quiet_hours`. A quiet-hours pause is exactly a [pause](https://prized.dev/docs/boxes#pause-and-resume): sleep rate from that moment, disk kept, processes per the size table. ## Audit log [#audit-log] **Dashboard → Workspace → Audit**: who did what, on which box, when. Owners and members read it; contractors see `Your role does not include the audit log.` The page filters by member, box, kind and range, pages 100 rows at a time, downloads a CSV of up to 50,000 rows, and, for owners, holds the [scheduled export](#scheduled-export) ([Dashboard: Audit](https://prized.dev/docs/dashboard#audit)). ### Two origins [#two-origins] **`server`: the control plane saw it.** Box lifecycle and settings, CLI tokens, snapshots, environments, secrets, membership and roles, policies, close and reopen, the export settings, every user SSH certificate minted, and every browser terminal opened. **`client`, shown as reported: the CLI or the box told us.** The control plane never sees a command run over ssh, a file copied, or a prompt started, so `prized exec`, `prized cp`, `prized prompt`, `prized desktop` and the box's edge API post a row afterwards. A reported row is a claim by a credential in your workspace, not an observation: an offline or older CLI posts none, and nothing checks it against what ran. Every row names the member (`actorUserId`) and the credential that acted (`actorTokenId`); browser terminal and phone sessions are attributed to the member who opened them. ### What is recorded [#what-is-recorded] Server-observed kinds. Names and hosts, never values, contents, keys or tokens: | Kind | Detail | | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `box.create` | `tier`, `region`, `hostname`, `restricted`, `from` (a snapshot or template) | | `box.wake`, `box.delete` | | | `box.pause` | `reason`: `quiet_hours`, `auto_pause`, `pause_deadline`, or none for a pause you asked for | | `box.resize`, `box.move_region` | `from`, `to` | | `box.settings` | The keys changed | | `box.env` | `count` | | `box.fork` | `from`, `fromHostname`, `to`, `toHostname`, `tier`, `snapshotId`, `keepSnapshot` | | `box.connect` | One per SSH certificate minted, so every connection starts with one: `keyId`, and `scope` (`terminal`, `monitor`, `mobile-ssh`) for a browser or phone session | | `box.session` | A browser terminal opened: `scope`, `ticketId` | | `snapshot.create` | `snapshotId`, `name`, `kind`, `templateName` | | `snapshot.delete`, `snapshot.template` | | | `environment.create`, `environment.update`, `environment.delete` | Update carries `change` (the kind of edit) and `key` (the var, file, repo or toggle); never a value or a file body | | `secret.create`, `secret.update`, `secret.delete`, `secret.mount`, `secret.unmount`, `secret.grant`, `secret.revoke` | Names and hosts | | `member.add` | `via`: `first_contact`, `domain`, `invite` | | `member.remove`, `member.leave`, `member.role`, `invite.rotate`, `invite.revoke` | | | `token.create` | `name`, `tokenId` | | `token.revoke` | `tokenId`, or `all: true` with `count` | | `policy.update` | `keys` | | `audit.export` | `action` (`set` or `clear`), `host` | | `workspace.settings`, `workspace.retention`, `workspace.reopen` | | | `workspace.close` | `purgeAfter`, `boxesSuspended`, `membersRemoved`, `cliTokensRevoked` | Client-reported kinds. Never stdout, stderr, file contents, the prompt beyond its first line, or a VNC password: | Kind | Detail | Reported by | | ------------------------------ | ---------------------------------------------------------------- | --------------------------------------------------------------------------------- | | `exec` | `command` (up to 512 characters), `cwd`, `detached`, `processId` | `prized exec` when it runs something; `POST /exec` | | `file.upload`, `file.download` | `path`, `bytes`, `files`, `inline`, `directory` | `prized cp` after a copy that moved something; the edge's file routes | | `prompt` | `firstLine`, `provider`, `runId`, `queued` | `prized prompt`; `POST /prompts` | | `desktop.start` | `mode`, `profile` | `prized desktop` and `prized browser` (not `--stop`); the dashboard's Desktop tab | ### From the CLI and the API [#from-the-cli-and-the-api] ```bash prized team log # the last 24 hours prized team log --since 7d --kind exec,prompt --member ana@acme.com ``` `prized team log` prints TIME, MEMBER, KIND, BOX, DETAIL and ORIGIN ([CLI: Team](https://prized.dev/docs/cli#team)). `GET /api/v1/audit` takes the same filters and pages by `before` and `limit`; `format=csv` streams up to 50,000 rows with the columns `id,at,kind,origin,actor_kind,actor_user_id,actor_token_id,box_id,detail` ([API reference](https://prized.dev/docs/api#audit-log)). ### Scheduled export [#scheduled-export] Owners set a destination in the **Export** section of the Audit page: a URL and a write-only secret, with a status line showing the last delivery and the last error. * **The destination.** An `https` URL of up to 2,048 characters with no credentials or fragment, on a public host, and a secret of 16 to 256 characters, encrypted at rest and never shown again. Private, loopback and link-local addresses are refused when you save the destination and again before every delivery. The copy is yours, at your endpoint; there is no hosted destination. * **Each delivery.** Once an hour, one `POST` per batch: the JSON body `{"v": 1, "workspaceId": "...", "events": [...]}`, oldest first, up to 500 events per batch and 20 batches per pass; headers `Content-Type: application/json`, `X-Prized-Signature: sha256=`, `X-Prized-Event-Id-From` and `X-Prized-Event-Id-To`. Ten seconds per request; redirects are never followed. * **Acknowledging.** Any `2xx` advances the cursor; anything else lands on the status line and the same batch is sent again next hour: at-least-once, in order, so dedupe on `id`. Delivery starts with the rows newer than the moment you saved the destination. ```bash openssl dgst -sha256 -hmac "$SECRET" body.json # must equal the X-Prized-Signature hex ``` Setting or clearing the destination is itself an `audit.export` row. ### Retention [#retention] Rows are kept 90 days by default; the owner sets 7 to 365 days under Policies. A row outlives the token, the member and the box it names, and goes when the workspace is erased ([Data retention](https://prized.dev/docs/data-retention#records-retained-after-deletion)). [Zero data retention](https://prized.dev/docs/data-retention#zero-data-retention) does not shorten the window; with it on, the `exec`, `file.upload`, `file.download` and `prompt` rows keep no detail (the page shows `no detail kept (zero data retention)`). ## Scoped secrets [#scoped-secrets] A [secret](https://prized.dev/docs/credentials#secrets-for-agents) is workspace-wide until you grant it to someone. * **No grants:** every box may mount it, except a contractor's. * **The first grant narrows it** to the boxes named and the boxes owned by the members named; removing the last grant makes it workspace-wide again. A contractor's box gets nothing it was not granted, to the box or to its owner. A secret created with an **Available to** choice (or with `grants` on the API) is born with those grants, and a [connector](https://prized.dev/docs/connectors)'s secret is born granted to the box you connected it to. * **Revoking is quick:** the box loses the value within 15 seconds, and the mount shows **no longer granted** until you unmount it. Grant from the dashboard (**Available to** on the new-secret form, **Details** on a row), the CLI (`prized secrets grant NAME --box BOX` or `--member EMAIL`; [CLI: Secrets](https://prized.dev/docs/cli#secrets)), or the API (`POST /api/v1/secrets/{id}/grants`; a mount the box may not have is `403 secret_not_granted`; [API reference](https://prized.dev/docs/api#secrets)). ### Usage [#usage] One use is one HTTPS request to an allowlisted host in which the broker swapped the placeholder for the value, counted whatever the upstream answered; a refused plain-HTTP request, a host outside the allowlist, or a request without the placeholder is not counted. Recorded: the placeholder, the host, the count, and the first and last time; never a path, header, body or value. Counts are a floor, because the box reports them in batches and a restart can lose the last one. Read them under **Details** on the Secrets page, with `prized secrets usage NAME`, or from `GET /api/v1/secrets/{id}/usage`. ## Team activity [#team-activity] **Dashboard → Workspace → Activity** shows what the team is doing right now: running boxes with their owner, idle time, attached sessions, last prompt and exec, and spend; members with their boxes, sessions, last active time and spend; and the newest 50 audit rows ([Dashboard: Activity](https://prized.dev/docs/dashboard#activity)). Contractors see their own boxes only. * **Attached session:** a device connected to a box (the CLI, a browser terminal, or a phone), counted while its tunnel is open and for at most 12 hours. It says a device is attached, never what is typed or shown. * **Last prompt, last exec:** from reported rows, so an older CLI leaves them blank. * **Today:** since UTC midnight. **Cycle:** the plan's period, or the UTC calendar month without a plan. * **Spend:** metered compute attributed to the box that drew it, in whole UTC-hour buckets. Attribution, not billing: the workspace pays one bill. `prized team status` prints the same as two tables and a one-line summary; `--json` prints the object `GET /api/v1/team/status` answers ([CLI: Team](https://prized.dev/docs/cli#team), [API reference](https://prized.dev/docs/api#team)). ## What is not covered [#what-is-not-covered] * **What happens inside an ssh session.** Only the connect is recorded (`box.connect`), never the commands typed in it; reported rows are the client's claim, not a check. * **Tokens that belong to nobody.** A CLI token from before workspaces existed is bound by no per-member policy and its rows name no member; only the owner can retire it ([CLI tokens](https://prized.dev/docs/workspaces#cli-tokens)). --- # Billing > Pick a monthly plan from $10 to $1,000, or one sized to your boxes past that. It lands as credit every cycle, boxes bill by the hour against it, and unused credit rolls over up to two months of your plan. URL: https://prized.dev/docs/billing ## How it works [#how-it-works] A plan is how much money you put in each month, at any rung from $10 to $1,000. Every cycle at least that much credit lands in your balance, and every box you own meters against it, by the second, quoted by the hour: | Box state | Rate | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Awake | Its monthly price spread over a 730 hour month. A Small is $100 a month, so $0.137 an hour. | | Asleep | $0.0001 per GB hour of the disk the box actually has; a Small asleep for a month is about $22. A box that kept a bigger disk pays the same per-GB rate for the extra disk, asleep or awake. | * **Pausing keeps the money.** Pause from the dashboard, or switch on [auto-pause](https://prized.dev/docs/boxes#auto-pause); `prized ssh` wakes the box when you come back. * **Credit rolls over,** up to two months of your plan; anything above that cap lapses when a cycle's credit lands, as its own line in your activity. Top-up credit is protected from the trim for two months. * **Each rung runs one size around the clock:** $10 a Nano, $100 a Small, $1,000 an Extra Large. The credit spends on any mix. * **Long cycles land more credit.** Rates assume 730 hours, but a calendar cycle runs up to 744, so a longer cycle lands proportionally more credit at no extra charge. * **A fleet bigger than the biggest rung** gets a plan sized exactly to it: an Extra Large plus a Small is a $1,100/mo plan, offered wherever your boxes outgrow $1,000. ## Free credit [#free-credit] New workspaces get $30 of free credit, once, when the owner walks through onboarding; it spends exactly like paid credit, and the tour's box step starts a box on it, no card needed. It does not refill: the sidebar shows what is left, at zero boxes pause as in [Running out](#running-out), and the dashboard then opens on the plan picker until you choose a plan, whose first cycle lands as credit right away. While the workspace runs on the free credit alone: * **It starts Nano, Micro, Extra Small, or Small.** Medium and larger come with a plan, and you can take that route on the spot: pick the bigger size on the tour or the create form, start its plan, and that size launches as soon as the first cycle's credit lands. Asking the API for one on the credit alone answers `409 tier_unavailable` and says so. The tour's size list and the create form's cards show how long the credit runs each size around the clock, from the rates below: about three months on a Nano, about five weeks on a Micro, about nine days on a Small (or 19 nights at eight hours awake a day). A create that names no size gets a Small. * **Boxes pause themselves after an hour idle**, unless the create sets an [auto-pause](https://prized.dev/docs/boxes#auto-pause) window or turns it off (`prized box new --auto-pause off`). A paused box keeps its disk at the sleep rate and wakes on `prized ssh` or from the dashboard, so credit nobody is using is not spent. * **It runs one box at a time.** A second create — from the dashboard, `prized box new`, a restore, or a fork — answers `409 box_limit` and says so. Delete the box you have and the slot frees up; pick a plan and the cap goes to the usual 100. All three rules lift the moment a plan is live; a box already started on the credit keeps its settings. ## Sizes and rates [#sizes-and-rates] The live shape and price table is at https://prized.dev/pricing. The last column is both the most a box can cost in a month and the plan that runs it around the clock. A comparison against renting the same machine elsewhere is on the [pricing page](https://prized.dev/pricing). ## Choosing and changing the plan [#choosing-and-changing-the-plan] The workspace owner picks a plan on the Billing page, or on the tour's payment step once the [free credit](#free-credit) is spent. Payment is Stripe Checkout, cards only, and the first cycle's credit lands before you are back. Choosing a plan when you have no box starts one, at the size that rung runs around the clock. | Change | Effect | | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Upgrade | Starts a fresh cycle today: one full month of the new plan, minus the unused share of your old one, bills immediately and lands as credit. Your renewal date resets. | | Downgrade | Changes what the next cycle bills, on your existing renewal date. This cycle's credit stays yours. | | Cancel | Stops the plan at the period's end; credit already landed keeps working until spent. Pick a plan again before then and it keeps renewing. | The page leads with the balance and how long it lasts at the current burn, and says so when your live boxes all fit a smaller plan or outgrow this one; past $1,000/mo the picker grows one more card, sized to your boxes. The balance and plan belong to the [workspace](https://prized.dev/docs/workspaces): everyone in it spends the same balance and can see this page, and only the owner can change the plan, buy top-ups, switch overage, or open the Stripe portal. ## Topping up [#topping-up] The owner can buy a one-time top-up from the Billing page, $10 to $250 by card, in your balance right away and as often as needed; it never changes what the plan bills. With [overage](#overage) on there is nothing to do: usage past your credit bills with the next invoice. ## When we tell you [#when-we-tell-you] * **Dashboard banner** once less than two days of credit remain at the current rate, and different ones in overage, after boxes were suspended, while a payment is failing, and before the account is funded. * **`prized status`** prints a credit line when the balance is running low. * **Email** at a quarter and a tenth of what the last cycle left you, once when you cross into overage, once if boxes are paused, and once if a payment fails. If you never started a plan, at most two reminders. ## Overage [#overage] **Off by default:** out of credit means boxes pause, and nothing bills past what you put in. The owner can switch it on from the Billing page, and running out then stops nothing: * The meter keeps billing past zero at the same hourly rates; the amount below zero is added to your next invoice as its own line, no markup. Prepaid credit always spends first, and adding credit (a top-up, an upgrade, the next cycle) pays the overage down first. * You see it: an overage banner, a negative balance on the Billing page, and one email when you cross. * Turn it off any time and the balance is the hard stop again; overage already run up still bills with your next invoice, or a final one if the plan ends first. * **A failed or disputed payment switches overage off** until a payment collects. **A cancelled plan** with a negative balance bills the outstanding overage once, on a final invoice; a tail under Stripe's 50 cent minimum is written off. * **Only metered compute bills as overage.** A balance made negative by a refund, a reversed payment, or a correction is never charged as overage. * **A reversed payment can put overage back.** Disputing the payment that settled your overage puts the balance, and the overage owed on it, back where they were. ## Running out [#running-out] With overage off, without a plan, or while a payment is failing, the balance is the hard stop: at zero, boxes suspend. Disks are kept, with your work, installed tools, and git state; the next cycle, an upgrade, or a top-up lets you start them again from the dashboard, and we never start them for you. Suspended boxes keep metering their disk at the sleep rate, and usage the balance cannot cover is written off, never turned into a debt. > Nothing is deleted automatically. If a balance stays empty for a long time, we contact you before any disk is released. ## Starting and resizing boxes [#starting-and-resizing-boxes] A create, a wake, or a resize needs enough balance to run the resulting fleet for four hours, counting every box at the disk it actually has. It is a check, not a charge; a refusal says what the fleet would burn and what the balance would need. A resize meters at the new rate from that moment, and a box in the middle of a change cannot be resized until it settles. A region move keeps the hourly rate but copies the whole disk, so it needs a funded balance and is capped per box ([Regions](https://prized.dev/docs/boxes#regions)). Every other cap is on [Limits](https://prized.dev/docs/limits). When a new or resized box would cost more around the clock than your plan puts in each month, the owner sees a plan offer in the create and resize flows: **Resize** offers the covering plan as one extra click, quoting the charge due today (decline and it just resizes); **Create** pre-selects it and the button reads "Create and switch to $X/mo" (uncheck to create on your current plan); past the biggest rung the offer is the exact monthly cost of your boxes; with no plan yet the same spot offers your first one. Nothing charges without the page saying so first, and the prepaid balance is the only thing that gates a box. ## Failed payments [#failed-payments] A failed cycle payment suspends nothing by itself: no credit landed, so boxes keep running on what remains, and overage switches off until a payment collects. Stripe retries on its own schedule, the dashboard shows a banner, and we email you once; the owner can update the card in the Stripe portal, linked from the Billing page. A refund, or a payment your card issuer pulls back (a dispute), takes the credit it bought out of the balance; a dispute also pauses the plan's billing and switches overage off while it stands. Won, the credit is restored and billing resumes, and overage returns with your next successful payment; lost, the plan is cancelled and overage stays off, so pick a plan again to start a new one. ## Receipts [#receipts] Every cycle produces a Stripe invoice you can download. The Billing page lists recent activity, each cycle's credit and one usage line per hour; card details, invoices, and receipts live in the Stripe portal, linked from the same page for the owner. ## Custom plans [#custom-plans] A fleet bigger than the largest rung needs no special deal: the plan sizes itself to your boxes ([above](#choosing-and-changing-the-plan)). For a startup deal or a negotiated amount, email the support address shown on https://prized.dev/docs/troubleshooting and we send you a link; the owner opens it to subscribe at that amount in one click, or to move an existing plan, and from then on it behaves like any other plan. Links expire after 30 days. --- # Limits > Every hard cap a box or a workspace can hit, in one place: counts, sizes, windows, and rates, with what the refusal looks like. URL: https://prized.dev/docs/limits Each table links the page that explains the behaviour around it. Money is on [Billing](https://prized.dev/docs/billing): the plan range, top-ups, the rollover cap, and the free credit. ## Boxes [#boxes] | Limit | Value | | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Boxes per workspace | 100, counting every box that is not deleted (paused and deep sleep included); 1 on the [free credit](https://prized.dev/docs/billing#free-credit). With an empty balance and no overage nothing starts or wakes ([Running out](https://prized.dev/docs/billing#running-out)) | | Balance to create, wake, or resize | Enough to run the whole resulting fleet for 4 hours ([Starting and resizing boxes](https://prized.dev/docs/billing#starting-and-resizing-boxes)) | | Box name | 1 to 32 characters, lowercase letters, digits, and hyphens, starting and ending with a letter or digit; unique in the workspace | | Sizes | 7, Nano to Extra Large ([Machines](https://prized.dev/docs/machines#sizes)); on the [free credit](https://prized.dev/docs/billing#free-credit), Nano to Small. No resize while provisioning, resizing, or moving; the disk never shrinks ([Resize](https://prized.dev/docs/boxes#resize)) | | Auto-pause window | 30 minutes to 7 days idle; the dashboard offers 30 minutes to 24 hours, the CLI and API the whole range ([Auto-pause](https://prized.dev/docs/boxes#auto-pause)). An [auto-pause floor](https://prized.dev/docs/teams#policies) caps members' boxes | | Pause deadline | 5 minutes to 30 days ahead, on a running box only ([Pause deadline](https://prized.dev/docs/boxes#pause-deadline)) | | Deep sleep | After 14 days paused the disk is parked as a snapshot; waking takes a few minutes ([States](https://prized.dev/docs/boxes#states)) | | Region moves | 4 per box in any rolling 24 hours, one at a time, running boxes only; 2 regions ([Regions](https://prized.dev/docs/boxes#regions)) | | Undo after delete | 7 days, as a final snapshot under Recently deleted ([Data retention](https://prized.dev/docs/data-retention#deleting-a-box)) | | Capacity | Not a per-workspace number. When no machine of that size is available, a new box waits in `provisioning` and is retried, or ends in `provision_failed` with the reason in its events; delete it and create again, or pick another size | ## Snapshots [#snapshots] | Limit | Value | | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Manual and template snapshots per workspace | 20. A snapshot still being deleted counts until its storage is released; fork copies, automatic snapshots, and Recently deleted copies never count ([Snapshots](https://prized.dev/docs/snapshots)) | | Snapshot or template name | 1 to 64 characters of letters, digits, `.`, `_`, and `-`, starting and ending with a letter or digit | | Automatic snapshots per box | `keep` of 1 to 10 (default 5), every 1 to 168 whole hours; each expires after keep x interval | | Snapshots in flight per box | 1 | | Fork disk copy | 30 minutes, then the new box is marked failed | | Restore target | Any size whose disk holds the snapshot; a smaller disk is refused with the numbers | | Region | A snapshot restores into the region its box was in | | Balance | Any snapshot needs a funded balance or an active plan | ## Environments [#environments] | Limit | Value | | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | Environment name | 1 to 64 characters of letters, digits, `.`, `_`, and `-`, starting and ending with a letter or digit ([Environments](https://prized.dev/docs/environments)) | | Env vars per environment | 64, each value a single line of up to 4,096 characters | | Per-box vars | 100 names and 64 KiB of names and values together, inside the launch budget | | Repos per environment | 16, each with an optional setup script of up to 8 KiB | | Secret files per environment | 16, each up to 64 KiB, at a path of up to 256 characters | | Setup script | 16 KiB | | Launch budget | The environment plus the box's own vars must fit 16 KiB compressed; a save or create that would not is refused as `environment_too_large` | | Versions kept | 50 per environment; older ones no live box pins are pruned | ## Commands and files [#commands-and-files] `prized exec`, `prized cp`, the edge API, and the dashboard's Files tab ([Run commands and files](https://prized.dev/docs/exec)). | Limit | Value | | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Synchronous run | 600 seconds through the API (default 30); `prized exec` has no limit unless you pass `--timeout` | | Output kept per stream | The last 1 MiB, flagged as truncated | | Exec request document | 1 MiB | | Environment variables per run | 32 | | Inline file read or write | 1 MiB | | Upload or download | 1 GiB, and 30 minutes per request. A directory download that reaches the cap is cut off, so a truncated archive never looks complete | | Detached process log tail | 16 KiB by default, 1 MiB at most. The process itself has no time limit, and its logs are never deleted for you | | Concurrent requests | 8 per token and 8 per box | | Request rate | 20 per second sustained, bursts of 40, per token and per box; over it answers `429 rate_limited` with `Retry-After` | | JSON request body | 64 KiB on most `/api/v1` routes; 128 KiB on the secrets routes; 2 MiB on the environment routes | | Dashboard Files tab | 1 MiB in the inline editor; 100 MiB per upload or download; one operation at a time per tab; a folder listing shows 2,000 entries and marks itself truncated past that | ## Prompting an agent remotely [#prompting-an-agent-remotely] | Limit | Value | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Prompt | 512 KiB ([Prompt an agent remotely](https://prized.dev/docs/remote-agents)) | | Runs in progress per box | 1. A second start answers `409 prompt_in_progress` unless it asks to be queued ([One run at a time](https://prized.dev/docs/remote-agents#one-run-at-a-time-and-a-queue)) | | Runs listed | 50, newest first; older run directories stay on the box | | Events per page | 200 by default, 1,000 at most, or 1 MiB, whichever comes first; one event of any size is always delivered | | Event text | 8 KiB per event (the `raw` line is kept whole); a single event over 32 MiB cannot be delivered | | Requests | As for commands and files above | ## Sync, sessions, and ports [#sync-sessions-and-ports] | Limit | Value | | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | File size in a synced project | 256 MB; larger files are skipped and named by `prized sync ls` ([Sync](https://prized.dev/docs/sync#add-a-project)) | | First sync | Up to 3 minutes for the box to install the sync agent and move the first bytes before it is called broken | | Drag-and-drop upload | 512 MB per drop, one file or several; drops older than 7 days are cleaned out ([Sessions](https://prized.dev/docs/sessions#drag-files-in)) | | Mirrored ports | 3000 through 9999 by default; never below 1024, never 22 or 7377; 5900 off by default; TCP only ([Ports](https://prized.dev/docs/ports#limits)) | ## Desktop [#desktop] | Limit | Value | | -------------------- | -------------------------------------------------------------------- | | Per box | One desktop and one browser window ([Desktop](https://prized.dev/docs/desktop#limits)) | | Screen | 1920x1080, scaled to your window; VNC, no audio, no GPU | | Browser profile name | Letters, digits, `-` and `_`, up to 64 characters | ## Secrets and access [#secrets-and-access] | Limit | Value | | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | Secrets per workspace | 100 ([Secrets for agents](https://prized.dev/docs/credentials#secrets-for-agents)) | | Secret name and value | The name is `SCREAMING_SNAKE`, a letter first, up to 128 characters; the value up to 16 KiB | | Allowlisted hosts per secret | 32 | | Secret grants | Each names one box or one member; a secret with none is available to every box except a contractor's ([Scoped secrets](https://prized.dev/docs/teams#scoped-secrets)) | | Secret usage | Counts are a floor; the usage read returns up to 1,000 rows ([Usage](https://prized.dev/docs/teams#usage)) | | SSH certificates | 15 minutes each, minted for you as you connect | | Phone access password | One per box, no expiry; generating a new one, or revoking, replaces it everywhere ([From your phone](https://prized.dev/docs/codex#from-your-phone)) | | Phone access gateway | 8 connections per box at once; a connection with nothing running on it closes after 15 idle minutes | ## Workspace [#workspace] | Limit | Value | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | CLI tokens | No cap; revoke any from **Dashboard → Workspace → CLI tokens** ([CLI tokens](https://prized.dev/docs/workspaces#cli-tokens)) | | Members | No cap | | Invite link | Does not expire; replaced by **New link** or by removing a member ([Invite people](https://prized.dev/docs/workspaces#invite-people)) | | Workspace icon | 1 MiB per upload | | Startup deal link | 30 days ([Custom plans](https://prized.dev/docs/billing#custom-plans)) | | Spend per member | Off, or 1 to 1,000,000 whole dollars a billing cycle ([Policies](https://prized.dev/docs/teams#policies)) | | Boxes per member | Off, or 1 to 100 live boxes (running or paused) | | Largest size per member | Off, or any size | | Auto-pause floor | Off, or 30 minutes to 7 days | | Quiet hours | Off, or one `HH:MM` to `HH:MM` window a day in one IANA timezone, per weekday, with all-day days; a box woken inside it stays up 15 minutes ([Quiet hours](https://prized.dev/docs/teams#quiet-hours)) | | Audit log retention | 7 to 365 days, 90 by default ([Audit log](https://prized.dev/docs/teams#audit-log)) | | Audit log reads | 100 rows a page by default, 1,000 at most per request; the CSV carries up to 50,000 rows | | Audit export | An `https` URL of up to 2,048 characters on a public host, and a secret of 16 to 256 characters; 500 events a delivery, 20 deliveries an hourly pass, 10 seconds a request ([Scheduled export](https://prized.dev/docs/teams#scheduled-export)) | | Reported audit detail | 16 keys of up to 40 characters, strings up to 512 characters, a body up to 64 KiB | ## Retention [#retention] Box metrics 30 days, CLI usage aggregates 90 days, audit rows 90 days by default (7 to 365 by policy), finished deletion operations 30 days. The windows and their zero data retention variants are on [Data retention](https://prized.dev/docs/data-retention#telemetry-retention-windows). ## How limits fail [#how-limits-fail] | Where | What you see | | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | The API | The HTTP status and an `error` code, with a message that quotes the numbers: `409 box_limit`, `409 insufficient_balance`, `429 snapshot_limit`, `402 secret_limit`, `400 invalid_name`, `400 invalid_auto_pause`, `400 invalid_pause_at`, `400 environment_too_large`, `400 too_many_repos`, `400 too_many_files`, `409 prompt_in_progress`, `413 payload_too_large`, `403 policy_denied` (with `rule` and `message`), `403 secret_not_granted`, `429 rate_limited` with `Retry-After` | | The CLI | The [exit code table](https://prized.dev/docs/cli#for-agents-and-scripts): a value outside a range is `USAGE` (2) before anything is sent; the box count, a taken name, or a balance that cannot run the box is `CONFLICT` (6); the snapshot limit and a refused lifecycle change are `CHECK_FAILED` (5) with the control plane's sentence. `--json` carries the same code and message in `error` | | The dashboard | The same sentence next to the control that asked, or the control is not offered: a size the balance cannot run cannot be picked, and the pause deadline is hidden on a paused box | --- # Data retention > What pausing, deleting, and closing keep and remove; the undo window for deleted boxes; zero data retention; and what is retained afterwards. URL: https://prized.dev/docs/data-retention ## Pause vs delete [#pause-vs-delete] | Action | Machine | Disk | Files, tools, git state | Undo | | --------------- | ---------------------------------- | ------------ | ----------------------- | ---------------------------------------- | | Pause | Stopped, billed at the paused rate | Kept | Kept | Resume any time | | Delete | Terminated | Erased | Gone from the box | Restore the final snapshot within 7 days | | Close workspace | Every box paused | Kept 30 days | Kept 30 days | Reopen within 30 days | | Erase workspace | Terminated | Erased now | Gone | None | Deep sleep is a pause that moves the disk to a snapshot; nothing here changes it, except that deleting a box while it is in deep sleep erases that snapshot and leaves no undo copy — there is no disk attached to take a final snapshot of. Disks and snapshots are encrypted at rest ([Security and trust](https://prized.dev/docs/security#data-at-rest-and-in-flight)). ## Deleting a box [#deleting-a-box] Deleting a box terminates the machine and erases its disk. Its last state is kept for 7 days as a snapshot named `deleted--` under **Recently deleted** on **Dashboard → Snapshots**. * **Undo a delete.** Restore the snapshot: the box comes back as a new box, with the old name if nothing else took it, and its disk as it was. Restoring renews the copy's 7 days. * **It does not count** toward the 20 snapshots you can keep. * **Remove it sooner.** **Delete now** on the row removes the copy at once; [zero data retention](#zero-data-retention) removes every copy and stops new ones. A delete removes the machine, its disk, its tunnel and SSH identity, and any phone access password right away. The box's event history, its usage on your ledger, and the [audit rows](https://prized.dev/docs/teams#audit-log) that name it stay in your workspace until the workspace is erased. ## Deletion operations [#deletion-operations] Every delete answers with an operation you can poll instead of watching the box or snapshot. ```bash curl -X DELETE -H "Authorization: Bearer $PRIZED_TOKEN" https://prized.dev/api/v1/boxes/ # {"box":{...},"operation":{"id":"dop_…","kind":"box_delete","status":"pending",...}} curl -H "Authorization: Bearer $PRIZED_TOKEN" https://prized.dev/api/v1/operations/dop_… # {"operation":{"id":"dop_…","kind":"box_delete","status":"completed","completedAt":"…"}} ``` `status` runs `pending`, `processing`, `completed` (for a box, the moment it reads `terminated`), or `failed` with `error`, which is rare and worth a support message. `DELETE /api/v1/snapshots/` answers the same way with `kind: "snapshot_delete"`; deleting something already being deleted returns the same operation; finished operations stay readable for 30 days. From the CLI, `prized ops status ` prints one and `--wait` polls it. Every route is on the [API reference](https://prized.dev/docs/api). ## Zero data retention [#zero-data-retention] **Dashboard → Workspace → Data and privacy**, owner only, from the dashboard (no CLI token can switch it). Turning it on asks you to type exactly `delete deleted box data`; turning it off asks nothing. With it on: * A deleted box leaves no final snapshot, so a delete cannot be undone, and earlier copies are removed at once. Prized's own disaster-recovery backups are separate: this switch does not turn them off, and each ages out within a week (see [below](#records-retained-after-deletion)). * The box monitor stores no process or port history, and deletes what it already stored. Metrics are kept 24 hours instead of 30 days. * Snapshots you take yourself are untouched. * The [audit log](https://prized.dev/docs/teams#audit-log) keeps every row for its full window (audit rows are workspace metadata, not box data), but the `exec`, `file.upload`, `file.download`, and `prompt` rows are written with no detail (`no detail kept (zero data retention)`). ## Closing a workspace [#closing-a-workspace] **Dashboard → Workspace → Close workspace**, owner only, confirmed by typing the workspace's name. At once: * The plan is canceled immediately. Credit in the balance stays but is not refunded; if you reopen, you pick a plan again. * Every box is paused; disks are kept. * Every CLI token, install link, and phone access password is revoked. Your own dashboard sign-in stays, so invoices stay readable and you can reopen. * Other members are removed, the invite link stops working, and domain auto-join is switched off. * You get one email naming the deletion date. Nothing is deleted for 30 days. A banner names the date, and **Reopen** clears the close: boxes stay paused until you start them, the plan is not restored, and auto-join stays off until you switch it back on. Until you reopen, no box can start or wake and no plan can be bought or changed. **Erase now**, below Close, skips the window: it terminates every box and disk, deletes every snapshot, secret, environment, and token, the audit log and policies, cancels the plan (any overage owed bills on a final invoice, as for any [cancelled plan](https://prized.dev/docs/billing#overage)), removes the billing account from Stripe, and signs you out for good. There is no undo. ## Records retained after deletion [#records-retained-after-deletion] Deleting a box or a snapshot removes the machine, the disk, and the storage; the box's event history, its ledger usage, and the audit rows that name it stay in the workspace that owns them. Erasing the workspace (**Erase now**, or 30 days after a close) deletes everything in it: boxes, disks, snapshots, secrets, environments, tokens, members, the ledger, hourly usage, event history, audit log, policies, and CLI usage aggregates. What remains: * **Invoices and payment records at Stripe**, as required for tax and accounting. Prized keeps no copy of your ledger. * **Sign-in identity history**: which provider account (Google or GitHub) signed in as which user, so a reassigned or recreated mailbox cannot inherit a deleted account's standing. * **Fleet-level aggregate telemetry**: counts describing the fleet as a whole, with no per-customer identity. * **Disaster-recovery backups of box disks**, for up to a week after the disk is gone. Prized keeps a rolling week of encrypted backups of every box disk to recover from a platform failure; they are not visible to you, no customer action removes them early, and each ages out on its own within a week. ## Telemetry retention windows [#telemetry-retention-windows] | Data | Kept | With zero data retention | | ------------------------------------------------------------- | -------------------------------------- | ----------------------------------------------------------- | | Box metrics (CPU, memory, disk, network) | 30 days | 24 hours | | Process and listening-port snapshots (the monitor's scrubber) | 72 hours | Not stored | | CLI usage aggregates (command names, versions, exit codes) | 90 days | 90 days | | Audit log ([Teams](https://prized.dev/docs/teams#retention)) | 90 days by default, 7 to 365 by policy | Same; the detail of exec, file, and prompt rows is not kept | --- # Troubleshooting > One command diagnoses and repairs the whole chain: config, auth, tunnel, SSH, sync, daemon, and the box agent. Start here. URL: https://prized.dev/docs/troubleshooting ## Run the doctor [#run-the-doctor] ```bash prized doctor ``` It repairs as it goes (a missing `~/Prized`, Mutagen not fetched, a stopped daemon, a missing SSH `Include`, a box agent behind your CLI), marking each `(fixed)`. A passing check prints nothing, so only what failed is on screen, with its remedy. `--no-fix` reports without touching anything; `-v` prints every check; `--bundle` writes a diagnostics tarball for a bug report, and nothing leaves your machine. ## Box and daemon [#box-and-daemon] | Symptom | Try | | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Box shows unreachable | `prized ssh`: a suspended box wakes on connect. Still down? `prized box info` and the dashboard show its state and last events. | | Box paused by itself | The last event in `prized box info` says why: `reconciler:auto_pause` (idle) or `reconciler:pause_deadline`. `prized box auto-pause BOX off` and `prized box extend BOX --no-deadline` switch the rules off ([Boxes](https://prized.dev/docs/boxes#auto-pause)). | | `ssh: Could not resolve hostname ` from `ssh ` or a sync session | A box's name resolves only through the SSH stanza prized writes (`~/.ssh/config.d/prized`), activated by an `Include` line at the top of `~/.ssh/config`; your ssh did not apply it. `prized doctor` says which and repairs what it can: the line missing (or a stray pre-rename `Include config.d/doppel` that names no file this install wrote — `prized doctor --fix` adds the `config.d/prized` line), a `Host`/`Match` block above it, or an ssh that resolves the box with `-F ~/.ssh/config.d/prized` but not from `~/.ssh/config` (an alias or wrapper around `ssh`, or a HOME that is not your account's). `prized setup`, `prized ssh` and `prized exec` read the stanza file directly and keep working meanwhile; `ssh ` and sync need the Include. | | `"" is not the box this machine knew by that name` (from `ssh ` or a prized command) | The alias was bound to a box that no longer wears the name: the box was recreated, or its freed name was taken by another box. If that is expected, `prized box rebind ` binds the alias to the box that wears the name now; otherwise `prized box ls` shows what is on the account. `could not verify that "" is still box …` means the check itself could not be made (signed out, or Prized unreachable): `prized login`, or try again. A `prized proxy` that was already running when the name changed hands stops with the same message; anything else still connected under that name is refused the same way until you rebind. | | Daemon not running | `prized daemon restart`, then `prized daemon logs` if it will not stay up. On a machine that has never run `prized setup` there is nothing to restart yet, and the command says so: setup installs the daemon when it connects a box (`prized daemon install` does it on its own). | | `prized setup` or `daemon install` says the daemon is installed for your login session and this terminal is not in it | You are in a shell outside your macOS login session (ssh, a terminal a remote tool opened, a shell either started), and the daemon on this Mac was installed from inside it, as a GUI-session agent that such a shell cannot see or touch. Run the same command from Terminal on the Mac itself. A machine that has never had the daemon is different: setup from an ssh or remote shell installs it as a background agent, which any login of yours brings up, and which Terminal on the Mac can manage too. | | "the running daemon serves a different box" | You switched the default with `prized init --force` while the old daemon ran, or aimed `--config` at another box's file. `prized daemon restart` moves it. | | doctor warns the daemon serves another box | `--config` drives another box with no port mirrors. Connect it with `prized setup --box NAME` and use `--box NAME` instead. | | doctor reports an unpinned SSH bridge | The stanza was written by an earlier release; `prized doctor --fix` rewrites it (so do `prized login` and `prized setup`). | | `no config for box NAME` | `prized setup --box NAME` connects it. A misspelled name lands here too; `prized box ls` has the right one. | | doctor fails `agent-version` | Agent behind the CLI: `prized doctor` updates it (a brand-new box also catches up on its own within a minute or two). `cli behind the agent`: run `prized update`. | | Claude Desktop (or another SSH app) asks for a password | The box has not received this machine's device key yet. Run `prized login` (or one `prized ssh`) here, wait a minute, retry ([Claude Desktop remote](https://prized.dev/docs/claude-desktop)). | ## Ports and sync [#ports-and-sync] | Symptom | Try | | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | A port is not on localhost | `prized ports ls` shows every listener and why one is not mirrored. A toggled-off port stays off until you toggle it back. | | Sync seems stuck | A first edit in a folder the box has not touched recently can take 10 s. Then `prized sync ls`, `prized sync flush` to force a cycle, `prized sync repair myproject` if a session halted. | | Sync shows `error` | The reason is under the table and the daemon retries by itself. Fix the cause (or `prized sync rm` the project) and re-run `prized setup`. | | `prized setup` says the project step failed, but the box is fine | Re-run `prized setup`; `prized sync ls` names the cause. | | Mutagen reported missing | `prized doctor` fetches Prized's managed copy; so does `prized mutagen ensure`. | ## CLI and updates [#cli-and-updates] | Symptom | Try | | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | CLI feels outdated | It [updates itself](https://prized.dev/docs/cli#updates); `prized update` installs the newest release now. | | `prized version` warns the daemon version differs | An update landed while something was connected through the daemon; `prized daemon restart` applies it now. | | Every command exits 5 with an update hint | Your CLI is older than the minimum the API supports. Run `prized update` once. | | `prized: command not found` right after installing | `~/.local/bin` is not on this shell's `PATH` yet: `source` the file the installer's last line names, or open a new terminal. Windows: see [Windows](#windows). | | `prized: command not found` on an older install | Run `doppel update` once, or re-run the install script; either installs `prized` and keeps `doppel` as an alias. | | macOS asks again for folder access after an update | `prized update` to the latest release and approve once more; releases are signed with a stable Developer ID, so macOS remembers it. | | An environment item fails during setup but installs fine by hand | Re-run with `prized setup --env-workers 1` (one item at a time, in order) and `--verbose` ([Bring your environment](https://prized.dev/docs/environment)). | | `ssh: not found in a trusted system directory` | Prized runs the OpenSSH client from the system directories (`/usr/bin`; `System32\OpenSSH` on Windows), never one found on `PATH`. For an ssh installed elsewhere, name it: `bin = "/path/to/ssh"` under `[ssh]` in `~/.config/prized/config.toml`. | | `docker: not found in a trusted location` (or `pg_dump`, `gzip`) during the data step | The data step runs its helpers only from standard system and package-manager directories, the same places the scan looks, never from `PATH`. Install the tool there, or answer no to the data group and copy by hand. | ## Windows [#windows] | Symptom | Try | | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `prized` is not recognized right after installing | A window that was already open lacks the new `PATH` entry. Open a new terminal, or run `$env:Path = "$env:LOCALAPPDATA\Prized\bin;$env:Path"` in that one. | | `prized ssh` says ssh is missing | Turn on Windows OpenSSH under Settings, Optional features, OpenSSH Client, or `Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0` in an elevated PowerShell. | | SmartScreen or Defender warns about `prized.exe` | The binaries are not code-signed, so Explorer can show "unrecognized app" (More info, Run anyway). If Defender quarantines it, restore it, re-run the installer, and email the support address shown on https://prized.dev/docs/troubleshooting. | | The daemon is not running, or dies after logout | `prized daemon install` re-registers and starts the `PrizedDaemon` task, which runs only while you are signed in; `prized daemon logs` has its log. | | `prized daemon install` fails with an access error | A machine policy that blocks Task Scheduler for users blocks it. Run `prizedd run` in a terminal you keep open, and tell the support address shown on https://prized.dev/docs/troubleshooting. | | `prized mosh`, `mount`, or `env` refuses | Not on Windows yet ([CLI](https://prized.dev/docs/cli#windows)); `prized ssh`, sync, ports, `exec`, and `cp` cover the same ground. | ## Older installs [#older-installs] Configs that still point at `doppel.prized.dev` or `api.doppel.prized.dev` keep working; page requests redirect to `prized.dev`. ## Account [#account] | Symptom | Try | | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Sign-in fails with "unable to link account" | Your identity provider reports a different account for this address than the one Prized knows (a mailbox deleted, recreated, or reassigned). Email the support address shown on https://prized.dev/docs/troubleshooting. | | A work address that had a Prized account now starts empty | Expected if the address was reassigned to you: accounts are keyed to your identity, not the mailbox, so you get a fresh one rather than the previous holder's. | ## Still stuck [#still-stuck] Email the support address shown on https://prized.dev/docs/troubleshooting with the doctor output. --- # Security and trust > How a box is reached, what the control plane can and cannot see, where credentials and secrets go, and how to report a problem. URL: https://prized.dev/docs/security This page describes what the product does today, as built. Where Prized does not do something yet, it says so. ## Reaching a box [#reaching-a-box] Your machine reaches a box through an encrypted WireGuard tunnel the CLI builds inside its own process, and SSH runs inside that tunnel. Nothing else on the box is reachable from the internet. * **The tunnel.** `prized login` generates a WireGuard key and an SSH identity for this install; only the public halves are sent to Prized. No root, no TUN device, no change to your network settings. * **What the box exposes.** Two inbound ports: the tunnel, and its fallback over TCP 443 for networks that block UDP. There is no inbound SSH port. * **SSH inside the tunnel.** The box presents a host certificate signed by Prized, so there is no trust-on-first-use prompt. You authenticate with a user certificate minted for your token and box on each connection, valid for 15 minutes. The box account has no password. ### Other ways in [#other-ways-in] | Path | Credential | What it reaches | | -------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Browser terminal, files, desktop | A ticket from your signed-in dashboard session: one box, 10 minutes, never in a URL | A relay opens its own tunnel and certificate per request; it terminates both encrypted hops, so it can read the session as it passes, and stores nothing. | | Box monitor | A monitor ticket, same lifetime | The box's process and port list, nothing else. | | Phone (the ChatGPT app) | A phone password for one box: shown once, stored hashed, revocable | The SSH gateway checks it, wakes the box, and bridges the session, so it too can read it as it passes. Terminal only; failed passwords are rate limited. | | Desktop apps (Claude Desktop) | This machine's SSH public key, registered by `prized login` | A restricted, tunnel-only key ([Claude Desktop remote](https://prized.dev/docs/claude-desktop#what-the-fallback-key-can-do)). | | Scripts and agents | Your CLI token (`dcp_`) | Everything the dashboard can do short of minting credentials or changing membership, roles, or policies, the [edge API](https://prized.dev/docs/exec#the-edge-api) included. An owner's token also reaches billing; a contractor's reaches only the boxes they own. | ## Identity and access [#identity-and-access] You sign in with Google or GitHub; a Prized account has no password of its own, and a verified email address is required. Boxes belong to a workspace and record an owner, the member who created them. Owners and members reach every box in the workspace; a contractor reaches only the boxes they own. Only the owner changes billing, the claimed domain, the invite link, membership and roles, policies, and the data-retention switch ([Workspaces](https://prized.dev/docs/workspaces), [Teams](https://prized.dev/docs/teams)). ### Credentials Prized issues [#credentials-prized-issues] | Credential | Scope | Stored as | Ends when | | ---------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | CLI token `dcp_` | The workspace, for one machine | sha256 hash; shown once | You revoke it or the member is removed; its tunnel closes and its device key leaves your boxes ([CLI tokens](https://prized.dev/docs/workspaces#cli-tokens)) | | Terminal and monitor tickets | One box | sha256 hash | 10 minutes after minting | | Phone password | One box | sha256 hash; shown once | Revoked, replaced, the box deleted, or its member removed | | Device SSH key | One CLI token | ed25519 public key | With its token ([Removing a device](https://prized.dev/docs/claude-desktop#removing-a-device)) | | SSH user certificate | Your workspace and one box | Not stored; one [audit row](https://prized.dev/docs/teams#audit-log) (`box.connect`) records who, which credential, which box, when | 15 minutes after minting | Only a signed-in dashboard session can mint a token, a ticket, or a phone password (the browser step of `prized login` is that session), so a leaked CLI token cannot mint more credentials. Removing a member revokes everything they created. ## Credentials and secrets [#credentials-and-secrets] Your agents' sign-ins travel from your machine to the box over SSH and never touch the control plane ([Credentials](https://prized.dev/docs/credentials)); no API route accepts one, and CLI telemetry carries command names only. * **Secrets and connectors.** A stored secret is a placeholder in the box's environment; the box's broker swaps in the real value on HTTPS requests to the hosts you allowlisted and refuses plain HTTP. The value is encrypted at rest and never read back. A secret with [grants](https://prized.dev/docs/teams#scoped-secrets) is decrypted only for the boxes named or owned by the members named; one without, for every box whose owner is not a contractor ([Secrets for agents](https://prized.dev/docs/credentials#secrets-for-agents)). * **What the broker does not protect against.** Anything running as your user can send a request that spends a mounted secret, and root on your box can read anything on it. The broker keeps values out of env files, shell history, logs, and pasted placeholders; it is not a defense against the box itself. * **Usage metadata.** Per secret and host, Prized records how often the value was swapped in and when; never a path, header, body, or value ([Teams: Usage](https://prized.dev/docs/teams#usage)). * **Environment secret files** are encrypted the same way and delivered over the box's authenticated channel ([Environments](https://prized.dev/docs/environments#vars-and-secret-files)). **Restricted boxes** receive no credentials through any channel ([Safe for third parties](https://prized.dev/docs/environments#safe-for-third-parties)). ## Data at rest and in flight [#data-at-rest-and-in-flight] * **Disks and snapshots** are encrypted at rest. Boxes run in `us-west-2` (Oregon) or `us-west-1` (Northern California); a snapshot stays in its box's region unless you move the box. Prized also keeps a rolling week of encrypted disaster-recovery backups of every box disk, which no customer action removes early. What a delete keeps is in [Data retention](https://prized.dev/docs/data-retention#records-retained-after-deletion). * **The control-plane database** holds a box's identifiers, size, region, state, tunnel addresses, last vitals, and settings; secrets and secret files only as ciphertext. * **In flight.** The site, the API, and the terminal relay serve HTTPS only, and the CLI refuses a plain-`http` control-plane URL anywhere but loopback. * **Telemetry.** A running box reports resource vitals, listening ports, and its top processes; never file names, file contents, terminal output, or shell history ([Privacy](https://prized.dev/docs/privacy), [Data retention](https://prized.dev/docs/data-retention#telemetry-retention-windows)). The [activity page](https://prized.dev/docs/teams#team-activity) derives sessions from tunnel connections, never from keystrokes or screen contents. * **Error reports and logs** never contain request bodies, `Authorization` headers, tickets, keys, or user data; the CLI and the box agent have no crash reporting. ## On the box [#on-the-box] * **Processes.** The box agent runs as your login user; the secrets broker, `sshd`, and the tunnel run as root ([Machines](https://prized.dev/docs/machines#what-runs-on-the-box)). Their updates are signed and checksum-verified before they run. * **Your account** has passwordless sudo, so anything running as you can become root. `sshd` accepts Prized's short-lived certificates, registered device keys, and any key in `~/.ssh/authorized_keys`. * **What the control plane can tell a box.** An agent version to update to, a pause request, device keys, environment variables and secret files, secret values, and the tunnel's peer list. No route carries a shell command to the box. * **What a CLI token can do.** Any command on any box in your workspace ([exec](https://prized.dev/docs/exec), files, [prompts](https://prized.dev/docs/remote-agents), a desktop); with sudo that is the whole machine. The [audit log](https://prized.dev/docs/teams#audit-log) records lifecycle, settings, tokens, snapshots, environments, secrets, membership, policies, and every certificate minted; commands, file transfers, prompts, and desktop starts are **reported** by the CLI or the box, not observed. The log never contains stdout, file contents, secret values, keys, certificates, or tokens. * **Isolation.** Every box is its own virtual machine with its own kernel and encrypted disk, and no cloud credentials are attached to it. ## What the control plane cannot see [#what-the-control-plane-cannot-see] Your agents' sign-in tokens; secret values after you save them; commands typed over ssh, terminal output, file contents, and prompt bodies (the audit log holds a command line, a path, or a prompt's first line only when the CLI or the box reported it); and your private keys, which never leave your machine. ## Sub-processors [#sub-processors] The services that hold or carry your data, all in the United States. | Service | What for | | ------------------- | ----------------------------------------------------------------------------------------------------------------- | | Amazon Web Services | Boxes, disks, snapshots, release artifacts, and database backups, in `us-west-2` and `us-west-1` | | Fly.io | The API, the terminal relay, and the SSH gateway | | Vercel | The site and dashboard, and aggregate page-view analytics | | Neon | The control-plane database | | Stripe | Billing, invoices, and card storage | | PostHog | Product events, session replay, and browser errors | | Sentry | Server-side error reports | | Cloudflare | DNS and transactional email | | Google, GitHub | Sign-in | | Anthropic | The environment advisor in `prized setup`, only when you use it; it receives a redacted inventory of your machine | The [audit export](https://prized.dev/docs/teams#scheduled-export) posts your audit rows to an endpoint you configure; that copy is held by you. ## Certifications and disclosure [#certifications-and-disclosure] None today: no SOC 2, ISO 27001, or HIPAA attestation, and no data processing agreement. To report a vulnerability, email the support address shown on https://prized.dev/docs/troubleshooting with the steps to reproduce; we would rather hear about a problem before it is published. There is no bug bounty. --- # Privacy > What Prized records about how you use the product, and what it never records. URL: https://prized.dev/docs/privacy Prized collects two kinds of usage data: what the CLI does on your machine, and how you use the website and dashboard. Neither reads what is inside your box. How a box is reached and what the control plane holds about it are on [Security and trust](https://prized.dev/docs/security). ## Never collected [#never-collected] Nothing from inside your box is sent anywhere for analytics: * Source code, files, and anything else on the box filesystem * Terminal output and shell history, the in-browser terminal included * Secret values and environment variable values * CLI arguments, flags, and paths ## Website and dashboard [#website-and-dashboard] The site records page views, clicks, and a short list of named product events: creating, pausing, resuming, resizing, and deleting a box; changing auto-pause; copying a CLI command; the onboarding steps you view; starting checkout (with the plan amount); and activating a plan. Once you sign in, those events are tied to your account, so a broken flow can be traced to the account that hit it. Before anything leaves your browser, its URL is cut down to the path: query strings and fragments are dropped, and one-time links (invites, install links) are replaced with a placeholder. Page views are also counted in aggregate by the platform that hosts the site; that count is never tied to your account and sets no cookie. ## Session replay [#session-replay] While you are signed in, the dashboard records a replay of the interface, masked in your browser before anything is sent: * Every input and every piece of text is masked. A replay shows layout, clicks, and navigation, never the words on the page. * The terminal, the secrets panel, and the environment variables panel record as empty placeholders. Signed-out visitors are never recorded, so the marketing pages and these docs produce no replay. ## Turning it off [#turning-it-off] * **Browser.** The site respects Do Not Track. With it on, the site records nothing: no page views, no events, no replay. * **CLI.** A separate switch, under [CLI: usage telemetry](https://prized.dev/docs/cli#usage-telemetry). ## How long it is kept [#how-long-it-is-kept] Box metrics, process snapshots, and CLI usage aggregates each have a fixed retention window, and a workspace can shorten them with zero data retention; see [Data retention](https://prized.dev/docs/data-retention). ## Where it goes [#where-it-goes] * **PostHog** holds the product events, the account they belong to, session replay, and error reports when a page throws, in its US cloud. * **Vercel Web Analytics**, run by the platform that hosts the site, holds the aggregate page-view counts: visits per page, from which country, on which browser. It is never told who you are, sets no cookie, and sees the same cut-down URL. An ad blocker may block the analytics requests; that affects our telemetry, never the dashboard.