Sandbox de código no confiable
El resto del modelo de seguridad (require, scope por-task) asume que vos escribiste el código. Esta página es lo opuesto: ejecutar código que no confiás — la salida de un LLM, el plugin de un usuario, un playground público. Dos herramientas: una desde adentro de tu programa, otra desde afuera.
1. El bloque sandbox — aislar una parte de tu programa
Envolvé la parte que maneja input no confiable. Adentro, se despojan todas las capacidades — nada de net/file/db/secret/exec, solo cómputo puro + print. Un require adentro es un no-op (no puede re-otorgar para escapar).
let payload be fetch("https://api.external.com/data") -- input no confiable
sandbox
let result be transform(payload) -- si `transform` es explotado, NO puede tocar nada
También es una expresión:
let clean be sandbox validate(untrusted_input) -- aislado, devuelve un valor
Usalo cuando una parte de tu código debe procesar algo peligroso pero no debe llegar al exterior.
-- Doc example: the `sandbox` block — isolate a piece of code with NO capabilities.
-- As an expression it computes and returns; net/file/db/secret/exec are stripped inside.
intent: "doc example: sandbox block"
task risky_pure(n)
give n * n
-- sandbox as an EXPRESSION: isolated, returns the value (run shows it)
print("sandbox result: " + text(sandbox risky_pure(7))) -- sandbox result: 49
test "sandbox runs pure computation and returns the value"
assert_eq(sandbox risky_pure(7), 49)
assert_eq(sandbox (40 + 2), 42)
2. El techo del host — --sandbox / --cap-set
Cuando corrés un programa entero que no escribiste, quien corre synsema impone un techo que el código no puede exceder, declare lo que declare:
synsema run --sandbox program.syn # solo stdout + time
synsema run --cap-set "stdout,db=:memory:" program.syn # un techo a medida
synsema test --cap-set "stdout,time,random,secret" tests.syn
--sandbox= el techo mínimo útil (stdout+time).--cap-set "<lista>"= vos decidís exactamente hasta dónde (nameoname=scope).- La regla:
caps ⊆ require ∩ techo— el código nunca sube por encima. Los auto-grants (llmtambién) se filtran, y aplica a los agentes spawneados y los workers deparallel_map. - Acotá
file/dbo das de más:file=scratch_*,db=:memory:.
Las tres capas juntas
| Capa | Quién restringe | Para |
|---|---|---|
require cap("scope") | el código (declara lo que necesita) | código que confiás |
bloque sandbox | el código (aísla una parte de sí mismo) | código que confiás |
--sandbox / --cap-set | el host (desde afuera) | código que NO confiás |
Se componen — gana la más restrictiva.
Patrón: un runner seguro (playground / agente)
Para ejecutar snippets que manda un usuario o un LLM, spawnealos bajo un techo en un dir efímero:
task run_snippet(code)
require exec("synsema")
require file.write("_run/*")
write_file("_run/s.syn", code)
let r be run("synsema", ["run", "--cap-set", "stdout,time", "_run/s.syn"], 5, {"cwd": "_run"})
give r["stdout"] + r["stderr"]
Sumá un timeout corto (arriba: 5s) y un cwd aislado; para un deploy público, envolvelo en un contenedor de SO como segunda capa (defensa en profundidad). Así funcionan el playground de este sitio y su tool MCP run_synsema.
Por qué importa para agentes: un agente que escribe y corre Synsema puede ejecutar su propio código bajo un techo que no puede escapar — así "correr el código que generó el LLM" es seguro por construcción. Eso hace a Synsema apto para sistemas agent-native.