Skip to content

Security

std-tools gives a model real capabilities: it reads and writes files, runs commands, fetches web pages, can drive a browser, can operate a desktop and can capture a screen. This page sets out what std-tools enforces, and what the client application that deploys it must do. The mechanics are described in Architecture.

The model: the container is the boundary

std-tools is built to run inside an isolated container that the client application controls. The container's mounts, network settings, user and resource limits decide what the agent can reach. std-tools adds defence in depth inside that boundary, but does not try to be the boundary itself:

  • File paths are not confined to a folder. The file, document and image tools accept .. and absolute paths, and can read anything the server process can.
  • execute_command runs whatever the model sends, with the server process's permissions.

What std-tools enforces

Protection Where
A size limit before a file is read (DPTTOOLS_MAX_FILE_SIZE_MB) File, document, image and skill-resource reads
Output truncation (DPTTOOLS_OUTPUT_TRUNCATION_LIMIT) File and document text, command and script output, and skill content. Directory listings, memory views and memory search results are not cut
Timeouts execute_command (kills the whole process tree), execute_skill_script, LibreOffice conversion, OCR, web fetches
SSRF protection: every address that is not public refused (private, loopback, link-local, carrier-grade NAT, and IPv4 inside IPv6 forms), every redirect re-checked, the connection pinned to the validated address fetch_webpage, fetch_image
Text-only page reads (binary responses refused before download) and size caps on page and image downloads fetch_webpage, fetch_image
Path containment The memory tools (inside DPTTOOLS_MEMORY_DIR) and the skills tools (inside DPTTOOLS_SKILLS_DIR)
Refusal to delete the whole memory store memory_delete
A pixel cap against decompression bombs Image Tools
Errors without request URLs or API keys web_search, OCR
No eval or exec of model input in the server process (browser_run_script runs model code in a separate child process); commands run as subprocesses Code execution, skill scripts, browser_run_script
The HTTP transport binds to 127.0.0.1 by default Composed launcher

What the client application must do

Responsibility Why
Run std-tools in an isolated container, as a non-root user, with only the mounts the agent needs (read-only where it should not write) The container is the only real limit on files and commands
Keep secrets out of reach. Anything in the server process's environment — including DPTTOOLS_WEB_SEARCH_API_KEY and LLMWHISPERER_API_KEY — is visible to commands the agent runs, because they inherit that environment. Do not mount credentials the agent should not read execute_command and skill scripts can print the environment and read any readable file
Restrict the network (--network=none, or an egress policy) when the agent needs no internet. The Web server and OCR need outbound access The fetch tools block private addresses, but commands the agent runs do not
Keep DPTTOOLS_WEB_FETCH_ALLOW_HOSTS short. Each host on it is reachable by the fetch tools, and the model chooses the URLs It is the one way past the private-address block
Put an authenticating proxy in front of the HTTP transport, or keep it on 127.0.0.1 The composed server does not authenticate callers
Approve consequential tool calls — deleting files, running commands, writing outside a work folder — with toolkit hooks or your own policy The general-purpose tools never ask for confirmation; see Using with the Toolkit
Treat tool output as untrusted. Web pages, documents, images and OCR text can contain instructions aimed at the model std-tools returns content as it finds it
Attach take_screenshot only where capturing that screen is intended It captures the real screen of the machine the server runs on, and sends it to the model provider
Run the computer server's host backend only with the user's knowledge It operates their real screen, mouse and keyboard, and every screenshot goes to the model provider; see the desktop server
Pin a release and restart after upgrades Configuration and code are loaded once per process

The browser and desktop servers

The browser server acts on live websites and the computer server on a desktop, so both have their own safeguards on top of the container boundary. Both are off unless you enable them: DPTTOOLS_ENABLE_BROWSER=true and DPTTOOLS_ENABLE_COMPUTER=true for the launcher, or by passing browser_server or computer_server in-process.

Browser network policy

