SDK de Python y Node.

Evalúa cada sentencia SQL con Vericto antes de que tu driver la ejecute, desde Python o Node.js: instalación, configuración, la API, el fallo cerrado, los errores y cómo usar los SDK con LangChain, LlamaIndex y otros frameworks.

Paquetes. Python: vericto en PyPI, código en vericto-python. Node.js: @vericto/sdk en npm, código en vericto-node. Los dos son código abierto bajo la Apache License 2.0.

Resumen

Los SDK son clientes ligeros de la API de runtime: antes de que tu driver ejecute una sentencia, la envían a POST /api/v1/proxy/query y aplican la decisión. Una sentencia bloqueada no llega a la base de datos, una marcada se ejecuta y se reporta, y una que lee columnas enmascaradas se ejecuta como la reescritura que devuelve Vericto. La evaluación ocurre en tu workspace: el SDK no evalúa nada localmente.

  • Python: vericto (Python 3.10+, una sola dependencia, urllib3). Wrappers para snowflake-connector-python, sqlite3, SQLAlchemy y cualquier driver DB-API; integraciones para LangChain, LlamaIndex, dbt y Airflow.
  • Node.js: @vericto/sdk (Node.js 18+, sin dependencias en runtime, ESM y CommonJS, tipos de TypeScript). Wrappers para snowflake-sdk y better-sqlite3, una integración para LangChain.js y un cliente de la API de runtime para cualquier otro driver.

Para PostgreSQL y MySQL también está el proxy TCP: no requiere cambios de código y protege todo el SQL que pasa por la conexión, incluido el que genera un ORM. El SDK protege el SQL que pasa por él (ver Límite de aplicación).

Instalación

# Python 3.10+
pip install vericto                  # core, sqlite3 and any DB-API driver
pip install "vericto[sqlalchemy]"    # extras: snowflake, sqlalchemy, langchain, llamaindex, airflow, dbt

# Node.js 18+
npm install @vericto/sdk             # entry points: /snowflake, /better-sqlite3, /langchain

Los drivers y los frameworks son extras (Python) o dependencias peer opcionales (Node): importar vericto o @vericto/sdk nunca carga un driver.

Configuración

Crea una API key con el scope proxy:evaluate en Dashboard → API keys (una por servicio, para que sus eventos lo identifiquen) y registra la base que proteges. Los dos SDK leen las mismas variables de entorno:

VariableSignificadoPor defecto
VERICTO_API_KEYLa API key (vtro_...) con el scope proxy:evaluate. Se envía solo en la cabecera X-API-Key y solo por HTTPS (HTTP solo hacia localhost).obligatoria
VERICTO_DATABASE_IDLa base registrada (UUID, en Dashboard → Bases de datos): se aplican sus reglas, columnas sensibles y allowlists de agentes.obligatoria
VERICTO_API_URLLa URL base de la API.https://api.vericto.com
VERICTO_FAIL_MODEclosed u open (ver Fallo cerrado).closed
VERICTO_TIMEOUTSegundos por intento.5
VERICTO_CA_BUNDLESolo Python: un bundle de CA adicional (PEM). En Node, usa NODE_EXTRA_CA_CERTS.el almacén del sistema

Las opciones del constructor tienen prioridad sobre las variables: Vericto(api_key=..., database_id=..., fail_mode=..., timeout=...) en Python (el bundle de CA va en RuntimeApiEvaluator(ca_bundle=...)) y new Vericto({ apiKey, databaseId, failMode, timeoutMs }) en Node, donde el timeout va en milisegundos. La key nunca aparece en logs, mensajes de error, repr() ni toString().

La API

