Integration Guide

Protect your databases in under 5 minutes. Zero credentials stored. Compatible with any PostgreSQL driver or ORM.

Prerequisites

  • A Vetro account (sign up free)
  • An API key (create one in Dashboard → API Keys)
  • For TCP mode: Docker or any container runtime to deploy the proxy
  • For HTTP mode: ability to make HTTP requests from your CI/CD pipeline

Zero credentials: Vetro never stores your database credentials. The TCP proxy runs in your infrastructure and forwards authentication transparently. Your credentials never leave your network.

Step 1: Create workspace & database

  1. Go to /register and create your account
  2. Navigate to Dashboard → Databases → Add Database
  3. Enter a name (e.g. prod-users-db) and select the dialect (PostgreSQL, MySQL)
  4. Copy the Database ID — you'll need it for configuration
  5. Go to Dashboard → API Keys and create a key — copy it securely

That's all you need from the Vetro dashboard. No connection strings, no credentials.

TCP Proxy — Production database protection

The TCP proxy intercepts every SQL query at the PostgreSQL wire protocol level. Deploy it as a sidecar or standalone container in your infrastructure. Your app connects to the proxy with the same credentials it uses for the database directly.

# docker-compose.yml
services:
  vetro-proxy:
    image: ghcr.io/donkan168/vetro-proxy:latest
    ports:
      - "5433:5433"
    environment:
      VETRO_API_URL: "https://api.vetro.dev"
      VETRO_API_KEY: "${VETRO_API_KEY}"
      VETRO_DATABASE_ID: "${VETRO_DATABASE_ID}"
      UPSTREAM_PG_HOST: "your-db-host.rds.amazonaws.com"
      UPSTREAM_PG_PORT: "5432"
      PROXY_PG_LISTEN_PORT: "5433"
      # Remote DB (RDS): encrypt the proxy → database hop.
      UPSTREAM_PG_SSLMODE: "require"   # or "verify-full" + UPSTREAM_PG_SSLROOTCERT

  app:
    environment:
      # Point your app to the proxy — same user:pass as before
      DATABASE_URL: "postgres://user:pass@vetro-proxy:5433/mydb"
    depends_on:
      - vetro-proxy

Encrypting connections (TLS)

Proxy → database. When your database is remote (e.g. a managed RDS/Cloud SQL instance), encrypt the proxy → database hop with UPSTREAM_PG_SSLMODE. The proxy performs the PostgreSQL SSLRequest negotiation and tunnels the session through TLS:

# Encryption only (does not verify the server certificate)
UPSTREAM_PG_SSLMODE=require

# Encryption + verify the certificate chain and hostname
UPSTREAM_PG_SSLMODE=verify-full
UPSTREAM_PG_SSLROOTCERT=/etc/vetro/rds-ca.pem   # omit to use public CA roots

# Optional: mutual TLS — present a client cert so the proxy authenticates to a
# database configured with `cert` auth (one service identity for the proxy)
UPSTREAM_PG_SSLCERT=/etc/vetro/proxy-client.crt
UPSTREAM_PG_SSLKEY=/etc/vetro/proxy-client.key

Client → proxy. By default the proxy declines client TLS, because it is meant to run inside the same trusted boundary as your app (sidecar, same host, or private subnet) where that hop never leaves your network. To encrypt it natively, set PROXY_TLS_MODE=require with a server certificate and key — the proxy answers the SSLRequest with 'S' and terminates TLS as the server:

PROXY_TLS_MODE=require
PROXY_TLS_CERT=/etc/vetro/server.crt
PROXY_TLS_KEY=/etc/vetro/server.key

Vetro recommends terminating client TLS at the proxy when:

  • The app → proxy hop crosses an untrusted network (different host, VPC, or AZ) instead of a sidecar / private subnet.
  • Your driver enforces sslmode=require and you don't want to run a TLS-terminating load balancer (AWS NLB, HAProxy) in front.
  • A compliance baseline mandates encryption in transit on every hop.

If the proxy already runs as a sidecar or in the same private subnet, leave it disabled — it isn't needed. This is server-side TLS (the client authenticates the proxy); the proxy relays auth end-to-end, so scram-sha-256, md5 and cloud token auth (IAM / Entra) work unchanged. SCRAM channel binding (SCRAM-SHA-256-PLUS) and per-end-client cert auth are not supported through a TLS-terminating proxy by design — both exist to detect a proxy in the middle. Inspecting SQL requires decrypting at the proxy, so opaque end-to-end TLS is incompatible with enforcement.

