français : cette page n’est pas encore traduite, la voici en anglais.
The MCP server lets a model deploy an image as a Cube, or several Cubes together, read their logs and go back to an earlier version; make sandboxes, run commands in them, read and write files, open ports, take snapshots and roll back; make Drives, disks that outlive sandboxes, mount them, keep snapshots of them and back them up; point domains at apps and sandboxes; and look at the project and the account. It keeps no state of its own: everything goes through the TypeScript SDK to the API.
It needs Node 23.6 or later, and runs from npm as glac-mcp, which brings the official MCP TypeScript SDK it is built on:
GLAC_API_URL=https://api.glac.ai GLAC_API_KEY=ak_... npx glac-mcpConnect a client
Claude Code:
claude mcp add glac -e GLAC_API_URL=https://api.glac.ai -e GLAC_API_KEY=ak_... -- npx glac-mcpClaude Desktop (claude_desktop_config.json; on macOS in ~/Library/Application Support/Claude/), and Cursor (~/.cursor/mcp.json or .cursor/mcp.json in a project) take the same entry:
{
"mcpServers": {
"glac": {
"command": "npx",
"args": ["glac-mcp"],
"env": { "GLAC_API_URL": "https://api.glac.ai", "GLAC_API_KEY": "ak_..." }
}
}
}Over HTTP
glac-mcp serve --http serves the same tools over MCP’s Streamable HTTP transport at /mcp, for a client that connects to an address instead of starting a process:
npx glac-mcp serve --http 127.0.0.1:7071
claude mcp add --transport http glac http://127.0.0.1:7071/mcp --header "Authorization: Bearer ak_..."The server holds no key of its own: each request brings an API key as a Bearer token, the server checks it with the API (and takes it as good for 30 seconds), and the tools use that key and no other. A request without a key, or with one the API does not know, is answered 401 with a WWW-Authenticate: Bearer challenge that points at the endpoint’s protected resource metadata (RFC 9728, at /.well-known/oauth-protected-resource/mcp). Every request is served on its own, with no session, so any number of these servers can stand behind one address. It listens on this machine only unless told otherwise; on any other address put TLS in front of it, and set GLAC_MCP_PUBLIC_URL to the address clients use.
Sign in instead of pasting a key
The endpoint Glac serves also takes the access token of an app you connected to Glac by signing in. [TODO: the endpoint’s address and its authorization server’s, once they are served.] A client such as Claude, ChatGPT or Cursor then connects with no key and no set-up: add the endpoint’s address as a remote MCP server (in Claude, a custom connector), and the client reads the endpoint’s protected resource metadata, finds Glac’s authorization server there (Ory Hydra, with the scopes below), and registers itself with it: by its own Client ID Metadata Document, which is what MCP’s revision 2026-07-28 asks for first, or with a registration (RFC 7591) where it has none. Its redirect address has to be https, or localhost for an app on your own computer. Your browser opens the console: sign in (with your password, GitHub or Google), and the console shows the app’s name, whether it registered itself or published its description, the address it sends you back to, and what it asks for. Pick the account and the project (one unless you pick more) and what it may do:
| Scope | What it lets the app do |
|---|---|
sandboxes:read |
See sandboxes, their snapshots, Drives, backups and SSH keys, and Browser sessions |
sandboxes:write |
Make, change and delete them; run commands, read and write files, open terminals and port links |
apps:read |
See apps on Cube, their versions, logs and metrics, deploys, domains, shared variables, databases and Managed Applications |
apps:deploy |
Deploy, roll back, scale and delete apps, deploys, shared variables and databases, and install Managed Applications |
billing:read |
See the account’s plan, bill, Credits, usage and limits (granted by an owner) |
Allow it, and the app gets an access token for the API and the MCP endpoint (it lasts ten minutes; the app refreshes it) and goes on as you allowed, in the projects you picked and no others. A tool outside those scopes is refused, naming the scope it takes (403 insufficient_scope), and so is what no connected app may do whatever its scopes: API keys, webhooks, the audit log, changes to billing or to the account’s settings, domains, secrets, log destinations. What your role in the account does not let you do, the app does not do either. Your connected apps are listed in the console under your settings (Connected apps), each with what it may do, where, and when it was last used; Revoke stops every token of it at the API at once, and at the MCP endpoint within 30 seconds.
An endpoint you run yourself is not one the authorization server issues tokens for: it takes API keys.
Tools
The 73 tools follow what a model needs to do, not the REST calls one by one. They are grouped, and named, by product: Cube (cube_*; the same tools answer to worker_* too, which are not listed), Sandbox (sandbox_*), Drive (drive_*), domains (domain_*), Browser (browser_*), Database (database_*), Managed Applications (catalogue_*, installation_*), then the project (project_*) and the account (account_*), and listed in that order.
Cube
Cube runs an image as a service at an address of its own, a Cube, which the API calls an app, and so do the tools’ arguments (app). These seven need the platform’s control plane.
| Tool | What it does |
|---|---|
cube_deploy |
Deploys an image to the app of a name, which is made the first time; what holds at once (public, regions, the scale, checkpoint_seconds: checkpoints of its keyed instances, 0 for none) is changed first. What holds at once also takes concurrency, depends_on, network (aliases, how it is reached) and metadata. A version also takes volumes (under the server’s prefix), kind, user, working_dir, stop_signal, stop_grace_ms, hostname and runtime (limits, sysctls, tmpfs, its resolver, the size of /dev/shm); never a file, which is how an app would be given a secret. Waits for the version for a while, and says whether it is ready, failed (in the image’s own words) or still building. |
cube_list |
The apps, or one in detail: its versions, where its traffic is, its keyed instances, the secrets its keys are signed with (by their first characters, never a value) and what its log counted. A database’s app says which database (managed_by), which deploys and deletes it: a deploy or a rollback of it here is refused. |
cube_rollback |
Sends an app’s traffic back to an earlier version. |
cube_metrics |
What the machines that serve an app counted of it, minute by minute or hour by hour: requests, those refused and by the class of their status, p50 and p99, instances awake and stopped, wakes, instances made, CPU and memory; the steps with something in them, and what they add up to. |
cube_logs |
Reads an app’s log: what its instances printed, its requests, what the platform did; by instance, version, kind, status or text, and on from a cursor. |
cube_instance |
Makes or deletes the instance of a key. Moving one to another version in place replaces its machine and ends its connections, and so does its revert: that is a person’s to start, cancel and follow (glac cube upgrade, glac cube upgrades revert|cancel, the SDKs’ upgrade and revert), a batch of them over an app’s keys the more so (glac cube upgrades batch), and no tool does it. |
cube_instance_ports |
The own public IPv6 address of a key’s instance and the ports open at it, wherever the instance is: open opens a port (TCP, or UDP) to the sources from names (IPv6 prefixes or addresses, ::/0 for everybody, which has to be said), close closes one. What is open stays open while the instance sleeps (a packet wakes it) and when it moves; a new instance of the key has none of it. The program has to listen on ::. |
Sandbox
| Tool | What it does |
|---|---|
sandbox_run |
Runs a command and waits. With a sandbox name the sandbox is made the first time and kept; without, a one-off sandbox is used and deleted. A sandbox it makes (as sandbox_exec makes one) takes a service of its own that starts when it boots (start, start_command, ready), project_network, hostname and stop. A sandbox it makes with init: "systemd" is a whole system: the image’s own systemd is PID 1 (without image, the platform’s Ubuntu with systemd and OpenSSH), so packages, services started with systemctl, cron and the journal work as on a machine; sandbox_exec takes it too, and sandbox_list shows each sandbox’s init. |
sandbox_exec, sandbox_output, sandbox_stdin, sandbox_signal |
Starts a long command; reads its output from a position; writes to its input; sends it a signal. |
sandbox_read_file, sandbox_write_file, sandbox_list_files |
Files. Images come back as images; other binary files are only described. |
sandbox_url |
A link to a port. |
sandbox_ports |
The sandbox’s own public IPv6 address, where it has one, and the ports open at it: open opens a port (TCP, or UDP) to the sources from names (IPv6 prefixes or addresses, ::/0 for everybody, which has to be said), close closes one. Inbound is closed by default; what is open stays open while it sleeps (a packet wakes it) and when it moves. The program has to listen on ::. |
sandbox_revoke_tokens |
Revokes every token handed out for a sandbox so far, those in preview links and cookies and those SSH sessions hold too (401 invalid_token from then on); tokens made after it work. With token (the token, or its id), that token alone. |
sandbox_egress |
What a sandbox may reach on the internet: default, allow and deny (default deny with an allow list keeps code you do not trust in), or clear. A sandbox named for the first time is made with the rules. The rules that add a secret to requests or name the requests a host may be sent are the project’s, and are left alone. |
sandbox_checkpoint |
A sandbox’s checkpoints while it runs, shown, and set with every_seconds (1 to 600; 0 turns them off): when its machine is lost, it goes on elsewhere from its last one with its processes. Off by default; a sandbox named for the first time is made with them. Where no machine takes checkpoints it is refused (checkpoints_unavailable). |
sandbox_list, sandbox_sleep, sandbox_delete |
The sandboxes and snapshots, or one in detail (with how its checkpoints go, where it has them); sleep now; delete. |
sandbox_snapshot, sandbox_fork, sandbox_restore |
Snapshot, fork, and back to a snapshot in place. |
sandbox_add_ssh_key, sandbox_list_ssh_keys, sandbox_delete_ssh_key |
An SSH key for one sandbox, for a day unless you say (30 days at most). |
Drive
A Drive is a disk of the project’s that outlives the sandboxes that use it: mounted read-write by one sandbox at a time, and kept, with what it holds, for the next sandbox: of its region, or of another whose machines read its home’s object store. A snapshot keeps a Drive as it is at an instant, a version of it in the object store: nothing is copied. A backup is a copy of it at an instant, in the object store, until it expires. These sixteen need the platform’s control plane. Behind a prefix, the server’s Drives carry it, and it mounts them in its own sandboxes only, so a Drive is made with a name.
| Tool | What it does |
|---|---|
drive_create |
Makes a Drive of a name, a size in GiB (64 unless you say, or 5 on Free) and a region, standard or strict (an fsync in the sandbox that has it returns once the object store holds every write before it; kept for good). Only what is written is billed. |
drive_list, drive_get |
The Drives, or one in detail: size, what is written, region, the sandbox that has it and where, the node that writes it, its home (the region whose object store holds its copy) and how far a move of it is (home_move: a Drive’s home follows it to another region an hour after it got there; no tool moves it sooner, glac drives home does), the Drive a fork was made of, the database a database’s Drive is under (managed_by: its instance alone mounts it read-write, and a rename, a restore or a delete of it is refused). |
drive_cache |
Sets how much of a Drive its host keeps on its NVMe, in GiB (cache_gib; null for all of it, the default; drive_create takes cache_gib too): what is past it is read from the object store when it is read, slower, and the cache is billed by the hour as it is filled, beside what the Drive holds. A smaller cache lets go of what is past it once the store holds it; a Drive a running sandbox mounted with all of it cached keeps it so until that sandbox sleeps or stops. Every Drive the tools return says its cache_bytes and what of it is there (cached_bytes). A database’s Drive keeps its own. |
drive_attach |
Mounts a Drive in a sandbox at a path, read-write, at once; one another sandbox has is refused, and the result says which has it. With snapshot, one of its snapshots instead, read-only, which any number of sandboxes mount at once. |
drive_detach |
Takes it away again (force while a process still uses it); it keeps what it holds. With snapshot, that snapshot’s read-only mount. |
drive_delete |
Deletes a Drive and what is on it; one that is mounted is not deleted. Its snapshots stay, and the result names them. |
drive_snapshot, drive_snapshot_list, drive_snapshot_delete |
Takes a snapshot of a Drive (fs_consistent freezes the file system of the sandbox that writes it; otherwise its machine is paused for a moment, as at a power cut), lists a Drive’s snapshots (without a Drive, those of every Drive, a deleted one’s among them), deletes one. |
drive_fork |
A new Drive of a name, made of a snapshot or of the Drive as it is now: nothing is copied, and it can be mounted at once. |
drive_restore |
Brings a Drive back to one of its snapshots; one a sandbox has is refused: drive_detach it first. |
drive_versions |
The versions a Drive keeps of itself where its window is set (its history_seconds: one every 30 seconds in which it was written), the newest first; with at, the one a restore or a fork to that time takes. |
drive_backup, drive_backup_list, drive_backup_get, drive_backup_delete |
Takes a backup of a Drive (a sandbox that writes it has its file system frozen for a few milliseconds, so that the copy is at rest), kept until it expires (expires_in_days, or the Drive’s rule: 30 days unless set); lists the backups of the server’s Drives, or of one, newest first, with why each was taken (manual; daily, by the Drive’s rule; before_version, before a service’s new version); shows one; deletes one. |
drive_backup_restore |
A new Drive of a name, made of a ready backup: the size of the Drive it was taken of, in its region, restoring until all of it is back from the object store, then ready (drive_get says which), and mounted by nothing before. Where the machine it is made on cannot bring it back, the platform tries once more on another. |
The model calls a snapshot by its name or its id (dsnap-…), always with its Drive: drive_snapshot gives no name twice among a Drive’s snapshots, and behind a prefix it names each one under the prefix, making a name up (the Drive’s and the time) when none is given. A snapshot outlives its Drive: the result of drive_delete names those that remain, and they are reached by the Drive’s id. A snapshot is billed for what it keeps alone, at the Drives’ data price ($0.02 a GB-month), until it is deleted; a fork is billed by all it holds, like any Drive (a fork of a 10 GB snapshot is billed as a 10 GB Drive); what a read-only mount reads from the object store costs nothing. A restore keeps the versions in between where a snapshot or a fork keeps them, so a Drive restored to an older snapshot can be restored to a newer one after; what it held that no snapshot kept is gone.
A backup is not a snapshot: a snapshot is a version of its Drive, while a backup is a copy of its own, called by its id (bak-…), with no name, from which a Drive is made alone. A Drive’s backups keep only what changed since the one before, and a block several of them need is billed once, on the earliest: what each is billed for (stored_bytes), what deleting it would save (own_bytes) and what it shares (shared_bytes) are in its row. It is making until the object store has all of it, then ready, and billed from then at the price of what sleeping sandboxes keep ($0.02 a GB-month), until it expires or is deleted. It outlives its Drive: a new Drive is made of it whatever became of the one it was taken of. Behind a prefix, the server sees the backups of its own Drives alone, and names a Drive made of one under the prefix. A Drive whose machine copies nothing to an object store has none: drive_backup says so.
Domains
| Tool | What it does |
|---|---|
domain_create |
Registers a name of the user’s own for an app of the server’s, or for a port of one of its sandboxes; it is pending_verification until DNS shows the record the result names (a CNAME, which also leads the name here, or a TXT record), and then its certificate is issued. The user adds the record at their DNS provider. |
domain_list, domain_update, domain_delete |
The domains that lead to the server’s apps and sandboxes, with the records that prove each while it is not proven; one pointed at another app, sandbox or port; one deleted. |
Browser
Not open yet. Browser is not open to accounts yet: what follows is the server as it stands, ahead of its opening.
A session is a hosted Chromium, awake while a client of the Chrome DevTools Protocol (CDP) is connected to it and asleep, its pages kept, a few seconds after the last one leaves. The server opens and closes sessions; what is done in the browser is done by Playwright’s MCP server, connected at the session’s address (npx @playwright/mcp --cdp-endpoint <cdp_url>). These three need the platform’s control plane. Behind a prefix, the server’s sessions carry it in their names: it sees and closes its own alone.
| Tool | What it does |
|---|---|
browser_open |
Opens a session (headless unless headful), or the one of the name given, woken, with its pages, and gives its CDP address (cdp_url) and where a person watches its page live (live_url). The address is the credential: whoever has it drives the browser. |
browser_list |
The server’s sessions: name, kind, whether each is awake (or failed, with why: its browser stopped and did not come back) and how many clients are connected. No address. |
browser_close |
Ends a session for good: its browser, its pages and its connections. |
A session is billed by the Cube resources it uses, as any instance is: its CPU while it computes, its memory while it is awake and its traffic out (pricing and plans). account_usage gives a session’s use beside its sandbox.
Databases
A database is a managed Postgres, asleep when nothing asks anything of it and woken by a connection, its sessions kept. The server makes them and branches of them, and looks at them; deleting one or a branch, putting it to sleep, waking it and a new password are a person’s (glac db rm, glac db branches rm, glac db sleep, glac db wake, glac db reset-password). These four need the platform’s control plane. Behind a prefix, the server’s databases carry it in their names: it sees its own alone.
| Tool | What it does |
|---|---|
database_create |
Makes a database (PostgreSQL 18 unless version), ready in a few seconds, and gives its connection string with the owner’s password: the one time it is given, so the model keeps it where the program that connects needs it (an environment variable such as DATABASE_URL). The name of one there is gives that one, without its password. On a Free plan sleep_after_seconds is 1, fixed. |
database_list |
The server’s databases: name, version, status, whether each is awake, paused or asleep, and its connection string without the password. |
database_get |
One database and its branches (main, then those made of it), each with its endpoint, its state and its connection string without the password; with branch, that branch alone. It says the database’s rest (how long it is held paused before it sleeps, its daily wake), from when its root branch can be restored, and the gaps of its WAL archive (where the object store refused its WAL so long that it was given up; a time in one is restored only to a whole minute, from its Drive’s window): a restore is a person’s (glac db restore). |
database_branch_create |
A branch of a database: a copy of main (or of parent) as it is now, made at once whatever its size, with a PostgreSQL of its own, to try a migration or a change on the real data without touching it. It gives the branch’s connection string without the password: its roles and passwords are its parent’s, so the owner logs in with the password database_create gave. With expires_in_hours it is deleted by itself then; otherwise a person deletes it (glac db branches rm). |
A database’s history (how far back it can be restored, glac db update --history) and a restore of it in place (glac db restore: its root branch back to a time, its connections dropped, its state before kept as a branch) are a person’s: no tool does either. A Drive’s window of versions is in the SDKs and glac drives versions|history, not yet among these tools.
A database is reached on the project’s network, from a sandbox made with project_network: true (and from the project’s apps), at its name.
Managed Applications
An application of the catalogue (Rivet, say) is installed in the project as its own Cube resources, labelled with the installation and changed through it alone (Managed Applications). The server looks at the catalogue, installs, and looks at the installations; changing one and deleting one, which takes its Volumes and buckets with what they hold, are a person’s (glac installations update, glac installations rm). These five need the platform’s control plane. Behind a prefix, the server’s installations carry it in their names: it sees its own alone.
| Tool | What it does |
|---|---|
catalogue_list |
The catalogue’s applications, each with what it is, its licence, whether the platform packaged it, and what a month of it comes to in Cube’s meters. |
catalogue_get |
One application (app, version): the parameters an installation of it takes (a JSON Schema), its outputs, and its month in Cube’s meters. |
installation_create |
Installs it (app, name, version, region, parameters, requires), waits for it up to the server’s wait, and gives its outputs, the secret ones too, which the model keeps where the program that uses it needs them. |
installation_list |
The server’s installations: application, version, status, region, what each is made of and its outputs. |
installation_get |
One installation: its status (and why, where it failed), its apps, Volumes and buckets, and its outputs (a secret one hidden where the server’s key may not manage the project). |
The project and the account
| Tool | What it does |
|---|---|
project_get, project_set_variable |
The project and its events, each in a few words (among them a Browsers session that failed after it had run; a database made, deleted, branched or restored, its WAL archive failing and archiving again, and a gap in it, opened and closed; and of the others, what each is of), and its shared variables, which an app’s env refers to: a control plane’s. project_get also shows the project’s spend limit and what it spent this month, its webhooks, its API keys and where the logs of its apps are exported (never a secret, a key or a header’s value; webhooks and keys with a key that has the admin scope), which a person sets up: no tool makes or changes them. Asking for one of the account’s limits to be raised is a person’s too (glac limits request). |
project_secrets |
The project’s secrets: names and hosts only, never values. |
project_deploy |
Deploys several of the server’s apps together, as one deploy of the project (/v1/deploys): each is given what cube_deploy takes of a version ({}: as it is), and they are taken in the order of what each waits for and switched together. Waits for the deploy a while, and says each app’s version and how it stands, or which app it failed at and why. A control plane’s. |
project_deploys |
The newest deploys of the server’s apps; or one, with each app’s version, how long each step took and why it failed if it did (wait: once it is over). A control plane’s. |
project_audit |
A page of the project’s audit log, the newest first: every call that was no reading, a refused one too, with when, who (a key by its name, a sandbox’s token, a user of the console, the platform, an operator), the method, the path, the status and where it came from; and every reading of an app’s log by an operator. Takes a key with the admin scope. A control plane’s. |
account_billing, account_usage |
The plan, the bill and the usage: read only. The bill also has what the key’s project keeps of volumes and Drives by the numbers a create is held to (keeps; a person asks for more), the account’s billing address and tax id, and says whether a purchase of the account needs the buyer’s express request to have it at once (a consumer’s in the EEA or the UK, who may withdraw within 14 days), in the request’s words. |
account_statement |
A month’s statement (period, YYYY-MM; this month’s unless given): each meter’s line, what it comes to at the list prices, what the usage past what Paid’s periods include comes to (taken from the balance as far as it goes), the parts of the month, the fees, the Credits debited and written off, what the account held at the month’s end, what each project used, and each database’s own line (what it used comes to at the list prices, its history and its tiered data apart). Read only. |
account_documents |
The invoices at Stripe (with the PDF’s link, and of a charge given back, its credit note), the mail the account was sent, and what it may still withdraw from as a consumer in the EEA or the UK, and the withdrawals it made. Read only: a withdrawal is the buyer’s own statement, never an agent’s (glac billing withdraw). |
account_settings |
Where the account’s notices go, the language they are written in, and its own cap on what it sends to the internet in a month: shown, and changed when the person you work for asks. It says when the address is not verified yet (email_verified: no notice goes to it until the link mailed to it is opened) and when mail to it bounced (email_undeliverable: none goes to it until the address is set again). |
A few rules the server keeps:
- State follows the name. A sandbox the model names stays the model’s, with its files, packages and processes, across conversations.
- A failed command is a result, not an error: a non-zero exit code, a time-out or a signal comes back as a normal result with the exit code on its first line. A missing sandbox, an API that cannot be reached or wrong arguments are errors, with what to do.
- Long output is cut in the middle: at most 16 384 bytes a call, the start and the end, with the call that reads the rest.
- A call waits at most 45 seconds; a command still running comes back as
runningwith its id, andsandbox_outputwaits on. - Paying is a person’s job. There are no tools to top up, change the plan, turn on auto reload, withdraw from a purchase or store a secret; the results say which
glaccommand does it. Where the account buys as a consumer in the EEA or the UK, a purchase needs the buyer’s own express request to have it at once: that request is theirs to give (--start-nowon theglac billingcommands, or the box on the console’s billing page), never an agent’s, and the commands the results name carry--start-nowthere. - Secrets stay out of reach of the model: an app deployed here (
cube_deploy,project_deploy) gets no secrets or files from this server, itsenvrefers to shared variables alone, and an image pull secret goes only to the registry configured for it (GLAC_MCP_IMAGE_PULL_SECRETS=GHCR=ghcr.io,HUB=docker.io).
Settings
| Variable | What it sets | Unless you say |
|---|---|---|
GLAC_API_URL, GLAC_API_KEY |
The API’s address and key | https://api.glac.ai; no key |
GLAC_MCP_IMAGE |
The image of the sandboxes it makes | The platform’s default image |
GLAC_MCP_IMAGE_PULL_SECRETS |
Pull secrets the model may name, each with its one registry | None: public images only |
GLAC_MCP_PREFIX |
A prefix for the names of everything it makes (sandboxes, snapshots, Drives, apps); it sees only those | None |
GLAC_MCP_WAIT_SECONDS |
How long a call waits for a command | 45 |
GLAC_MCP_OUTPUT_BYTES |
How much output or file a call returns | 16384 |
GLAC_SSH_GATEWAY |
The SSH gateway, for the ssh command lines it writes |
None |
GLAC_MCP_LOG |
1 logs every tool call on stderr (over HTTP, every request too) |
Off |
GLAC_MCP_HTTP_ORIGINS |
Over HTTP: the hosts of web pages a browser may call it from, besides this machine | None |
GLAC_MCP_PUBLIC_URL |
Over HTTP: its address as clients reach it, named in its protected resource metadata, and what an access token’s audience has to name | http://<listen address>/mcp |
GLAC_MCP_AUTHORIZATION_SERVER |
Over HTTP: the authorization server whose access tokens it takes besides keys (its issuer), named in its protected resource metadata | None: keys only |
It is built on the official MCP TypeScript SDK, and speaks the protocol of 2026-07-28 and, for older clients, the initialize handshake of 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05: one revision a connection, the one its client opens it with. Not there yet: tools for volumes (the disks underneath Drives), a terminal (pty), a sandbox’s own environment, and making a sandbox from a snapshot.