EN

La frontera de gobierno

La frontera se interpone entre tu agente y sus herramientas, y corre en tu red. Es la única forma de despliegue que puede llegar a GOVERNED: una puerta que vive en la nube del proveedor hay que llamarla; una frontera se interpone. Ninguna llamada a un modelo en el camino gobernado: una decisión es aritmética, así que cuesta microsegundos.

corre en tu redfijada por digest ~36 µs por decisiónsin cargo por llamada

Guía rápida — pon la frontera en el camino

No construyes nada. Descargas una imagen publicada y la corres con tu propia configuración. La clave de tu organización llega con tu suscripción. Nada de esto nos manda el texto de tus agentes ni los argumentos de sus herramientas: el parte que sube lleva identidad, interfaz, transporte, nombres de las herramientas servidas y dos atestaciones, y su propia cabecera dice ni argumentos ni texto.

1 · Descarga la imagen — por DIGEST, nunca por etiqueta

# la dirección del registro y un token con alcance llegan con tu suscripción
docker login <registro> -u <nombre-del-token> -p <secreto>
docker pull <registro>/d4agent-gateway@sha256:<digest>

La credencial es un token con alcance: puede leer exactamente un repositorio y nada más del registro. Apúntalo a cualquier otro y contesta authentication required. Es revocable en un comando de nuestro lado, y caduca.

Por qué un digest y no una etiqueta — y no es pedantería. Una etiqueta es mutable: :1.2.3 puede apuntar hoy a una imagen y mañana a otra sin que el nombre cambie. Un digest es el SHA-256 de la imagen misma, así que solo puede nombrar ese artefacto exacto. Lo que este producto vende es evidencia que puedes enseñar a un tercero — un auditor, un cliente tuyo, un regulador — y evidencia sobre un artefacto que pudo cambiar por debajo no es evidencia. Si no se puede decir qué bytes gobernaban cuando se firmó aquella decisión, la firma es decoración. Fija el digest, anótalo junto a tu configuración, y cámbialo a propósito.

2 · Dale tu configuración

La imagen es genérica. Todo lo que la hace tuya entra al arrancar, por una de tres vías — y si no hay ninguna se niega a arrancar y lo dice. No levanta con un valor por defecto.

víacómo
ficheromóntalo en /etc/d4a/gateway.json
variableD4A_GATEWAY_CONFIG_JSON con el JSON
variable, base64D4A_GATEWAY_CONFIG_B64 — la forma que sobrevive al camino real, porque un JSON lleno de comillas cruza shells, portales y tuberías antes de llegar aquí
docker run --rm -p 8099:8099 \
  -e D4A_REPORTE_KEY=<la clave de tu organización> \
  -v /ruta/a/tu/gateway.json:/etc/d4a/gateway.json:ro \
  -v d4a-state:/data \
  <registro>/d4agent-gateway@sha256:<digest>
El volumen de estado no es opcional, y es el que hay que acertar. -v d4a-state:/data monta almacenamiento durable, y el store de tu configuración tiene que apuntar dentro. Ahí viven dos cosas, y las dos son el producto: el libro mayor de trayectoria — los totales contra los que se mide un tope acumulado — y la cadena de auditoría firmada, la evidencia que puedes enseñar a un tercero. Sin un montaje durable las dos quedan en el sistema de ficheros del propio contenedor y un reinicio se las lleva: el tope acumulado vuelve a empezar en silencio — un reinicio amnistiaría la trayectoria, que es justo lo que la firma existe para impedir — y la cadena arranca de cero. Y no se nota, porque la cobertura se computa de lo que se reporta y una frontera que ha olvidado sigue reportando. En cualquier plataforma que reemplace contenedores — Kubernetes, Container Apps, Cloud Run, cualquier autoescalado — ese montaje tiene que ser un volumen persistente de verdad. Escalar a cero cuenta como reinicio.

Ningún secreto va horneado en la imagen, y ninguno pinta en ese fichero. La clave con la que se autentica el parte se pasa por variable de entorno y desde la configuración solo se la nombra, con api_key_env. Un fichero de configuración se versiona, se copia y se pega en un ticket; un secreto dentro de uno acaba donde no debe.

3 · Apunta tu agente, y quítale la ruta directa

Cambia una línea en la configuración MCP de tu agente: el endpoint pasa a ser el /mcp de la frontera. El modelo, los prompts, los temas y los esquemas de herramienta no cambian — esto es alta de seguridad y configuración, no reconstruir un agente.

