Skip to main content
Mibyan runs shell commands through a pluggable set of terminal backends. The built-in backends (local, Docker, Singularity, Modal, Daytona, Vercel Sandbox, SSH) live in the core repo under tools/environments/. Third-party sandbox vendors integrate as plugins instead — a standalone plugin repo installed under ~/.mibyan/plugins/, registering a backend the user selects exactly like a built-in one via terminal.backend in config.yaml. This page mirrors the Browser Provider Plugins guide — same registration flow, same scope semantics.

What a provider controls

A registered backend automatically participates in every core surface: Declaring these flags on the provider closes the classic “new backend missed classification site N” bug class — the core consults the registry at each site instead of a hardcoded list of names.

Minimal provider

~/.mibyan/plugins/acmebox/__init__.py
~/.mibyan/plugins/acmebox/plugin.yaml
Enable it, select it, run:

Rules

  • Reserved names. Registrations that collide with a built-in backend name (local, docker, singularity, modal, managed_modal, daytona, vercel_sandbox, ssh) are rejected. Plugins extend the backend set; they never shadow in-tree backends.
  • create_environment must accept **kwargs and ignore unknown keys — the forward-compat contract that lets the factory signature evolve without breaking older plugins.
  • is_available() / probe() must be cheap. No network calls — they run during requirement checks and UI paints.
  • Fail-soft everywhere. A provider attribute that raises is treated as its default by the core (e.g. a raising skip_container_guards keeps the approval layer ON). Don’t rely on exceptions for control flow.
  • Secrets belong in strip_env_keys. Your vendor token must never be readable by a model-authored shell command; listing it strips it from every spawned subprocess unconditionally, like the built-in MODAL_* / DAYTONA_API_KEY handling.

Environment object contract

create_environment() returns an object satisfying the same duck-typed interface as tools.environments.base.BaseEnvironment:
  • execute(command, timeout=None, ...) → {"output": str, "exit_code": int}
  • cleanup() — release resources; called on session teardown / idle reaping
  • Optional: persistence hooks mirroring the built-in cloud backends
Subclassing BaseEnvironment is recommended (you inherit the shared file-sync and background-process plumbing) but not required.

Session isolation semantics

If your sandbox is resumed by name (a durable VM the backend re-attaches to), set session_isolated_when_nonpersistent = True. With terminal.container_persistent: false, each session then gets its own sandbox identity instead of sharing one — without this, two independent ephemeral runs could attach one live VM and delete it out from under each other.