Comparte una instancia de Vericto por proceso: es segura entre hilos y reutiliza sus conexiones HTTP. Los wrappers llaman a enforce por cada sentencia, y tú puedes llamarlo alrededor de cualquier otro driver:

  • check(sql, dialect): devuelve el veredicto, sea cual sea, sin lanzar en BLOCKED: sirve para preguntar. Si Vericto no responde aplica el modo de fallo, como enforce: con closed lanza el error de indisponibilidad; con open devuelve un veredicto UNEVALUATED con el motivo (desde vericto 0.5.0 y @vericto/sdk 0.4.0; antes lanzaba también con open).
  • enforce(sql, dialect): evalúa y aplica la decisión. Lanza el error de bloqueo, registra FLAGGED y MONITORED, aplica el modo de fallo si Vericto no responde y devuelve el veredicto.
  • prepare(sql, dialect): enforce, y devuelve el SQL que debes ejecutar: la reescritura enmascarada si la hay; si no, tu SQL.
  • enforce_all (Python) y enforceAll (Node): un lote. Un bloqueo en cualquier sentencia lanza antes de que ejecutes la primera.
  • Opcional en todos: context (ticket y tenant, de 1 a 128 caracteres) y end_user (Python) o endUser (Node), hasta 16 atributos del usuario final para el que actúa tu servicio. El SDK los valida con los límites del servidor antes de enviarlos. Tienen efecto cuando las políticas de negocio están habilitadas en tu workspace, y llevan la confianza de la key que los envía, nunca más (vericto 0.5.0 y @vericto/sdk 0.4.0).
import psycopg
import vericto
from vericto import Vericto, VerictoBlocked

guard = Vericto()  # one per process; reads the VERICTO_* variables
conn = vericto.wrap(psycopg.connect("dbname=app"), dialect="postgres", guard=guard)

try:
    conn.cursor().execute("DELETE FROM customers")  # no WHERE: blocked, never reaches the database
except VerictoBlocked as exc:
    print(exc.verdict.rule_code, exc.suggested_fix)

sql = guard.prepare("SELECT id, email FROM customers LIMIT 10", "postgres")  # the masked rewrite, if any
import { Vericto, VerictoBlockedError, sqlToExecute } from '@vericto/sdk';

const vericto = new Vericto(); // one per process; reads the VERICTO_* variables

try {
  const verdict = await vericto.enforce(sql, 'postgres');
  await pool.query(sqlToExecute(verdict)); // the masked rewrite, if any
} catch (err) {
  if (!(err instanceof VerictoBlockedError)) throw err;
  console.log(err.verdict.ruleCode, err.suggestedFix);
}

El veredicto trae decision, el código de regla, la severidad, la corrección sugerida, la reescritura enmascarada, los findings, las columnas sensibles que lee, lo que la allowlist del agente denegó y el id del evento en el Audit Trail (en Python con nombres rule_code, suggested_fix, rewritten_query, event_id; en Node, ruleCode, suggestedFix, rewrittenQuery, eventId). Los valores de los parámetros nunca se envían: el SQL va como lo escribió tu aplicación, con sus placeholders, y el driver los enlaza después.

Qué hace cada decisión

VeredictoQué hace el SDK
ALLOWEDEjecuta la sentencia.
FLAGGED, MONITOREDLa ejecuta, la registra (advertencia o info) y llama a on_verdict / onVerdict.
PARSE_ERRORLa política de errores de parseo la dejó pasar: la ejecuta y registra una advertencia. Si la política bloquea, el servidor responde BLOCKED.
BLOCKEDLanza VerictoBlocked (Python) o rechaza con VerictoBlockedError (Node) antes de llamar al driver.
rewritten_queryEjecuta la reescritura en lugar de tu SQL: las columnas sensibles llegan enmascaradas. Nunca la original.
sin veredictoDecide el modo de fallo.

Los logs llevan el código de regla, la severidad y el event_id, nunca el texto SQL (puede tener datos personales en sus literales). Cada evaluación queda en el Audit Trail y cuenta en la cuota mensual de queries.

Fallo cerrado, timeouts y reintentos