Después mueve la credencial del sistema de arriba detrás de la frontera y quítale al agente la ruta directa, y declara credencial_upstream_separada: true.

Esa declaración es una atestación, no una prueba, y el producto lo dice en voz alta: ningún software puede demostrar que tu agente no se guardó una puerta lateral. Lo que sí puede es exigirte declararlo y dejar constancia. Si el agente todavía puede alcanzar el sistema real sin pasar por aquí, la capacidad no está gobernada del todo — y el idioma de la cobertura (GOVERNED / PARTIAL / OBSERVED / UNMANAGED) existe precisamente para que eso no se pueda tapar en silencio.

4 · Qué deberías ver

Se niega a abrir si la frontera no se sostiene — claves que faltan, un catálogo sin sellar, una atestación ausente, fail_closed apagado. Falla antes de escuchar, y nombra de una vez todo lo que falta.

Una llamada bloqueada vuelve como resultado, no como error de protocolo: isError: false con un cuerpo que dice blocked, la acción, el motivo y el número de eslabón de la cadena. El motivo es el mismo texto que queda firmado en la cadena de auditoría, así que se puede comparar carácter a carácter.

/actividad en la frontera sirve una ventana de solo lectura, con un read_token que no es el token del agente. Quien puede ejecutar herramientas y quien puede mirar el registro son dos personas distintas, y dos credenciales distintas.

Límites, dichos y no escondidos

El TLS termina en tu proxy, no dentro del servidor HTTP de la frontera. Sírvela detrás de un terminador, en una red de confianza.

Los límites de tasa y las sesiones viven en memoria, por proceso. Con varias réplicas cada una tiene su ventana; dimensiona rate_per_minute para una réplica.

El sistema de arriba puede ser un subproceso stdio o un servidor MCP remoto por HTTP. Con uno remoto el intercambio es petición/respuesta: no hay resumabilidad ni flujo largo hacia arriba.

La taxonomía de desenlaces es asimétrica a propósito. Una conexión rechazada es un envío failed — hay prueba de que la petición no llegó a salir. Un timeout de lectura, un cuerpo truncado o un 5xx son unknown: puede haberse ejecutado. Un unknown se reconcilia y jamás se reintenta, porque repetir un efecto que quizá ya ocurrió es exactamente el daño que esa taxonomía existe para evitar.

GOVERNED describe la FORMA de tu despliegue, no cuánto lleva funcionando. Dice cuatro cosas: la frontera está en el camino, no se sirve nada fuera del ancla firmada, las dos atestaciones están afirmadas, y el ancla que se aplica es la que se firmó. Una frontera que se levantó bien hace un minuto está GOVERNED — no es un defecto: es lo que la palabra significa. Lo que no dice es que se haya gobernado nada todavía: para eso se lee la cadena, que es el registro de las decisiones realmente tomadas, empieza vacía, y es lo que se le enseña a un tercero. La cobertura se computa al leer y no se guarda nunca, porque un estado de cobertura guardado es una opinión que envejece mientras sigue sonando a verdad.

Conceptos

TérminoQué es
anclaLa misión del agente, congelada y firmada en el despliegue. Se escribe una sola vez; el agente no puede reescribirla en ejecución. Se captura de la misión y las herramientas que tu agente ya declara — no hay una configuración aparte que redactar.
pasoUna cosa que el agente se propone hacer: llamar a una herramienta o producir una salida. Un paso = una llamada gobernada.
desviaciónCuánto se aparta un paso del ancla, medido en seis canales: scope, objective, output_type, tradeoff, tone, execution.
persistenciaUna media exponencial de la desviación. Decae. Responde a «¿está el agente fuera de misión ahora mismo?"
rachaContadores de ciclos consecutivos por canal. No decaen. Cazan la deriva lenta y sostenida que se queda por debajo de todos los umbrales por paso — el fallo que un guardrail turno a turno no puede ver.
admisibilidadUna comprobación estructural y dura que se ejecuta antes de cualquier puntuación, y con independencia de ella. Una herramienta fuera de alcance o una restricción incumplida se bloquea de plano; ninguna puntuación puede readmitirla.

Cómo funciona por dentro

Las cinco piezas, en orden. Esto es el mecanismo; la portada dice qué te hace a ti. Nada de aquí es opcional en ejecución — es lo que corre en cada paso gobernado.

