# How resources stay secure

This page explains _how_ Volly resources are secured. If you want the practical, day-to-day side of this instead: see [connect real data (resources)](https://volly.so/docs/external-resources).

## Your credentials never reach the browser

An app's JavaScript never holds a password, API key, or OAuth token. Volly
stores the credential encrypted, and only the server-side gateway decrypts
it, at the moment of a proxied request and never before. The app calls a
relative URL like `/__volly/resources/crm-data/query`, and Volly attaches
the real credential on the way out.

For Postgres this goes a step further. You never hand Volly an admin
password at all: setup generates a SQL script you run yourself, which
creates a dedicated read-only role scoped to whichever schemas you chose.
Volly only ever stores that role's connection string.

## Read access is enforced twice

Database access runs through a role whose own database-level grants allow
only `SELECT`. That's the real boundary, not something Volly is trusting an
app to respect on its own. The gateway adds a second, redundant check on top
of that (rejecting anything that isn't a read-only transaction) purely as
defense in depth.

REST APIs work the same way through scoping: connect a key with read-only
permissions, and even a compromised app can't do more than that key allows.
Write access (POST, PUT, PATCH, DELETE) only opens up when an admin
explicitly grants a specific app read-and-write, and most apps never get it.

## One org can never reach another's resources

Every request is filtered by the organization and app making it, and that's
enforced at the database layer through row-level security, not just checked
in application code. An app that hasn't been granted a resource just gets a
plain 404. It can't tell "doesn't exist" apart from "exists but you're not
allowed," so there's nothing to probe.

## Every call is logged

Settings → Audit log records who ran what, from which app, against which
resource, with the query or request path, the outcome, and how long it
took. If a key or connection ever needs investigating, this is the trail;
treat the log itself as sensitive, since it can contain the substance of
what was queried.

## Cross-site requests can't ride along

Every resource call must carry the `X-Volly-Resource` header. That's not a
secret an attacker could steal. It exists because browsers won't let a
cross-site page attach a custom header without a CORS preflight, and Volly's
gateway never answers that preflight. In practice that closes the one gap a
`SameSite=Lax` session cookie leaves open, a cross-site top-level GET
navigation, without asking app builders to manage a token themselves.

## The gateway can't be pointed at your internal network

The proxy is confined to the exact base URL or database address an admin
configured, and it refuses to follow redirects that would send it
elsewhere. Requests also leave from Cloudflare's network, which physically
can't reach private (RFC1918) or link-local addresses, so even a
misconfigured resource can't be turned into a path onto something on your
internal network that was never meant to be public.

## If a shared encryption key were ever compromised

Credentials sit under a versioned encryption key. If that key ever needed
rotating, Volly can add a new one, re-encrypt everything under it, and
retire the old key, all without any credential being written out in the
clear at any point.