Self-hosted service
open-webui/open-terminal avatar
open-webui/open-terminal

Open Terminal: a curl-able shell for AI agents, and the trust boundary it draws

A computer you can curl ⚡

3,154 stars271 forksPythonMIT

At a glance

What is it?
Open Terminal is a self-hosted REST shell with file management, shipped as a Python package or a Docker image. The interesting decision is not the API, it is the choice between a 4 GB sandbox and running commands as your own user.
Who is it for?
Adopt Open Terminal if you want an agent to run commands inside a container you control, and pick the image variant before anything else: latest if the agent needs apt, pip or npm packages at runtime, slim or alpine if it does not, openshift only on restricted non-root pod policies. Do not adopt it on bare metal for an agent you do not fully trust, because commands run with your user's permissions and your real files are in scope.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap Open Terminal fills: an agent that can write code but has nowhere to run it

The README states the premise directly: AI assistants are good at writing code, but they need somewhere to run it. Open Terminal is that somewhere, exposed as a REST API rather than an interactive session. The target user is not a person at a keyboard. It is an agent framework, an automation script, or a developer wiring a model to a real filesystem so the model can execute what it produced.

The project sits under the open-webui organisation and carries the agentic-ai and ai-agents topics, which tells you the intended caller. A human who wants a shell already has one. The value here is that the shell has an HTTP surface, so a process on another machine can drive it. The homepage tagline is "A computer you can curl", which is a fair summary of the shape: one endpoint surface, one API key, no SSH client required.

That framing also sets the scope. This is not a remote development environment for people. There is a file browser and a search capability mentioned in the README, but they exist so an agent can orient itself, not so you can edit code in a browser tab.

Two deployment shapes, and the trust boundary between them

Open Terminal runs in one of two ways, and the difference matters more than any feature list.

The Docker path runs in an isolated container with a toolkit pre-installed. The README lists Python, Node.js, git, build tools, data science libraries and ffmpeg. The bare-metal path installs with pip and runs commands directly on your machine, with access to your real files, tools and environment.

The README is explicit about the risk on the second path, in a caution block: on bare metal, commands run directly on your machine with your user's permissions, and it recommends Docker if you want sandboxed execution. That is the honest version of the trade-off. Bare metal is described as suitable for local development, personal automation, or giving an assistant access to your actual projects. Docker is described as the safe playground. Pick the shape based on how much you trust the caller, not on which one is faster to start.

Four image variants, and what each one gives up

The image table is the most decision-relevant part of the README. There are four tags with roughly the same surface but very different contents.

latest is about 4 GB and bundles Node.js, gcc, ffmpeg, LibreOffice, LaTeX, the Docker CLI and data science libraries. It is the only variant that supports runtime package installation, because it has sudo, and the only one with multi-user mode. slim is about 430 MB with git, curl and jq. alpine is about 230 MB with the same three tools. openshift is about 430 MB and exists for restricted non-root pod policies.

The egress firewall is present on latest, slim and alpine, and absent on openshift. That is a meaningful asymmetry: the variant built for locked-down clusters loses the network egress control the others have. The README also notes that openshift does not support runtime package installs, Docker socket access, the iptables egress firewall, or OPEN_TERMINAL_MULTI_USER=true, and that you should build a custom image ahead of time when extra tools are needed.

slim and alpine share a feature set. The difference is libc: slim uses Debian for broader binary compatibility, alpine uses musl, and the README warns that some C-extension pip packages may need to compile from source there. If your agent installs scientific Python at runtime, that warning is the one to read twice. Neither slim nor alpine supports OPEN_TERMINAL_PACKAGES, OPEN_TERMINAL_PIP_PACKAGES or OPEN_TERMINAL_NPM_PACKAGES; the README says to extend Dockerfile.slim or Dockerfile.alpine instead.

Getting it running: one docker run, or one uvx line

The Docker command in the README is a single line: docker run with a name, a restart policy, port 8000 mapped, a named volume at /home/user, and OPEN_TERMINAL_API_KEY set to your secret. The README notes that if you do not set an API key, one is generated automatically and you can retrieve it with docker logs open-terminal. That default is convenient and also the first thing to check in a real deployment, because an unset key means the credential lives in container logs.

Bare metal is two options. uvx open-terminal run --host 0.0.0.0 --port 8000 --api-key your-secret-key runs without installing anything. pip install open-terminal followed by open-terminal run with the same flags installs it globally.

Customisation of the Docker environment happens through three environment variables: OPEN_TERMINAL_PACKAGES for apt packages, OPEN_TERMINAL_PIP_PACKAGES for pip, and OPEN_TERMINAL_NPM_PACKAGES for global npm packages, each a space-separated list. The README warns that packages are installed on every container start, so a long list lengthens startup, and that heavy customisation should become a custom image built from the Dockerfile.