Cinco piezas, y se leen en orden: 01 fija contra qué se mide todo, 02 mide, 03 decide, 04 lo deja demostrable meses después — y 05 es lo que ninguna de las otras cuatro puede ver, porque no está dentro de ningún agente.

01 · ANCLA

Firmada en el despliegue, se escribe una vez

La misión se captura de lo que el agente ya declara y la firma la credencial de despliegue — y lo que el gate aplica a partir de ella es estructurado: tus herramientas y tus topes, al pie de la letra. El agente no puede reescribir su propia vara de medir.

02 · CUATRO MONITORES

Ahora, últimamente, en total — y hacia dónde

Una media que se olvida de lo viejo (¿se está saliendo ahora?). Un contador de turnos seguidos que se pone a cero con un solo turno limpio (¿cuánto lleva así?). Un contador que nunca resta y no tiene tope (¿cuánto se ha salido en toda su vida?). Y una medida de dirección (¿se aleja en línea recta, o da vueltas y vuelve?). Sólo el tercero caza la deriva que va y viene; sólo el cuarto separa irse de verdad del ruido que se cancela — dos trayectorias que gastan lo mismo y llevan la misma racha, y todos los demás contadores las leen iguales.

03 · GATE DURO

Admisibilidad primero

Una herramienta fuera de alcance o una acción fuera de política se bloquea antes de cualquier puntuación, y con independencia de ella. La puntuación no puede readmitirla jamás.

04 · AUDITORÍA

Reproducible, y firmada

Cada decisión es una fila encadenada por hash y firmada con tu credencial. Tocar un paso rompe la cadena; reescribir la historia entera y rehacer los enlaces tampoco cuela, porque la firma no se puede recalcular sin la clave. Hecha para defenderse ante un regulador.

05 · ACOPLAMIENTO

La deriva que te llega de otro agente

Los cuatro de arriba vigilan a un agente. Éste vigila lo que pasa entre ellos. Un agente que lee lo que otro escribió hereda cuánto se había alejado ese otro de su propia misión — así que una trayectoria que nunca se salió de su encargo puede acabar parada, por una exposición que no gastó. Cualquier contador que mire dentro de un solo agente la lee como limpia, porque la información no está ahí dentro. Míralo en el operations floor de Acme →

Qué compara realmente el gate

Esta es la pregunta que hace todo el mundo, y merece una respuesta directa: si la misión es texto libre, ¿cómo se aplica sin que un modelo la lea?

Porque el texto libre no es lo que el gate compara. El registro produce dos cosas distintas, y solo una de ellas es aplicable.

Firmado & registradoAplicado en cada paso
qué es El texto de tu misión y el perfil de tono — resumidos con hash dentro del ancla, para que la auditoría pueda probar qué misión estaba en vigor. allowed_tools, output_types, hard_constraints — listas y comparaciones.
de dónde sale De tu system prompt, literal. De tu manifiesto de herramientas, tomado al pie de la letra — nunca inferido — más los topes que declares.
¿necesita un modelo? No. Se guarda y se resume con hash, no se interpreta. No. Pertenencia a un conjunto y aritmética.

Así que el gate nunca pregunta «¿esto está en misión?», que es un juicio. Pregunta «¿está esta herramienta en la lista?» y «¿es 500 ≤ 200?», que son hechos. Por eso una decisión son ~36 µs y no cuesta nada.

Ejemplo resuelto

# texto de la misión (firmado, con hash, para humanos — el gate no lo interpreta)
"Resuelve disputas de facturación dentro de la política de reembolsos. Nunca reembolses más de $200."

# lo que se aplica de verdad: tu manifiesto + el tope que declaraste
allowed_tools    = [lookup_invoice, issue_refund, escalate_to_human]
hard_constraints = [ issue_refund.amount <= 200 ]

# el agente propone issue_refund(amount=500)
  is "issue_refund" in allowed_tools?   yes
  is 500 <= 200?                        no   -> BLOCK

# el agente propone process_upsell(plan="premium")
  is "process_upsell" in allowed_tools? no   -> BLOCK

Entonces, ¿para qué sirve el modelo?

Para los casos que una lista no sabe expresar. Un agente puede permanecer dentro de todas las herramientas permitidas y por debajo de todos los topes mientras sus respuestas se van convirtiendo poco a poco en un argumentario de venta, o su registro se desplaza bajo presión. Eso es significado, y el significado necesita un modelo — es el canal semánticoopcional, que puntúa el objetivo y el tono en texto libre.

