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.
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.
: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ía | cómo |
|---|---|
| fichero | móntalo en /etc/d4a/gateway.json |
| variable | D4A_GATEWAY_CONFIG_JSON con el JSON |
| variable, base64 | D4A_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>
-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.
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érmino | Qué es |
|---|---|
| ancla | La 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. |
| paso | Una cosa que el agente se propone hacer: llamar a una herramienta o producir una salida. Un paso = una llamada gobernada. |
| desviación | Cuánto se aparta un paso del ancla, medido en seis canales: scope, objective, output_type, tradeoff, tone, execution. |
| persistencia | Una media exponencial de la desviación. Decae. Responde a «¿está el agente fuera de misión ahora mismo?" |
| racha | Contadores 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. |
| admisibilidad | Una 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.
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.
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.
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.
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.
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 & registrado | Aplicado 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.
"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
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ón | qué significa | qué haces |
|---|---|---|
| permitir | En misión. | Ejecuta la acción. |
| warn_replan | La 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_escalate | Una 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.
| campo | obligatorio | notas |
|---|---|---|
| mission | sí | Texto 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. |
| tools | sí | Se toma al pie de la letra, nunca se infiere. Esta es la lista que el gate aplica de verdad. |
| signing_authority | sí | Tu credencial de despliegue o identidad de servicio. Los valores vacíos o de relleno se rechazan. |
| hard_constraints | no | {"tool","arg","op","value"}. Operadores: <= < >= > ==. Incumplirla = bloqueo inmediato. |
| allowed_topics | no | Alcance temático. |
| out_of_scope | no | Exclusiones explícitas. |
| mode | no | enforce (por defecto) o observe. |
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.
Registra un agente; devuelve agent_id y anchor_hash.
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.
Lista tus agentes registrados.
El ancla firmada, tal y como está guardada.
El registro de decisiones encadenado por hash, más el resultado de su verificación.
Vida del servicio. Sin autenticación.
Errores & cuota
| código | significado |
|---|---|
| 400 | Petición mal formada — el mensaje dice qué campo. |
| 401 | Clave de API ausente o inválida. |
| 402 | Un límite del plan — una negativa tipada, con las condiciones para reabrirlo. |
| 404 | Agente desconocido, o de otra organización. |
| 500 | Culpa 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 sí 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ón | dónde | si NO la haces |
|---|---|---|
| mission | registro | El agente no se registra. |
| tools | registro | El agente no se registra. Es la lista que el gate aplica de verdad; sin ella no hay nada que aplicar. |
| signing_authority | registro | 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_constraints | registro | 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_map | registro | 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_s | registro | No se exige frescura: un recibo de hace seis horas sostiene «se ha liquidado» en un sistema que cambia cada minuto. |
| acknowledge_no_semantic_sensor | registro | 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 / descriptores | en 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_invariant | en 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_lineage | en 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_precedence | en 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_provider | interfaz 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_KEY | entorno | 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ándo | dónde | para qué sirve |
|---|---|---|
| al crear | POST /v1/agents | lo que ya sabes que va a leer. La respuesta te devuelve reads_declared: cuántos descriptores quedaron. |
| en cualquier momento | POST /v1/agents/{id}/reads | cuando lo descubres entre pasos, o lo sabe otro sistema. |
| en el mismo paso | POST /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.
| nivel | qué declaras | qué marca |
|---|---|---|
| 1 | la clave exacta que leíste | solo escrituras sobre esa clave |
| 2 | intervalos sobre atributos ordenados | lo que cae dentro del intervalo |
| 3 | solo 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:
| campo | efecto 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
| declaración | qué caza | si 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 |
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.
| camino | latencia |
|---|---|
| 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.