Synsema docsENES

Capacidades e intent

Synsema es deny-by-default. Nada toca la red, el filesystem, la base de datos, los secretos ni la shell salvo que declares la capacidad con require. Si la olvidás, la operación no corre — el intérprete la rechaza, aunque el código la pida.

Esto es lo que hace seguro por construcción correr código no confiado (la salida de un LLM, un playground de docs).

capabilities.syn
-- Doc example: deny-by-default capabilities + faithful scope.
-- Uses `secret` because it proves the model with no network/disk side effects.
intent: "doc example: capabilities and intent"
require secret("APP_*")          -- name-prefix scope: covers APP_KEY, APP_DB, ... only

task read_app_key()
    -- APP_KEY is under the declared APP_* scope → allowed (still redacted, as always)
    give text(secret("APP_KEY", "demo")) == "secret(APP_KEY)"

task read_unscoped()
    -- DB_PASSWORD is NOT under APP_* → denied at the capability check (before any use)
    give secret("DB_PASSWORD")

print("APP_KEY is in scope → " + text(read_app_key()))

test "a capability you declared (in scope) is allowed"
    assert(read_app_key())

test "anything outside the declared scope is denied (deny-by-default)"
    assert_error(read_unscoped)

Declará lo que necesitás

require net("api.example.com")    -- un host
require file.read("/data/*")      -- solo lectura, bajo /data
require db("./store.db")          -- esta base de datos
require secret("STRIPE_KEY")      -- este secreto

Un fetch a cualquier host que no declaraste se bloquea. Un read_file fuera del scope se bloquea — el scope de ruta es fiel: un escape con .. se normaliza y se deniega.

Auto-otorgadas vs. a declarar

Bajo run/test, solo se auto-otorgan stdout / time / llm. Todo lo demás — net, file, db, secret, exec, serve, reveal, y random — es deny-by-default (sí, random también: es para tokens/nonces). Bajo serve, incluso esas se exigen.

Scopes

Capacidades por task

Una task puede declarar su propio require más acotado — solo alcanza lo que declara, aunque el programa sea más amplio:

task fetch_orders()
    require net("api.shop.com")
    give fetch("https://api.shop.com/orders")    -- SOLO puede llegar a api.shop.com

sandbox

Un bloque sandbox despoja todas las capacidades del cuerpo no confiado que contiene — un require adentro es un no-op.

Techo del host — --sandbox / --cap-set (ejecutar código que NO confiás)

require, el scope por-task y sandbox asumen que vos escribiste el código. Cuando no fue así — correr un programa generado por un LLM, el plugin de un usuario, o un playground público — el host (quien corre synsema) impone un techo que el código no puede exceder, declare lo que declare:

synsema run  --sandbox program.syn                       # techo = stdout + time solamente
synsema run  --cap-set "stdout,db=:memory:" program.syn  # un techo a medida
synsema test --cap-set "stdout,time,random,secret" tests.syn

Tres capas — quién restringe, y cuándo:

CapaQuién restringeUsar cuando
require cap("scope")el código declara lo que necesitaconfiás en el código
bloque sandboxel código aísla una parte de sí mismoconfiás en el código
--sandbox / --cap-setel host impone un techo desde afuerano confiás en el código

--sandbox vs --cap-set:

La regla: caps_efectivas ⊆ require ∩ techo. Un require net("") bajo --cap-set "net=api.mock" obtiene nada — el código nunca sube por encima del techo. Solo resta*; los auto-grants (stdout/time/llm) también se filtran, y se propaga a los agentes spawneados y a los workers de parallel_map.

Acotá tu file/db o das de más: un --cap-set "…,file" pelado deja al código leer cualquier ruta absoluta — usá file=scratch_* (un prefijo) o db=:memory: para que solo toque lo que querés.

El playground de este sitio usa exactamente esto: cada Run/Test corre con un techo que permite cómputo, secret, SQL en memoria y archivos scratch, pero deniega exec y net real — probá un snippet con require exec(...) y tocá Run. Para un deploy público, combinalo con un contenedor de SO (defensa en profundidad).

intent

intent: "Leer datos de clientes y generar reportes"

intent es descriptivo (cualquier idioma) y se congela al arrancar — una inyección de prompt no puede ampliarlo. La seguridad viene de las capacidades, nunca de la prosa. Mirá Secretos para credenciales.