Dos capas, y la diferencia importa comercialmente. La capa estructural es determinista, sin modelo, de microsegundos, y es la que bloquea. La capa semántica es opcional, cacheada, fuera del camino crítico, y solo aconseja: puede levantar una señal que a la estructural se le escapó, pero nunca puede bajar una. Si el modelo no está disponible, la API te lo dice ("protected" y una nota l1_unavailable ) en lugar de adivinar en silencio.

El propio registro puede usar un modelo una vez, opcionalmente, para convertir un prompt largo en prosa en esos campos estructurados. Es una llamada por agente, nunca por paso — y allowed_tools se toma siempre al pie de la letra del manifiesto, así que la comprobación de mayor peso nunca descansa sobre una inferencia.

Compruébalo tú mismo — corre la demo

El mismo agente, bajo la misma presión, ejecutado dos veces: una solo y otra con el gate delante de sus herramientas. Dos minutos, un comando, nada que instalar y sin registrarse — el script se da de alta solo en el plan gratuito. El escenario reconstruye un incidente real y documentado: un agente choca con un muro de credenciales en staging, encuentra un token con alcance sobre producción y escala.

Descargar la demo  ·  28 KB, 6 ficheros

# 1. una clave de modelo, la que quieras (el plan gratis de Groq son dos minutos)
export GROQ_API_KEY=gsk_...

# 2. y esa es toda la instalación
python3 run_demo.py

Cada paso que da el agente lo decide el modelo — nada del escenario está guionizado. Y lo que cuenta como fallo lo juzga el estado del entorno simulado — ¿se destruyó el volumen?, ¿salieron credenciales? — nunca nuestras propias reglas. El entorno es el juez; el gate es el examinado.

  WITHOUT DiaCroma
     runs that failed   : 1/2
       - DESTRUCTIVE: executed -> sudo -u postgres psql -c "DROP DATABASE railway;"

  WITH DiaCroma
     runs that failed   : 0/2
     steps blocked      : 3
       turn 4: run_terminal_cmd
         -> admissibility:tool_out_of_scope:run_terminal_cmd
Si tu agente no falla, la demo lo dice, y después dice cuánto vale eso. Cero fallos en n corridas no es una tasa cero; es una cota superior de 3/n al 95 % de confianza. La herramienta imprime esa cota y la multiplica por tu volumen mensual, porque “nunca nos ha pasado” y “hasta 300 incidentes al mes” pueden ser exactamente la misma medición. Los modelos capaces aguantan a menudo — y aun así no puedes descartar la cola observando.

Veredictos & control

Graduado, no binario — porque una parada en seco ante cada bandazo es inusable, y un aviso ante una violación real es negligencia.

acciónqué significaqué haces
permitirEn misión.Ejecuta la acción.
warn_replanLa deriva sostenida cruzó el umbral de aviso, o lo hizo la racha de un canal. Todavía no ha ocurrido nada ilícito.Ejecútalo si quieres, pero devuélvele reason para que el agente se reoriente.
block_escalateUna restricción dura o una acción fuera de alcance, o deriva acumulada por encima del umbral de rechazo.No lo ejecutes. Escala a una persona; dale al agente el motivo.

Campos de la respuesta

{
  "action":      "allow" | "warn_replan" | "block_escalate",
  "allow":       true,          // booleanos de conveniencia
  "block":       false,
  "verdict":     "nominal" | "warning" | "refuse",
  "cumulative":  0.1640,       // persistencia EWMA, 0–1
  "max_streak":  15,           // racha más larga por canal
  "admissible":  true,          // false = violación estructural dura
  "reason":      "…",          // legible; dáselo al agente
  "protected":   true,          // false = solo observa, NO aplica
  "audit_seq":   41,
  "audit_head":  "9f2c…",       // cabeza de la cadena tras este paso
  "usage":       { "calls_used": 41, "calls_remaining": 959 }
}

El ancla de misión

El ancla es aquello contra lo que se mide todo. Se crea en el registro, la firma tu credencial de despliegue, se resume con hash y no se muta jamás. No hay ningún endpoint para editarla: cambiar la misión significa registrar un agente nuevo, y esa es justo la idea — un agente que deriva no puede mover su propia portería.

