Organization environments
An organization environment is a revisioned configuration document. It is a
different resource from the managed sandbox platform reported by
GET /v1/capabilities. When a Session receives a sandbox, Cloud applies the
environment revision pinned to that lease before its first Turn starts.
Create one with a caller-owned UUID and PUT /v1/environments/{environmentId}.
The document accepts a name, description, shell setup script, variables, an
extensions list, cache.paths, an optional github boolean (default
false), and an optional repositories list. When github is on, sandboxes of
Sessions on this environment are intended to receive short-lived tokens of
WAMP’s GitHub App for GitHub installations granted to the installation that
owns the Session, with contents and pull_requests write. Delivery to the
sandbox arrives with a later engine release; today the flag is stored and
checked but no token is minted. Invalid inputs return 400 environment_invalid with field
paths in issues. Read with GET /v1/environments or
GET /v1/environments/{environmentId}. People need sessions:create to read;
an App installation needs environments:manage. Writes need the sensitive
wamp.cloud.environments:manage capability.
Every change creates a numbered revision with its writer. Quote the current
revision in If-Match, for example If-Match: "3", when updating a document,
secret, or archive state. A stale revision returns 409; an identical write
does not advance the revision. PUT .../secrets/{name} accepts { "value": "..." }; reads list names, versions and writers, never values. Secret values
are encrypted in Cloud’s database. DELETE .../secrets/{name} removes the name
from the head. Restore creates a new head from a prior document and its pinned
secret versions; it cannot reverse a credential rotation at the external
provider, and does not change the environment’s default or archive state.
GET /v1/environments/{environmentId}/revisions lists revisions newest first,
each with its writer, whole document and the secret versions it pins. Archive,
unarchive and default changes also write a revision, with the document
unchanged. Pass the page’s nextBefore as before for older revisions;
limit is 1–50, default 20.
A writer is createdBy: { kind, id }: a person by membership id, or an
installation. The agent of a Session that a person or installation started can
change environments on their behalf, with their capabilities; what it writes
also carries createdBy.via: { sessionId, runId }, the agent’s Session and
Run. Secret versions name their writer the same way.
PUT /v1/organization/default-environment with { "id": "..." } selects the
default for new human Sessions; { "id": null } clears it. Installations never
inherit the default. A Session create request can name environment: { "id": "..." }, environment: { "name": "..." }, or environment: null to use the
standard sandbox. Omission uses the default for a person and no environment
for an installation. Installations need environments:manage to select one.
The selection is part of the idempotent create request. A Session response
shows its bound id, name and current revision, or null.
The sandbox receives variables and the pinned secret versions. setup runs
in the workspace with a 600-second limit. An environment_forbidden result
means the stored Turn authority can no longer use the Session; a failed or
timed-out setup fails the waiting Turn with environment_setup_failed and a
redacted output tail in the wamp.environment.apply event. Stop during setup
releases the sandbox. A later Turn on the same lease joins the in-flight apply.
setup runs as the sandbox user, without root, on the same image whichever
compute the organization uses: Debian 12 with Node.js 22, compilers, make,
pkg-config, the common -dev libraries, git, curl, ssh, and Python 3
with its headers, pip and venv. It cannot install apt packages or call
sudo; install toolchains and CLIs into the user’s home or the workspace
instead. The system Python is externally managed (PEP 668), so install Python
packages into a virtual environment:
npm cipython3 -m venv .venv.venv/bin/pip install -r requirements.txtecho "PATH=$PWD/.venv/bin:$PATH" >> "$WAMP_ENV"The last line puts the environment’s Python first on PATH for the agent. The
image also has the wamp command, so wamp ext init and wamp ext pack
build an extension archive inside the sandbox without a network.
cache.paths may contain workspace-relative paths or ~/ paths. On a cache
hit Cloud restores the archive before running setup. The key includes the
environment inputs, GitHub access, the repositories the sandbox clones, sandbox
image and pool, launch repository and branch, and the UTC day. A clean sandbox may save a new archive once per key and day;
restored or forked sandboxes never receive a save slot. A save skipped because
of a secret or the size limit appears as cacheSkip in the apply event, and a
restore the sandbox discarded (a failed download, a sha256 mismatch, a
malformed archive) as cacheDiscarded with the reason. Cache content must not
include credentials or WAMP state.
repositories names up to 20 GitHub repositories a Session without a launch
repository starts with. It needs github: true, because the clones use that
access. Each entry is { "repository": "owner/name" } with an optional branch
(a branch or tag; default: the repository’s default branch) and an optional
path, the directory under /workspace (default: the repository name). A path
is one name of letters, digits, ., _ or -, does not start with . or -,
and is not project; paths must differ, ignoring case, after the defaults are
filled in, so acme/web and other/web need a path for one of them. Reads
return the entries as written, and [] when none is set.
{ "name": "product", "setup": "(cd backend && npm ci)\n(cd site && npm ci)", "github": true, "repositories": [ { "repository": "acme/backend" }, { "repository": "acme/web", "branch": "develop", "path": "site" } ]}Before the cache restore and setup, every fresh sandbox of such a Session
clones each listed repository that is missing from /workspace/<path>, without
file contents until they are needed (git clone --filter=blob:none). Anything
already at a path, such as a directory restored from a checkpoint, is left
alone. A clone that fails fails the apply with environment_setup_failed and
names the repository; nothing after it runs. All clones of one sandbox share a
five-minute limit. A Session with a launch repository keeps its single
repository at /workspace/project and skips the list. The terminal
wamp.environment.apply event reports each entry as cloned, kept,
skipped or failed. Editing the list affects each Session’s next fresh
sandbox; it only adds clones and never removes a directory.
Extensions
Section titled “Extensions”extensions lists up to 20 extensions that every sandbox installs after
setup has run and the cache has been saved, each listed once:
{ "catalog": "<slug>" } for an item of the WAMP extension catalog, or
{ "archive": "sha256:<hex>" } for a packed extension the environment owns.
{ "name": "issues", "setup": "npm ci", "extensions": [ { "catalog": "wamp-mcp-postgres" }, { "archive": "sha256:5e5e…" } ]}A catalog item installs at the version the catalog serves when the sandbox
starts. A write checks only the catalog items it adds: a slug the catalog does
not list, or lists with no release this platform runs, is
400 environment_invalid, and a catalog Cloud cannot reach is
503 cloud_extension_catalog_unavailable with Retry-After.
An archive is the .zip that wamp ext pack writes. Upload it with
PUT /v1/environments/{environmentId}/archives/{sha256},
Content-Type: application/zip and at most 32 MiB, where sha256 is the hex
digest of the bytes. The answer is
{ "archive": { "archive", "sizeBytes", "extension": { "id", "name", "version" } } }.
An upload writes no revision and takes no If-Match; the archive runs once a
revision lists it, and uploading the same bytes again answers the same
archive. Cloud refuses, with 400 environment_invalid and the reason in
issues, an archive that is not a zip, has an entry outside its root, carries
no valid extension.json, needs a plugin API this platform does not serve,
declares an agent runtime, or needs the desktop app (requiresElectron). An
environment’s reads list its archives under archives. An organization holds
at most 1 GiB of archives (409 quota_exceeded). An archive that no revision
in the retention window lists — the last 30 days, the last 50 revisions and
the newest revision a person wrote — is deleted a day after it was uploaded,
and restoring a revision that names a deleted archive is 400 environment_invalid naming it: upload it again first.
The agent of a Session adds an archive it built without sending bytes. It
presents the .zip with present_files, then names its digest with the
environments.archives.put operation, or lists { "archive": "sha256:<hex>" }
in environments.put directly. Cloud copies the newest file with that digest
presented in the agent’s own Session or in a Session it started; any other
digest is refused.
An extension that cannot install does not fail the apply. It may be refused in
the sandbox — built into the sandbox already, declaring an agent runtime,
needing the desktop app, or connecting only through an OAuth sign-in a sandbox
cannot complete — or its package may fail to download or verify. After the
installs, MCP servers already running restart on the environment’s variables
and secrets, MCP servers newly declared since the sandbox started (for example
by setup) start, and pooled agent-runtime connections are recycled. The apply
then waits up to 120 seconds for the MCP servers the extensions declare. The
terminal wamp.environment.apply event lists every entry as active or
failed with a reason, and the transcript’s setup row shows them: an
extension whose MCP server did not start is failed, with the server’s error.
MCP servers
Section titled “MCP servers”An environment gets an MCP server in one of three ways:
- a catalog extension that declares one;
- an archive that declares one — an
extension.jsonwithcontributes.mcpServersand no code is a complete extension, and one that ships its own server keeps it underruntime/and names it as"args": ["${extensionDir}/runtime/server.mjs"](see the manifest reference); - a
.wamp/mcp-servers.jsonin the Session’s workspace, committed with the repository or written bysetup, which runs in that directory.
{ "servers": { "tracker": { "transport": "http", "url": "https://mcp.example.com/mcp", "headers": { "Authorization": "Bearer ${TRACKER_TOKEN}" } }, "search": { "command": "npx", "args": ["-y", "@example/search-mcp@1.4.0"], "env": { "SEARCH_API_KEY": "${SEARCH_API_KEY}", "REGION": "${REGION:-eu}" } } }}${NAME} and ${NAME:-default} expand from the sandbox’s environment, which
holds the environment’s variables and secrets, in command, args, env,
url and headers. A local server’s process receives a basic environment
(such as PATH and HOME), what its env names, and the variables its
extension declares — not every secret. Keep tokens in secrets and reference
them this way rather than writing them into the file. MCP servers reach every
agent runtime; an extension’s own tools reach only the WAMP runtime, so tools
meant for every runtime belong in an MCP server.
Archiving
Section titled “Archiving”Archiving is reversible and leaves existing Session bindings intact. The
current default cannot be archived. Unarchiving a name already used by a live
environment returns 409 environment_name_conflict. At most 200 live
environments may belong to an organization.