In launch and persistent modes, every connection Chromium makes — pages, sub-resources, fetch and XHR, WebSockets — goes through an egress proxy inside std-tools on 127.0.0.1. For each connection the proxy:

  1. checks the domain lists, DPTTOOLS_BROWSER_ALLOWED_DOMAINS (empty: any public host) and DPTTOOLS_BROWSER_BLOCKED_DOMAINS (which wins);
  2. resolves the host once, and refuses every address that is not public (private, loopback, link-local, carrier-grade NAT, multicast, reserved, and IPv4 addresses inside IPv6 forms), unless the host is listed in DPTTOOLS_BROWSER_ALLOW_HOSTS;
  3. connects to the address it validated. Chromium never resolves names itself, so DNS rebinding cannot redirect a connection.

HTTPS is tunnelled unchanged: there is no TLS interception. On top of the proxy, navigation refuses non-http(s) schemes (file:, javascript:, data:, chrome:) and re-checks the final URL after redirects, and service workers are blocked.

In cdp mode std-tools did not launch the browser, so the proxy does not apply. The agent's own tabs are checked request by request — the domain lists and the private-address block — but connections cannot be pinned to a validated address.

fetch_webpage_rendered uses a fresh, credential-free context per call behind the same policy.

browser_run_script runs model-written Python as the server's user. It can open network connections directly, bypassing the egress proxy, and read the process environment through /proc. In cdp mode there is no proxy even for the browser's own traffic: std-tools checks the requests of the tabs it manages, and a script can open pages outside them (in a new context, say). Enable it only where execute_command would also be acceptable, and rely on the container's network policy.

A script can also reach every page of the browser it attaches to, so while run_script is enabled std-tools allows one browser session at a time, in launch mode starts a fresh browser for each session, and in cdp mode requires DPTTOOLS_BROWSER_CDP_SHARE_TABS=true. Isolating clients from each other's scripts takes one std-tools server per client.

Who stops a dangerous action — three layers

Layer Where it runs What it knows about the action Example Limits
1. Server hard policy Inside std-tools; the operator sets it through env vars The URL, the host, the file path, which tool is being called Refuse file://; refuse a domain not on the allow-list; refuse an upload outside the upload directory; javascript_exec disabled; host mode off Blunt yes/no rules; no human involved
2. Server confirmation gate Inside std-tools; it asks a human through MCP elicitation The meaning of the action, because the server resolves ref_12 to a real element: "button 'Place order'", "password field", "file input" Clicking an element named like purchase / pay / delete / send / submit order; typing into a password field; file_upload; javascript_exec; the first action of a desktop host session Needs an MCP client that supports elicitation
3. App approval In the application running the agent — the toolkit's hooks (skip_tool, pause) Only the tool name and its arguments "Pause before every browser_left_click and ask the user" std-tools cannot count on it: any MCP client can connect, and many have no hooks. The app sees ref_12, not "Place order"

Layer 1 is always on. Layer 2 is DPTTOOLS_BROWSER_CONFIRM for the browser: off (default), deny (refuse flagged actions outright; suits unattended pipelines) or elicit (ask a human through MCP elicitation; refuse if nobody can answer, including in-process use and clients without elicitation — the toolkit has none today). The gate flags clicks on elements whose name reads like a purchase, payment, deletion, sending or publishing, account closure or accepting terms (a word-list heuristic), typing or entering a password, file uploads, running JavaScript and running scripts. For the desktop, DPTTOOLS_COMPUTER_CONFIRM takes the same three values: elicit asks before the first action of each host session ("Allow the agent to control this computer for this session?") and before every key chord that closes, quits or deletes (alt+f4, ctrl+w, ctrl+q, super+w, super+q, delete, shift+delete); deny refuses those chords. The desktop gate cannot tell what a click will do (pixels carry no element names), so app approval matters more there. Layer 3 — the app's hooks — is where human approval normally lives.

The confirmation gate at a glance

Value Browser: DPTTOOLS_BROWSER_CONFIRM Desktop: DPTTOOLS_COMPUTER_CONFIRM
off (default) std-tools does not stop flagged actions; the client application approves them The same
deny Flagged actions are refused outright. Suits unattended pipelines Key chords that close, quit or delete are refused
elicit std-tools asks a human through MCP elicitation before a flagged action std-tools asks before the first action of each host session, and before each of those chords