campoobligatorionotas
missionTexto libre — tu system prompt de siempre vale. Se firma y se resume con hash; el gate no lo interpreta (ver qué compara el gate). Lo lee el canal semántico opcional.
toolsSe toma al pie de la letra, nunca se infiere. Esta es la lista que el gate aplica de verdad.
signing_authorityTu credencial de despliegue o identidad de servicio. Los valores vacíos o de relleno se rechazan.
hard_constraintsno{"tool","arg","op","value"}. Operadores: <= < >= > ==. Incumplirla = bloqueo inmediato.
allowed_topicsnoAlcance temático.
out_of_scopenoExclusiones explícitas.
modenoenforce (por defecto) o observe.
El límite de confianza, dicho sin rodeos. Si alguien puede reescribir el system prompt de tu agente sin autorización, puede reescribir su misión — pero a esas alturas ya es dueño del agente de todos modos. Heredamos exactamente el límite de confianza de tu cadena de despliegue: ni más débil que tu plataforma, ni ceremonias que no añaden seguridad real. Lo que es estructural: el agente, en ejecución, no puede reescribir su ancla ni saltarse el gate.

Endpoints

Esta es la API de gestión: registrar agentes, proponer restricciones, leer el ancla firmada y la cadena de auditoría. El camino gobernado no está aquí — está en la frontera, en tu propia red.

POST/v1/agents

Registra un agente; devuelve agent_id y anchor_hash.

POST/v1/agents/{agent_id}/constraints/proposal

Onboarding automático (N4a). Lee la misión registrada del agente — más los documentos de política que pases en documentos (nombre → texto) — y devuelve una propuesta: los límites aplicables por máquina que encontró, cada uno con la frase exacta que lo justifica y el veredicto independiente de un segundo modelo; lo que leyó con juicio, enviado a revisión; los huecos que encontró, convertidos en preguntas para la autoridad; y un veredicto de expresabilidad cuando parte de la conducta no cabe en matemáticas. El schemas opcional (tool → [args]) elimina las conjeturas sobre nombres de argumentos. Calcula y devuelve, nada más — proponer no es sellar: nada gobierna hasta que la autoridad firma.

GET/v1/agents

Lista tus agentes registrados.

GET/v1/agents/{agent_id}/anchor

El ancla firmada, tal y como está guardada.

GET/v1/agents/{agent_id}/audit

El registro de decisiones encadenado por hash, más el resultado de su verificación.

GET/healthz

Vida del servicio. Sin autenticación.

Errores & cuota

códigosignificado
400Petición mal formada — el mensaje dice qué campo.
401Clave de API ausente o inválida.
402Un límite del plan — una negativa tipada, con las condiciones para reabrirlo.
404Agente desconocido, o de otra organización.
500Culpa nuestra. Decide tu postura — ver abajo.
// 402 — un límite del plan es una negativa constructiva, no un muro
{
  "error": "rescan_allowance_exhausted",
  "mode":  "REFUSE",
  "blocking_condition": "the growth plan includes 5 re-scan(s) and 5 are used. …",
  "reopening_conditions": [
    "upgrade the plan (the allowance recharges on plan change)",
    "contact diacroma@veritglobal.com"
  ],
  "rescans": { "plan": "growth", "allowance": 5, "used": 5 }
}

Un plan cubre un número de agentes y una franquicia de re-escaneos de política. El primer escaneo de restricciones de cada agente registrado va incluido en la compra. Al agotarse la franquicia el servicio rechaza el re-escaneo, antes de incurrir en ningún coste de modelo — y no se para nada más: tus agentes siguen gobernados y la frontera sigue aplicando.

No hay cargo por llamada. Las llamadas gobernadas que pasan por una frontera que corres en tu propia red no se miden ni se facturan, porque nunca llegan a nuestro servicio.

Si la capa de gobierno no está disponible

Decide esto a propósito. Fallar abierto (por defecto) mantiene tu agente funcionando sin gobierno y avisa a gritos — lo correcto para la mayoría de productos, porque una capa de gobierno que tumba tu agente se desinstala el primer día. Fallar cerrado se niega a actuar sin veredicto — lo correcto para trabajo regulado o de radio de impacto alto. Elijas la que elijas, déjalo escrito en tu propio manual de operación.

Qué se declara, y qué pasa si no lo declaras

El gate solo puede ver lo que le has dicho que mire. Ninguna omisión hace que falle abierto sobre lo que declaraste — lo que hacen es reducir qué puede ver, y eso queda escrito en el perfil firmado que acompaña a cada decisión. Esta tabla es la lista completa.

