> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inviolet.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Postgres

> Connect a Postgres database so transactional agents carry an grant to every query.

Postgres is the typical source for transactional agents — agents that
write, agents that read live application state, agents embedded inside
a SaaS product. The Postgres connector pairs with the Ultraviolet-tier
database proxy to give you column-level enforcement at the wire.

## Why it matters

Transactional agents have the highest blast radius if compromised:
they often have write access. Inviolet's Postgres path narrows every
query to the column allowlist on the matched intention, and (with Vault
binding) issues a short-lived role per grant instead of using
the application's persistent password.

## 1. Create a read-only role

```sql theme={"dark"}
CREATE ROLE inviolet_ro NOINHERIT;
GRANT CONNECT ON DATABASE app TO inviolet_ro;
GRANT USAGE ON SCHEMA public TO inviolet_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO inviolet_ro;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT ON TABLES TO inviolet_ro;

CREATE USER inviolet_svc WITH PASSWORD '<rotate>' IN ROLE inviolet_ro;
```

## 2. Add the data source in Inviolet

App dashboard → **Data Sources → Add → Postgres**. Paste:

* Host, port, database name
* Username (`inviolet_svc`) + password
* SSL mode (`require` for managed Postgres)

Inviolet performs a test connection + introspection and lists the
discovered tables.

## 3. Wrap a query with intent evaluation

```ts theme={"dark"}
import { Inviolet } from "@inviolet/sdk"

const inviolet = new Inviolet({ apiKey: process.env.INVIOLET_API_KEY! })

const decision = await inviolet.intent.evaluate({
  userId: "user_2pX9...",
  toolCall: {
    name: "lookup_user_by_email",
    arguments: { email: "alex@example.com" },
  },
  dataSourceId: "postgres_app_prod",
})

if (decision.outcome !== "allowed") return null

// Use decision.data_elements as the column allowlist for the actual SELECT
const columns = decision.data_elements.join(", ")
// SELECT ${columns} FROM users WHERE email = $1
```

<Note>
  This is the **evaluate** path (`intent.evaluate`) — a lightweight pre-call
  check. For **structural** enforcement where the agent never holds the database
  password, mint a mandate and `guard` the call with
  [`@inviolet/agent-sdk-core`](/sdks/node) and bind a short-lived role via
  [credential brokering](/concepts/credential-broker). See the
  [Quickstart](/quickstart).
</Note>

## 4. Verify

Decision-feed entries for this source carry `data_source_id =
postgres_app_prod`. The query pattern map shows tables and columns
touched. Anything outside the matched intention's element list should
fall to the lower-confidence "ambient" cluster — that's the cohort to
watch when promoting to enforcement.

## Read next

* **[Connect Vault](/guides/connect-vault)** — wire the credential
  broker so each grant gets a fresh Postgres role
* **[Salesforce](/integrations/data-sources/salesforce)**
* **[Snowflake](/integrations/data-sources/snowflake)**