Si Vericto no puede responder (error de red, timeout, HTTP 5xx, una respuesta que no es JSON, límite por minuto después de los reintentos o cuota mensual agotada), con closed, el valor por defecto, el SDK lanza VerictoUnavailable (Python) o VerictoUnavailableError (Node) y la sentencia no se ejecuta. Con open nunca lanza por eso: enforce ejecuta la sentencia sin evaluar, emite VerictoFailOpenWarning y pasa un veredicto UNEVALUATED a on_verdict / onVerdict; check y las herramientas de solo validación devuelven ese veredicto UNEVALUATED con el motivo. Los dos SDKs siguen el mismo contrato.

Los errores de configuración nunca se dejan pasar, en ningún modo: una key ausente o revocada (401), una key sin el scope o sin acceso a esa base (403 sin decision), una solicitud inválida (400) o un dialecto que el servidor no evalúa lanzan VerictoConfigError. Dejarlos pasar apagaría el firewall sin que nadie lo note.

Los errores de red, los timeouts, los 5xx y el 429 RATE_LIMIT_EXCEEDED se reintentan 2 veces por defecto (max_retries / maxRetries), con backoff exponencial y jitter de hasta 2 s que respeta Retry-After. QUOTA_EXCEEDED no se reintenta. El timeout es por intento: con los valores por defecto, una sentencia puede esperar tres intentos de 5 s más el backoff (y, en la primera llamada, la consulta de los dialectos del servidor) antes de fallar cerrada. Bájalo en las rutas sensibles a la latencia.

Errores

Todos heredan de VerictoError.

PythonNodeCuándo
VerictoBlockedVerictoBlockedErrorVericto bloqueó la sentencia; no se ejecutó. Trae el veredicto, los findings, la corrección sugerida y el SQL.
VerictoUnavailableVerictoUnavailableErrorNo hubo veredicto y el modo es closed.
VerictoQuotaExceededVerictoQuotaExceededErrorSubclase de la anterior: la cuota mensual se agotó (resets_at / resetsAt).
VerictoConfigErrorVerictoConfigErrorKey, scope, id de base, URL, solicitud, o un context / end_user inválido. En los dos modos de fallo.
VerictoUnsupportedDialectVerictoUnsupportedDialectErrorSubclase de la anterior: el servidor no evalúa ese dialecto. No se ejecutó nada.
PlaceholderMappingError—Solo Python: una reescritura enmascarada no se pudo devolver a los placeholders %s del driver. No se ejecutó nada.

Síncrono y asíncrono

Python es síncrono. Cada evaluación es una llamada HTTP bloqueante, también la de vericto.sqlalchemy.instrument() sobre un AsyncEngine. En código asyncio (FastAPI, OpenAI Agents SDK, Pydantic AI), evalúa en un hilo para no bloquear el event loop:

import asyncio

sql = await asyncio.to_thread(guard.prepare, query, "postgres")

Node es asíncrono. Todos los métodos devuelven una promesa y no hay variante síncrona. Por eso, en better-sqlite3, prepare y exec pasan a ser asíncronos, y en snowflake-sdk, execute() resuelve la sentencia.

Frameworks

Una integración oficial es código del SDK (un módulo o un entry point) que ya hace la evaluación. Un patrón es una forma documentada de llamar al SDK desde tu código con ese framework: el SDK no incluye un adaptador para él. Cada guía tiene un ejemplo mínimo y las versiones con las que se probó.

