Sandbox combined mode (TUI + server) instead of skipping entirely
Prerequisites
- I am running the latest code.
- I searched using keywords relevant to my issue to make sure that I am creating a new issue that is not already open (or closed).
Feature Description
Follow-up to #930. Combined mode (the default llamafile -m model.gguf, TUI chat + HTTP server in one process) currently skips pledge()/SECCOMP sandboxing entirely — see sandbox: disabled in combined mode; use --server for a sandboxed server in tools/server/server.cpp (patched via llama.cpp.patches/patches/tools_server_server.cpp.patch). The stated reason is that the in-process TUI acts as an HTTP client and needs connect(), which the server's default accept-only (anet) promise forbids.
That reasoning only explains why anet alone doesn't work — it doesn't require skipping the sandbox altogether. The same codebase already has a mechanism for this: needs_outbound relaxes anet → inet for other connect()-needing features (--rpc, server-side tools, the MCP proxy), keeping writes/exec blocked. Combined mode looks like it fits the same pattern.
Motivation
Investigation (see thread with @claude, and design notes in docs/security.md / the sandboxing project memory) suggests combined mode is sandboxable:
- The TUI thread is spawned via
pthread_createinside theon_readycallback inllamafile/main.cpp(combined_main), which only fires afterctx_http.start()— i.e., after the point where pledge() would already be installed on the main thread. Cosmo's per-thread filter inheritance rule means the TUI thread would automatically inherit whatever promise set is installed pre-start(), the same way server worker threads do today. - Tracing the combined-mode chat commands (
llamafile/chatbot_comm.cpp,chatbot_backend.h,chatbot_file.cpp):/dumpis already disabled in API/combined mode (supports_dump()defaultsfalse, onlytruefor the standalone--chatdirect-model backend inchatbot_direct.cpp), so thewpath/cpathrequirement standalone--chathas doesn't apply here./uploadonlystat()s + reads a file (purerpath, no writes)./push,/pop,/clear,/undo,/forget,/stats,/contextare in-memory only, no filesystem I/O.
- So the promise set combined mode would need is approximately
stdio inet rpath tty(pluswpath cpathonly if the server side already needs them for slot-save/prompt-cache) — no more permissive than what standalone--chatalready runs with today, unsandboxed in that one dimension.
Possible Implementation
- Extend
needs_outbound(llamafile/sandbox.c) to also trigger whencombined_modeis true, soanetrelaxes toinet. - Fold
ttyinto the promise string built for the combined-mode case (mirroringchatbot_main.cpp's own"stdio rpath wpath cpath tty"for standalone--chat). - Wire this through the
tools_server_server.cpp.patchcall site, which currently just logs a skip warning whencombined_modeis true, instead of building a real spec. - Note: GPU mode still forces a total skip independently (
sandbox_skip_status()), so this only changes behavior for combined mode run with--gpu disable.
This is currently tracked as a known deferred gap (not a router-mode-style permanent no-op) in the sandboxing design notes from #930's implementation.
Source: mozilla-ai/llamafile