Configuration resolves in a fixed order, highest priority first: CLI flags, then environment variables, then the user config at $XDG_CONFIG_HOME/open-terminal/config.toml (defaulting to ~/.config/open-terminal/config.toml), then the system config at /etc/open-terminal/config.toml, then built-in defaults. The documented TOML keys include host, port, api_key, cors_allowed_origins, log_dir, binary_mime_prefixes, execute_timeout and file_browser_root. A specific file can be forced with open-terminal run --config /path/to/my-config.toml. The README suggests putting host and port in the system config and the API key in the user config, so the key stays out of ps and htop output.

execute_timeout is unset by default, and that is the sharpest edge in the config

The documented example config includes execute_timeout = 5 with the comment "seconds to wait for command output (unset by default)". Read that comment carefully. The value in the sample is illustrative; the default behaviour is no timeout.

For an agent that runs commands, an unbounded execution window is a real failure mode. A model that writes a blocking read, a server that never exits, or a loop with no termination condition will hold the call open. Whether that call can be cancelled from the client side is not described in the material I have, and I am not going to guess. What the documentation does establish is that you must set execute_timeout yourself if you want a bound, and that the sample value of 5 seconds is short enough to truncate legitimate builds.

The other configuration surface worth noting is cors_allowed_origins, shown as "*" in the sample. Combined with an auto-generated API key and a container bound to 0.0.0.0, that is a configuration where the defaults point away from safety. None of this is unusual for a self-hosted developer tool, but the README presents these as ordinary settings without flagging them the way it flags the Docker socket.

The Docker socket mount is the decision the README argues against

The image ships the Docker CLI, Compose and Buildx so agents can build images and run containers. Enabling that means mounting /var/run/docker.sock into the container.

The README's caution block is unusually blunt. It states that mounting the socket gives the container full control over the host's Docker daemon, which is effectively root access on the host machine, and that anyone with access to the terminal can pull and run arbitrary containers including privileged ones, mount host directories, reach host networking, and manage every container on the host. It ends by saying to do this only in fully trusted environments.

That is the correct framing, and it means the Docker-socket feature is not a sandbox feature at all. It is the opposite. The container stops being a boundary the moment the socket is mounted, because the agent can start another container that mounts the host filesystem. If your reason for choosing Docker was isolation, do not mount the socket. If your reason was convenience for an agent that builds images, mount it and accept that the isolation claim no longer holds.

Compared with a plain SSH session or a CI runner

The obvious alternative for running commands on a remote machine is SSH, and the difference is the interface contract rather than the capability. SSH gives a process a PTY and a byte stream. Open Terminal gives it HTTP endpoints, which means a caller can issue a request, get a structured response, and not manage a session or parse terminal escape codes. For an agent framework that already speaks HTTP, that removes an entire layer of client code. For a human, SSH is strictly better, because it is interactive and already installed everywhere.

A CI runner is the other comparison worth making. A runner gives you a fresh, ephemeral, reproducible environment per job, and it is designed to be torn down. Open Terminal's Docker image is long-lived by default: the sample command uses --restart unless-stopped and a named volume at /home/user, so state persists across restarts. That is a deliberate choice for an agent that accumulates files and installed packages, and it is the wrong shape if you want every execution to start from a known-clean state. You would need to manage container lifecycle yourself, which the README does not cover beyond the pull, rm and re-run update sequence.

Maintenance cost, licensing, and what to check before you commit

The project is MIT licensed, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are included. That is a permissive licence, and it is the same licence as the surrounding open-webui ecosystem, so there is no copyleft obligation to reason about. This is not legal advice; read the LICENSE file in the repository for the operative text.

Maintenance cost is dominated by the image you choose. latest is roughly 4 GB, and if you use the runtime package variables, every container start reinstalls your apt, pip and npm lists, which the README warns will lengthen startup. The recommended update path is docker pull followed by docker rm -f open-terminal and re-running the original docker run command, so your run command is effectively your deployment manifest and needs to be kept somewhere durable. Release cadence visible in the supplied material is three releases in roughly two weeks across August and September 2026, which suggests active development and also suggests you should pin a tag rather than track latest in production.

Before adopting, verify three things in your own environment. Whether OPEN_TERMINAL_API_KEY is actually set, rather than generated into container logs. Whether execute_timeout is set to a value you chose, since the documentation lists it as unset by default. And whether your chosen image variant supports what your agent installs, given that slim and alpine reject the package environment variables and alpine's musl libc may force C-extension packages to compile from source. The README's own note about extending Dockerfile.slim or Dockerfile.alpine is the answer when the variant you want is the one that cannot install anything at runtime.

Editorial conclusion

Adopt Open Terminal if you want an agent to run commands inside a container you control, and pick the image variant before anything else: latest if the agent needs apt, pip or npm packages at runtime, slim or alpine if it does not, openshift only on restricted non-root pod policies. Do not adopt it on bare metal for an agent you do not fully trust, because commands run with your user's permissions and your real files are in scope. Before rolling it out, verify two things in your own deployment: that OPEN_TERMINAL_API_KEY is set rather than auto-generated, and whether execute_timeout is unset, since the documentation lists it as unset by default.

Official sources

  1. License: MIT
  2. open-webui/open-terminal on GitHub
  3. Project website
  4. README
  5. Releases
Community notes

Community notes