roxy-guard
Trazabilidad y control de acceso para flujos de agentes que consumen MCPs.
Cuando un agente delega en otro, y ese en otro, el que termina tocando la base de datos está a varios saltos de quien pidió la tarea. Este SDK registra esa cadena mientras ocurre y somete cada acción sensible al veredicto de Roxy antes de que se ejecute.
Instalación
pip install roxy-guard
# o
uv add roxy-guard
El paquete se instala como roxy-guard y se importa como roxy (igual que
pillow → import PIL). El nombre corto ya estaba tomado en PyPI.
CLI
Al instalarlo queda el comando roxy. Apunta al despliegue público por
defecto; ROXY_API_URL lo cambia.
roxy check # ¿responden dashboard, demo-api y gateway?
roxy runs # últimas corridas de agentes
roxy tree <sessionId> # el árbol de delegación de una corrida
roxy logs --denied # solo lo que Roxy bloqueó
roxy audit reglas.json # Z3 sobre tus reglas: ¿hay algún hueco?
--json en cualquiera devuelve la respuesta cruda, para encadenar con jq.
roxy audit es el único que necesita el verificador levantado
(--engine o ROXY_ENGINE_URL): no está expuesto en el despliegue público.
Sale con código 1 si encuentra un hueco, así que sirve en CI.
Qué funciona sin montar nada
| necesita | |
|---|---|
| Trazabilidad (el árbol) | solo api_url — funciona contra el despliegue público |
roxy check / runs / tree / logs |
nada, apuntan al despliegue |
Control de acceso (guard()) |
un gateway y un MCP registrado con sus reglas |
roxy audit |
el verificador corriendo |
Uso
Se engancha una vez, en la invocación. Los agentes no se tocan.
from roxy import Roxy
roxy = Roxy(api_url="https://roxygt.lat/api")
executor.invoke(
{"input": "concilia las facturas pendientes"},
config={"callbacks": [roxy], "metadata": {"purpose": "conciliación mensual"}},
)
print(roxy.tree()) # el árbol completo, como lo ve el dashboard
Cada agente que se lance dentro de esa invocación queda registrado solo, con su padre correcto. No hay ids que pasar de mano en mano.
Sub-agentes en invocaciones separadas
Un sub-agente lanzado con su propio .invoke() llega sin padre — LangChain
no puede encadenar dos invocaciones independientes. child_config arma la
config con el padre ya declarado:
executor.invoke(entrada, config=roxy.child_config(run_id_del_padre,
purpose="conciliar INV-1005"))
Control de acceso
from roxy import RoxyUnavailable
try:
decision = roxy.guard(
action="update_invoice",
payload={"invoiceId": "INV-1005", "proposedTotal": 0,
"computedSubtotalSum": 600000},
run_id=run_id,
mcp_name="invoices-mcp",
)
except RoxyUnavailable:
return # sin veredicto no hay permiso
if not decision.allowed:
return f"denegado: {decision.reason}"
escribir_en_la_base()
RoxyUnavailable no es una negación ni un permiso: significa que Roxy no
pudo decidir. Tratarlo como permiso sería conceder acceso justamente
porque la capa de seguridad se cayó.
Delegación entre procesos (A2A)
Cuando el sub-agente corre en otro proceso, la cadena se propaga por headers:
# quien delega
requests.post(url_del_subagente, json=tarea, headers=roxy.headers_to_send(run_id))
# quien recibe
roxy.receive(request.headers, ejecutar_subagente, tarea)
Sin esto el agente remoto arranca un árbol nuevo y la cadena se corta justo donde importa: en el salto entre organizaciones.
Auditoría
roxy.lineage(agent_id) # de la raíz hasta ese agente
roxy.tree() # todos los nodos de la sesión
Cómo funciona por dentro
Roxy hereda de BaseCallbackHandler, así que LangChain le avisa cada vez
que arranca un chain. En cada aviso el SDK decide si ese chain es un agente
que vale la pena registrar y, si lo es, hace POST /agents.
Hay dos detalles que resuelve por su cuenta:
Traducción de ids. LangChain identifica cada run con un UUID; la API de
Roxy asigna un ObjectId de Mongo y valida que parentId sea uno de los
suyos. El SDK mantiene el mapa run_id → id de Roxy y traduce, porque el
id del framework no sirve como padre.
Filtrado de ruido. Una corrida de cuatro agentes dispara unos noventa
eventos: prompts, parsers, scratchpads. Registrarlos todos convertiría el
árbol en ruido. Se registra un nodo cuando la invocación trae purpose en
su metadata o cuando el chain es un AgentExecutor.
Todo el registro es fail-open: si la API no responde, se pierde el nodo y el agente sigue. La traza es observación, no la tarea. El control de acceso es lo contrario, fail-closed: sin veredicto no se ejecuta nada.
Licencia
MIT
Metadata
Release files for roxy-guard 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| roxy_guard-0.2.0.tar.gz | 12.4 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| roxy_guard-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.7 kB
Release files / roxy_guard-0.2.0.tar.gz
| Download URL | roxy_guard-0.2.0.tar.gz |
|---|---|
| Size | 12.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
19e1eee91eceae5d6800d7ea049e635811405d75ec00696e74928c2b30f39e9e
|
|
BLAKE2b-256 checksum How to use checksums |
e14ae82eff69f8455e1697dad04f3603349268d1bc6d39f8361627f1931cb96b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / roxy_guard-0.2.0-py3-none-any.whl
| Download URL | roxy_guard-0.2.0-py3-none-any.whl |
|---|---|
| Size | 12.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
67a6c3fc30c4628a696eb1c9760c20b9e88378a1e9593ac4df62fb51af1059fc
|
|
BLAKE2b-256 checksum How to use checksums |
35872f0ca7dde72d21dbb18eb3aa919186620229cab7b0b06669edd2dc1c0ea0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Security for roxy-guard 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.