Frameworks & ORMs

Once the proxy is running, point your app to it. The only change is the host — same credentials, same database name:

ORM / DriverConfigurationExample
Prisma DATABASE_URL in .env postgres://user:pass@vetro-proxy:5433/mydb
SQLAlchemy create_engine(url) postgresql+psycopg2://user:pass@proxy:5433/mydb
Drizzle ORM connectionString postgres://user:pass@proxy:5433/mydb
TypeORM host + port in DataSource host: 'vetro-proxy', port: 5433
ActiveRecord DATABASE_URL postgres://user:pass@proxy:5433/mydb
Go (pgx/lib-pq) sql.Open("postgres", url) postgres://user:pass@proxy:5433/mydb
Java (JDBC) jdbc:postgresql://host:port/db jdbc:postgresql://vetro-proxy:5433/mydb
.NET (Npgsql) Host=;Port=;Database= Host=vetro-proxy;Port=5433;Database=mydb

HTTP API — CI/CD & pipeline integration

Evaluate SQL queries via REST without deploying a proxy. Ideal for pre-deploy checks, migration validation, and code review automation. Queries are analyzed but never executed against your database.

CI/CD pipelines

Add a step to your pipeline that sends each migration file to the Vetro API. If any query is blocked, the pipeline fails before reaching production.

# GitHub Actions
- name: Vetro SQL Check
  run: |
    for file in migrations/*.sql; do
      RESULT=$(curl -s -X POST https://api.vetro.dev/api/v1/proxy/query \
        -H "Authorization: Bearer ${{ secrets.VETRO_API_KEY }}" \
        -H "Content-Type: application/json" \
        -d "{\"query\": \"$(cat $file)\", \"database_id\": \"${{ vars.VETRO_DB_ID }}\", \"dialect\": \"postgres\"}")
      DECISION=$(echo $RESULT | jq -r '.decision')
      if [ "$DECISION" = "BLOCKED" ]; then
        echo "::error file=$file::Blocked by $(echo $RESULT | jq -r '.rule_code')"
        exit 1
      fi
    done
# GitLab CI
vetro-check:
  stage: test
  script:
    - |
      for file in migrations/*.sql; do
        RESULT=$(curl -s -X POST https://api.vetro.dev/api/v1/proxy/query \
          -H "Authorization: Bearer $VETRO_API_KEY" \
          -H "Content-Type: application/json" \
          -d "{\"query\": \"$(cat $file)\", \"database_id\": \"$VETRO_DB_ID\", \"dialect\": \"postgres\"}")
        DECISION=$(echo $RESULT | jq -r '.decision')
        [ "$DECISION" = "BLOCKED" ] && echo "BLOCKED: $file" && exit 1
      done
  rules:
    - changes: ["migrations/**/*.sql"]

Code examples

# Node.js — validate a query before execution
const result = await fetch('https://api.vetro.dev/api/v1/proxy/query', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.VETRO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: sqlToValidate,
    database_id: process.env.VETRO_DB_ID,
    dialect: 'postgres',
  }),
}).then(r => r.json());

if (result.decision === 'BLOCKED') {
  throw new Error(`Blocked by ${result.rule_code}: ${result.ast_node_path}`);
}
# Python — pre-deploy migration check
import requests, sys, glob

for path in glob.glob("migrations/*.sql"):
    with open(path) as f:
        sql = f.read()
    r = requests.post(
        "https://api.vetro.dev/api/v1/proxy/query",
        headers={"Authorization": f"Bearer {os.environ['VETRO_API_KEY']}"},
        json={"query": sql, "database_id": os.environ["VETRO_DB_ID"], "dialect": "postgres"},
    )
    if r.json()["decision"] == "BLOCKED":
        print(f"BLOCKED: {path} — {r.json()['rule_code']}")
        sys.exit(1)
print("All migrations safe ✓")

Verify & monitor

Once integrated, verify the setup is working:

  • TCP Proxy: Go to Dashboard → Databases and click "Verify connection". The proxy will report its status within seconds.
  • HTTP API: Send a test query and check the response. A 200 with "decision": "ALLOWED" confirms the integration works.
  • Dashboard: Navigate to the Queries page to see real-time evaluation events flowing in.

Enable Observe Mode during initial rollout — it logs all decisions without blocking anything, so you can validate your ruleset before enforcing it.