FrameworkSDKTipoCómo
LangChainPythonOficialvericto.langchain: VerictoSQLDatabase para SQLDatabaseToolkit, create_sql_agent y las chains SQL; VerictoCheckSQLTool.
LlamaIndexPythonOficialvericto.llamaindex: VerictoSQLDatabase para NLSQLTableQueryEngine y NLSQLRetriever.
SQLAlchemyPythonOficialvericto.sqlalchemy.instrument(engine): ORM, Core y text().
DB-APIPythonOficialvericto.wrap(conn, dialect=...) para psycopg, PyMySQL, oracledb, pyodbc y otros.
DjangoPythonPatrónUn connection.execute_wrapper que evalúa cada sentencia con prepare().
CrewAI, OpenAI Agents SDK, Pydantic AIPythonPatrónLa función de la herramienta llama a prepare() antes de ejecutar y devuelve el bloqueo al modelo (como texto o, en Pydantic AI, con ModelRetry).
VannaPythonPatrónUn SqlRunner (Vanna 2.x) que llama a prepare() antes de ejecutar.
LangChain.jsNodeOficial@vericto/sdk/langchain: VerictoSqlDatabase, VerictoSqlToolkit para createSqlAgent, VerictoCheckSqlTool.
Vercel AI SDK, Mastra, OpenAI Agents SDKNodePatrónEl execute de la herramienta llama a prepare() antes del driver y devuelve el bloqueo como texto.
node-postgres, mysql2, Knex, DrizzleNodePatrónprepare() antes de cada consulta, en el punto donde tu código la envía.
PrismaNodePatrónEnvuelve el driver adapter de Prisma 7, que recibe el SQL final: se evalúan las consultas del modelo y las crudas. Depende de una interfaz de Prisma, así que fija su versión; en PostgreSQL y MySQL, el proxy TCP no requiere código.

El patrón es el mismo en cualquier framework de agentes: la herramienta evalúa la query antes de ejecutarla, ejecuta el SQL que devuelve prepare() y, si Vericto la bloquea, devuelve el motivo y la corrección sugerida como texto para que el modelo corrija la query.

import psycopg
from vericto import Vericto, VerictoBlocked

guard = Vericto()  # one per process
conn = psycopg.connect("dbname=app")  # a plain connection: the tool does the check

def run_sql(query: str) -> str:
    """The function you register as the agent's SQL tool."""
    try:
        sql = guard.prepare(query, "postgres")  # the masked rewrite when there is one
    except VerictoBlocked as exc:
        return f"Vericto blocked this query ({exc.verdict.rule_code}); it was not run. Suggested fix: {exc.suggested_fix}"
    with conn.cursor() as cur:  # VerictoUnavailable and VerictoConfigError propagate
        cur.execute(sql)
        return repr(cur.fetchall())
import pg from 'pg';
import { Vericto, VerictoBlockedError } from '@vericto/sdk';

const vericto = new Vericto(); // one per process
const pool = new pg.Pool(); // a plain pool: the tool does the check

// The function you register as the agent's SQL tool.
export async function runSql(query: string): Promise<string> {
  let sql: string;
  try {
    sql = await vericto.prepare(query, 'postgres'); // the masked rewrite when there is one
  } catch (err) {
    if (!(err instanceof VerictoBlockedError)) throw err; // unavailable or misconfigured: never hand it to the model
    return `Vericto blocked this query (${err.verdict.ruleCode}); it was not run. Suggested fix: ${err.suggestedFix}`;
  }
  const { rows } = await pool.query(sql);
  return JSON.stringify(rows);
}

Deja que VerictoUnavailable y VerictoConfigError se propaguen: convertirlos en texto para el modelo apagaría el fallo cerrado sin que nadie lo note. Las integraciones oficiales ya lo hacen así. Algunos frameworks convierten cualquier excepción de una herramienta en un mensaje para el modelo (OpenAI Agents SDK, Vercel AI SDK, Vanna): cada guía muestra cómo evitarlo.

Límite de aplicación

El SDK protege el SQL que pasa por él. No evalúa el SQL que se ejecuta por otra conexión, por el objeto original del driver (cursor.wrapped, db.raw, rawDataSource) o desde otro proceso con las mismas credenciales, ni las consultas que un ORM genera sin exponer su SQL antes de ejecutarlo. Combina el SDK con mínimo privilegio en la base de datos y, en PostgreSQL y MySQL, con el proxy TCP cuando la garantía importa.