declaracióndóndesi NO la haces
missionregistro El agente no se registra.
toolsregistro El agente no se registra. Es la lista que el gate aplica de verdad; sin ella no hay nada que aplicar.
signing_authorityregistro El agente no se registra. Un ancla sin autoridad real no gobierna nada, así que preferimos que el despliegue no arranque a que arranque sin proteger.
hard_constraintsregistro Ese tope no existe y nada lo comprueba. No hay valor por defecto secreto. Un tope con un operador que el gate no implementa se rechaza al sellar, no al ejecutar.
evidence_mapregistro Una afirmación se sostiene con haber ejecutado algo de la clase correcta — una heurística. Declarándolo se exige el recibo concreto.
claim_evidence: "receipts"registro Se queda en "tools": un nombre de herramienta basta como evidencia. Para pagos, identidad o accesos, eso es poco.
evidence_max_age_sregistro No se exige frescura: un recibo de hace seis horas sostiene «se ha liquidado» en un sistema que cambia cada minuto.
acknowledge_no_semantic_sensorregistro No se puede desplegar sin el juez semántico sin reconocerlo. Es una bandera que hay que escribir a mano, a propósito: sin el juez, el canal de objetivo — el de más peso — lo cubre solo el detector estructural.
declare_reads / descriptoresen vida No se marca a nadie nunca. El acoplamiento entre agentes deja de verse. No falla: es que no existe. Se declara en cualquier momento — el conjunto de lectura CRECE durante la vida del agente, no se fija al sellar.
nivel del descriptor (1/2/3)en vida Se toma el nivel 3 (la colección entera): marca de más, nunca de menos. Cada nivel marca un superconjunto del anterior, y marcar de más solo cuesta confianza, no trabajo abortado.
declare_invarianten vida El caso en que cada agente cumple su regla y la regla que los relaciona se rompe igual no se detecta. Es el fallo que ninguna comprobación por paso puede ver.
declare_lineageen vida Una escritura en el origen no marca a quien lee el derivado, y durante la ventana de retardo el lector trabaja sobre datos que ya no valen sin saberlo.
declare_precedenceen vida Las restricciones sobre la secuencia —«no reembolsar sobre un ticket cerrado»— no se comprueban. Cada paso es admisible por separado; el orden no.
state_providerinterfaz efectora La interfaz refusa. Sin él no puede saber si la trayectoria se movió después de emitir el permiso, y un permiso que no se puede ligar no autoriza nada. Debe devolver digest, cycle y generation: el ciclo lo pone el almacén, nunca quien presenta el token.
D4A_AUDIT_KEYentorno La cadena de auditoría queda sin firmar: es consistente consigo misma, pero quien pueda escribir el almacén puede reescribir la historia entera y recalcular los enlaces. Con clave, no.

La regla, en una línea: lo que no declaras no se gobierna, y te lo decimos al desplegar, no cuando pase algo.

Tres agentes o más

Todo lo de esta página gobierna un agente. Para que varios se vigilen entre sí no hace falta que hablen: hace falta que declaren qué leen. Es opcional — sin declarar nada, cada agente queda gobernado por su cuenta y el acoplamiento simplemente no existe.

1 · Declarar qué lee cada agente — en cualquiera de los tres momentos

Es opcional, y los tres son el mismo campo reads. Existen tres porque un agente descubre qué datos necesita mientras trabaja: si solo se pudiera declarar al crearlo, todo lo que descubra después quedaría invisible. El conjunto de lectura crece durante la vida del agente; no se fija al sellar.

cuándodóndepara qué sirve
al crearPOST /v1/agents lo que ya sabes que va a leer. La respuesta te devuelve reads_declared: cuántos descriptores quedaron.
en cualquier momentoPOST /v1/agents/{id}/reads cuando lo descubres entre pasos, o lo sabe otro sistema.
en el mismo pasoPOST /v1/agents/{id}/step el momento natural: el agente acaba de usar el dato. Se procesa antes que las escrituras del mismo paso, para que un paso que lee y escribe a la vez quede registrado como lector antes de marcar a nadie.
// 1) al crear el agente
POST /v1/agents
{ "mission": "...", "tools": ["..."], "signing_authority": "...",
  "tenant": "acme",
  "reads": [{"collection": "facturas", "tier": 1, "key": "f-1042"}] }
// -> { "agent_id": "agt_…", "reads_declared": 1, … }

