TCP proxy

Multi-tenant SaaS: rules per database and per service user

In a SaaS, the same code serves all your customers, and one badly written statement can touch everyone's data at once. The Vericto TCP proxy applies rules, allowlists and masking per database and per database user, with one proxy per database or tenant group. It does not isolate tenants on its own: that is still the job of your data model and your database's permissions.

Diagram: multi-tenant SaaS with one Vericto proxy per databaseIn your infrastructure, the SaaS application connects as the app_rw user to two Vericto proxies, one per database: tenants-a and tenants-b, each holding one tenant group. The internal support tool connects as support_ro to the proxy of database B. Each proxy applies the allowlist of the session's user (VERICTO-087); a statement outside it comes back as an error. The proxies get each database's rules from Vericto Cloud and send it telemetry for the workspace audit trail.Your infrastructurerules · telemetryoutside the allowlist: VERICTO-087Vericto Cloudper-db rules · audit trailSaaS applicationuser: app_rwSupport tooluser: support_roProxy: database A087 allowlist per userEvaluates hereProxy: database B087 allowlist per userEvaluates heretenants-aone tenant grouptenants-bone tenant groupDiagram: multi-tenant SaaS with one Vericto proxy per databaseIn your infrastructure, the SaaS application connects as the app_rw user to two Vericto proxies, one per database: tenants-a and tenants-b, each holding one tenant group. The internal support tool connects as support_ro to the proxy of database B. Each proxy applies the allowlist of the session's user (VERICTO-087); a statement outside it comes back as an error. The proxies get each database's rules from Vericto Cloud and send it telemetry for the workspace audit trail.Your infrastructureVericto Cloudper-db rules · audit trailSaaS applicationuser: app_rwProxy: database A087 allowlist per userEvaluates heretenants-aone tenant groupSupport tooluser: support_roProxy: database B087 allowlist per userEvaluates heretenants-bone tenant group
  • SQL statement path
  • Vericto Cloud: rules and telemetry
  • Blocked statement: an error comes back
  • Where Vericto evaluates
One proxy per database, each holding one tenant group. The application and the support tool connect as different database users, each with its own allowlist. Each proxy syncs its database's rules with Vericto Cloud, where the workspace audit trail lives.

What it protects

What Vericto applies in a SaaS, all at the TCP proxy:

  • Rules per database. They are configured per workspace, with per-database overrides, and the critical ones (DELETE and UPDATE without WHERE, DROP, TRUNCATE) block by default: the statement that would touch every row of every tenant never reaches the database.
  • VERICTO-087 (Team and Enterprise): an allowlist of tables and columns per database user, in each database. Give each service its own user: the API, the workers, the support tool. Under an allowlist, DDL, SET ROLE, SET search_path and USE are denied.
  • VERICTO-085 (Team and Enterprise): sensitive columns blocked or returned masked. The policy is per database and applies to all its sessions: if the support tool must see masked values and the application must not, give support its own proxy, registered as another database with masking on.
  • VERICTO-019: disabling a table's Row Level Security or triggers, the basis of row-level isolation on Postgres.
  • Audit trail per workspace. Every event carries its database and its identity (the database user or the API key), so you see each database's and each service's traffic separately. With sanitized telemetry, literals, such as your customers' data, are replaced before they leave the proxy.

What Vericto isolates, and what it does not

Vericto decides on the shape of each statement, not on the rows it returns, and it does not know the tenant of the request behind it. So:

  • It does separate rules, allowlists and sensitive columns per database, and allowlists per database user. With a schema or a database per tenant and one user per tenant, an allowlist that names only its schema denies the other tenants' tables.
  • It does not filter rows. In a shared table with a tenant_id column, Vericto cannot check that the WHERE filters on the right tenant: it does not know whether that request should get tenant_id = 42 or 43. That is what Postgres Row Level Security, or a schema or database per tenant, is for.
  • It does not authenticate your tenants, separate networks or credentials, or resolve views, functions and triggers. Your model's roles, grants, RLS and database separation are still the isolation mechanism; Vericto is defence in depth on top.

Latency and availability

Each statement adds the hop to its database's proxy and the evaluation in its process, with no network calls on the critical path. Allowlists and sensitive columns are decided on the AST, like any other rule, without reading results.

For parsing and rule evaluation, the design target is p99 < 2 ms. It is a target, not a measurement: the proxy latency benchmark is not published yet.

With one database per tenant, count the proxies: one per database is the simplest, and memory grows with connections, about 4 KB per TCP session. Protected databases depend on the plan: 1 on Free, 3 on Builder, 10 on Team and custom on Enterprise.

How to implement it

  1. Choose the model: a database per tenant or tenant group (the easiest to isolate), a schema per tenant, or shared tables with tenant_id and RLS. Register each database in the workspace with its dialect.
  2. Deploy one proxy per database (the container ghcr.io/vericto/vericto-proxy) with UPSTREAM_HOST pointing at it, and connect the services that use that database to it.
  3. Create one database user per service (the API, the workers, the support tool) and, on Team and Enterprise, its allowlist in observe. Simulate it against its queries from the last 7 days and switch it to enforce.
  4. Tag each database's sensitive columns. If one service must see them masked and another must not, give it its own proxy, registered as another database.
  5. With shared tables, turn on Postgres Row Level Security and check that VERICTO-019, which catches it being disabled, is set to block.
  6. Before changing a rule for one database, simulate it against that database's real traffic (Builder, Team and Enterprise).

When not to use this option

  • As the only isolation between tenants. Vericto is not a tenant-isolation mechanism: it does not see the request's tenant or filter rows. Use it on top of RLS or a database per tenant, not instead of them.
  • Thousands of small databases, one per tenant. One proxy per database multiplies instances, and protected databases count against the plan: group tenants per database, or talk to sales about Enterprise.
  • Oracle or SQL Server. There is no TCP proxy for those dialects: use the Runtime API with one API key per service. The allowlist then becomes a decision your code honours, not a control.

Plan availability

FeatureFreeBuilderTeamEnterprise
TCP proxy (PostgreSQL and MySQL)YesYesYesYes
Queries per month (proxy and Runtime API)500K5M50MUnlimited
Databases1310Custom
Custom rules (YAML)—520Unlimited
Per-agent allowlists (VERICTO-087)——YesYes
Sensitive columns and masking (VERICTO-085)——YesYes
Rule simulator—Up to 30 daysUp to 90 daysPer contract (up to 180 days)

Prices and every limit on Pricing.

Try it with your own SQL

The Free plan includes 500K queries a month and 1,000 CLI and MCP validations, with no credit card. For deployments in your VPC, SSO and dedicated SLAs, talk to sales.