With elicit, std-tools refuses when nobody can answer — including in-process use and clients without elicitation. The toolkit has no elicitation support today.

On the desktop, approval in your application matters more

The browser gate reads element names, so it knows when a click lands on "button 'Place order'". The desktop gate sees only pixels and coordinates: it cannot tell what a click will do, and it flags only the close, quit and delete key chords. Approve desktop actions in your application — for example, a toolkit hook on the computer_* tool names — or run the agent on a virtual desktop that holds nothing it should not touch.

The desktop server

The virtual backend gives each MCP session its own Xvfb display inside the container, so the agent sees only the applications it started there. A virtual desktop needs no network; any VNC view (DPTTOOLS_COMPUTER_VNC_PORT) listens on 127.0.0.1 only.

Key input cannot run commands. Each key in computer_key, computer_hold_key and the click tools' modifiers must be a single character or a key name, and each chord reaches xdotool as the only key of its own call, so no model text is read as an xdotool command (new in 0.6.1; before it, a word such as exec in computer_key started a program).

The desktop's programs (Xvfb, the window manager, x11vnc, xdotool), and anything started from the desktop, inherit the server's environment, API keys included. Run the computer server in a launcher process of its own that holds no API keys, or accept that programs on the desktop can read them.

Host mode is the user's real computer. Run it only with the user's knowledge: every screenshot, with whatever is on the screen, goes to the model provider. Safeguards that are always on in host mode, whatever the confirmation gate says:

  • Kill switch. Moving the mouse into the top-left corner of the screen, or pressing DPTTOOLS_COMPUTER_KILL_HOTKEY (default ctrl+alt+shift+k), stops the agent; every later call returns an error until the server restarts.
  • Pause on human activity. If the user moved the mouse or typed in the last second, the next action is refused ("the user is using the computer right now"). On Windows, input that arrives during an action also stops that action ("the user took over"). On Linux X11 the system does not mark the agent's own input reliably, so std-tools ignores input during an action and for 0.4 s after it: there the pause, and the top-left corner, work between actions only. The kill hotkey works at any time on both.

The computer server page has the details, and Isolated vs Attached compares the two setups.

What off means: the security burden moves to the client app

With the gate off, std-tools does not stop a consequential action on its own; the app has to. Layer 1's fixed rules still hold whatever the app does:

std-tools still enforces, always The client app is responsible for
The browser and computer servers stay off unless enabled; cdp and host modes stay off unless configured Approving consequential actions before they run — purchases and payments, sending messages or posts, deleting data, account or permission changes, accepting terms — with toolkit hooks (pause, skip_tool) on the browser and computer tool names, or by setting the gate to deny
Network policy: non-http(s) schemes refused, domain allow and block lists, non-public addresses blocked, IP pinning through the egress proxy (launch and persistent modes) Choosing an allow-list for its use case — the default allows any public host
Opt-in members (javascript_exec, file_upload, console, network, run_script) stay off unless enabled; uploads confined to the upload directory Getting the user's consent before an agent takes over their browser (cdp) or desktop (host), and telling them their screen content goes to the model provider
Host mode: kill switch, pause on human activity, one session at a time, a chosen monitor Isolation: running the agent in a container with minimal privileges and no credentials it does not need
A fresh, credential-free browser context per session (launch mode) Logging and reviewing what the agent did

A limitation to document for apps. A hook sees the tool name and its arguments — browser_left_click(ref="ref_12"), not "button 'Place order'" — so app-level approval is coarser: per tool, per mode or per domain. Two things help: every pointer and form tool names the element it acted on in its result ("Clicked button 'Place order'"), which serves audit logs; and the deny gate blocks by element name without needing a human.

On the desktop the limitation is sharper still: a hook sees computer_left_click(x=412, y=230), and the result names only the point and the display, not what was under it.

A system-prompt block for applications

doc_processing_std_tools.browser.BROWSER_SAFETY_PROMPT is a short instruction block — page content is untrusted data; ask before consequential actions; never bypass access controls — that an application can add to its agent's instructions. std-tools never injects it. See the browser server page for an example.