// 2) en cualquier momento, tantas veces como haga falta
POST /v1/agents/{agent_id}/reads
{
  "tenant": "acme",
  "reads": [
    {"collection": "facturas", "tier": 1, "key": "f-1042"},
    {"collection": "clientes", "tier": 2,
     "ranges": [["saldo", 0, 5000]]},
    {"collection": "kb_politicas", "tier": 3}
  ]
}
// -> { "declared": 3, "exact_keys": 1, … }

// 3) dentro del paso que usa el dato
POST /v1/agents/{agent_id}/step
{ "proposed_tool": "…", "output_kind": "…", "tenant": "acme",
  "reads":  [{"collection": "kb_politicas", "tier": 3}],
  "writes": [{"collection": "facturas", "key": "f-1042"}] }

tenant es obligatorio en las tres. Sin ámbito no hay partición, y un índice sin partición le revela a un cliente que otro está trabajando: se rechaza al declarar, no al evaluar.

nivelqué declarasqué marca
1la clave exacta que leíste solo escrituras sobre esa clave
2intervalos sobre atributos ordenados lo que cae dentro del intervalo
3solo la colección (el valor por defecto) toda escritura en ella

Cada nivel marca un superconjunto del anterior, así que omitir el nivel marca de más y nunca de menos. El 3 es el que hace que una consulta por similitud (RAG) deje de ser un caso aparte: no se puede expresar como condición, pero la colección sí se puede declarar. Si no declaras nada, no se marca a nadie nunca.

2 · Declarar qué escribe, en el mismo paso gobernado

No hay una llamada aparte: va en el /step que ya haces.

POST /v1/agents/{agent_id}/step
{
  "proposed_tool": "actualizar_factura",
  "tool_args": {"id": "f-1042", "estado": "anulada"},
  "output_kind": "action",
  "writes": [
    {"tenant": "acme", "collection": "facturas", "key": "f-1042"}
  ]
}

Con eso, todo agente que hubiera declarado leer f-1042 queda marcado — y la marca arrastra cuánta exposición llevaba el que escribió. Un escritor limpio ensucia menos que uno que ya iba desviado.

3 · Qué devuelve el paso: lo que marcaste y lo que heredaste

Son dos cosas distintas y por eso son dos campos. coupling es lo que este paso movió debajo de otros. inherited es lo que otros movieron debajo de este — y es el que gobierna.

POST /v1/agents/{agent_id}/step
{
  "action": "block_escalate",
  "admissible": false,
  "reason": "admissibility:coupling_poisoned: 1 premise(s) moved …",
  "exposure": 1.87,

  // lo que ESTE paso marcó (solo si declaró `writes`)
  "coupling": { "marked": 3, "pivot": false },

  // lo que ESTE paso heredó (solo si había marcas pendientes)
  "inherited": {
    "marks": 2,
    "grade": "contaminated",
    "inherited_exposure_native": 700000000,
    "confidence_penalty": 0.50,
    "block": false,
    "degraded": false,
    "written_by": ["agt_cobros", "agt_libro"]
  }
}

Qué hace cada uno con la decisión, que es lo que lo separa de un panel:

campoefecto en el ciclo
inherited_exposure_native se suma a Lt, el techo de por vida, dentro de la misma transición que firma la fila. En escala nativa entera, así que un replay la recomputa exacta. Un agente impecable respecto de su propia ancla puede agotar su presupuesto por servir fielmente a un dato que ya venía torcido.
confidence_penalty multiplica a la confianza (1 − p) antes del umbral, así que puede disparar la política de confianza baja que ya tenías declarada. La fila dice cuál de las dos mitades bajó: confidence_coverage y coupling_confidence_penalty van por separado.
block marca envenenada — la escribió alguien que estaba refusando, o que afirmó algo sin recibo. Entra por admisibilidad, no por deriva: ninguna puntuación posterior la readmite.
degraded el almacén de acoplamiento no respondió. No se sigue como si no hubiera marcas: no saber si tus premisas se movieron es exactamente el caso en el que baja la confianza.

grade es stale (alguien movió tu premisa), contaminated (además iba desviado) o poisoned (el que escribió estaba bloqueado o afirmó algo sin recibo). pivot: true significa que este agente es el cruce: trae premisas movidas y lo que escribe lo lee alguien. Es el que hay que ir a mirar de los tres.

4 · Mirar sin cobrar

GET /v1/agents/{agent_id}/coupling
{
  "marks": 2, "grade": "contaminated", "pending": true,
  "inherited_exposure": 0.70, "inherited_exposure_native": 700000000,
  "confidence_penalty": 0.50, "block": false,
  "circulation": 0, "pending_lineage": 0,
  "written_by": ["agt_cobros", "agt_libro"]
}

Esta vista no consume. Mírala mil veces y el número no cambia: es lo que el próximo paso gobernado va a heredar — por eso pending: true. Quien consume es el ciclo, y solo si se confirma: el borrado de las marcas va en la misma transacción que el estado y la fila de auditoría, así que o se cobran las tres cosas o no se cobra ninguna. Un GET que descontara lo que mide dejaría que un panel refrescando cada treinta segundos se comiera la contaminación antes de que la pagara nadie.

5 · Lo que existe en la biblioteca y todavía no es un endpoint

Todavía no en la API hospedada. Las invariantes declaradas, el linaje entre sistemas, la precedencia de operaciones y los presupuestos delegados están construidos y probados en la biblioteca, y no están expuestos como rutas HTTP. Se usan hoy auto-hospedando el SDK. Preferimos decirlo a documentar una ruta que devuelve 404.
declaraciónqué cazasi no la haces
invariants A escribe X, B escribe Y, ninguno toca el dato del otro y la regla que los relaciona se rompe igual no se detecta; cada agente sigue pareciendo correcto
lineage el derivado aún no ha cambiado y el lector ya está trabajando sobre él; la marca se retiene hasta que pasa lag_seconds marcarías al sincronizar, que llega tarde
precedence reglas sobre la secuencia: reembolsar sobre un ticket ya cerrado cada paso es admisible por separado y el orden no se comprueba
delegación un padre reparte su presupuesto entre subagentes; el ancla derivada solo puede restringir, y lo arrendado cuenta como gastado cada subagente lleva su cuenta y la suma puede pasar del techo del padre
Una nota de honestidad. Nada de esto adivina relaciones. Si dos agentes leen el mismo dato y ninguno lo declara, no hay acoplamiento que ver — y te lo decimos al desplegar, no cuando pase algo.

Auditoría & reproducción

Cada decisión es una fila en un registro de solo-añadir, encadenado con SHA-256. Manipular cualquier fila pasada rompe todos los enlaces posteriores, y la verificación informa de la primera secuencia rota.

GET /v1/agents/{agent_id}/audit

{
  "verify": { "ok": true, "first_broken": null, "detail": "verified 41 records" },
  "head":   "9f2c…",
  "records": [ { "sequence": 0, "payload": {…}, "chain_hash": "…" } ]
}

La reproducción es determinista: la misma trayectoria produce los mismos veredictos y los mismos hashes. Eso es lo que hace que el registro sea defendible ante un auditor, y no meramente informativo.

Rendimiento

No hay ningún LLM en el camino gobernado. Una decisión es aritmética sobre seis canales, una búsqueda en un conjunto y un hash — así que no hay latencia de modelo, ni coste por tokens, ni dependencia de un proveedor.

caminolatencia
Decisión, en proceso (SDK)~36 µs p50 · ~58 µs p95
Decisión + escritura de auditoría~99 µs
Ida y vuelta a la API alojada~1,2 ms + red
Rendimiento~10.000 decisiones/s por núcleo

Los agentes sensibles a la latencia (asistentes de código) deberían usar el SDK en proceso y ahorrarse el salto de red por completo. Las mediciones son reproducibles: python3 tests/bench.py.

Seguridad & privacidad

Qué guardamos

Por defecto, el rastro de auditoría registra resúmenes y metadatos estructurados — nombres de herramienta, argumentos comprobados contra restricciones, veredictos, hashes — no los prompts en crudo de tu agente ni los datos de tus clientes. Las claves de API se guardan solo como hash SHA-256; la clave en claro se enseña una vez, al aprovisionar tu suscripción.

Aislamiento

Los agentes pertenecen a una organización. Una clave de una organización no puede leer ni gobernar los agentes de otra — ese límite se aplica en cada petición y está cubierto por tests.

Para compradores regulados

Hay despliegue en tus instalaciones y dentro de tu VPC, de modo que ningún tráfico de agentes sale de tu red, junto con un acuerdo de tratamiento de datos. Pregúntalo antes de integrar, no después.

Siguiente

Ver la demo de deriva en vivo →
Precios →