security

every guarantee below, we can prove.

Nothing here is a promise you have to take on faith. Each claim below is backed by something we actually built to enforce it, and where we have not built that yet, we say so plainly instead of implying it.

in one minute

What we read

Your Stripe subscriptions, over a key that cannot write. From your app: one table and the columns you chose, nothing else.

What we never touch

Card numbers, payment methods, any other column in your database, or any Stripe data outside the scopes listed below.

How to revoke or delete

Revoke the Stripe key from your Stripe dashboard any time. Deleting your report deletes the credentials, the snapshot and every finding at once, immediately, nothing held back for later.

the primary path

one read-only function, in your own backend

Most of the apps Entitled reconciles are built on Lovable, Bolt or Replit, and their managed databases don’t expose a connection string to grant a role on, so this is the path the connect wizard defaults to and most customers use. You paste one prompt into your builder. It writes a small, read-only function that returns one table as JSON. Entitled calls it with a bearer token you generate and can revoke at any time, over HTTPS. Nothing is installed on your infrastructure beyond that one function.

HTTPS only

Your access token is never sent in cleartext.

technical detail

An http:// endpoint URL is refused at configuration time, with the reason stated plainly: "Entitled only connects over HTTPS. An http:// URL would send your access token across the network in cleartext." You find out while setting up, not during a run.

Can't be redirected somewhere else

A redirect from your endpoint can't be used to send your token to a different address.

technical detail

Entitled never follows a redirect automatically. Each hop it is pointed to is checked by hand against the same HTTPS and public-address rules as the URL you typed, and it gives up after 3 hops with a clear error rather than following one indefinitely.

Can't reach your private network

The endpoint can't be used to make Entitled read something not meant to be public.

technical detail

Before every request, including every redirect hop, the address it is about to call is checked and a private, internal or loopback address is refused with a plain reason. That is the class of address an attacker would use to reach something behind your firewall, so it is refused outright rather than just going unused.

Bounded, not indefinite

A broken or slow endpoint fails loudly instead of hanging your scan.

technical detail

A 30-second timeout on the whole request, and an 8 MB cap on how much of the response is read, so a runaway function on your side can't hang a run or use unbounded memory on ours.

A row cap, stated when it's hit

Entitled never silently compares an incomplete list as if it were the whole one.

technical detail

Reads stop at 10,000 rows. If your response is longer than that, the run says plainly that the count may be incomplete, rather than quietly comparing only the first 10,000 customers as if that were everyone.

applies either way

Read-only Stripe

Entitled cannot write to your Stripe account.

technical detail

A full secret key is refused the moment you paste it. Entitled only accepts a restricted key, one you create yourself with read-only access, and every request it makes is a hard-coded read: there is no path in the connector that can issue a write, even if a key were given broader access by mistake.

Secrets unreadable by the web app

The application serving this page cannot decrypt your credentials, ever.

technical detail

Each credential is encrypted (AES-256-GCM) with a key generated just for it, and that key is itself sealed with a separate master key pair (RSA-OAEP) split across two processes. The web app you are browsing right now holds only the half that can seal a credential, never the half that can open one. Only the worker, a separate process with no public entry point, holds that half. The web app has no way to decrypt a credential; it isn't a permission it simply doesn't use, it does not hold the key that could.

Fixed egress IP

You can restrict access to a single known address.

technical detail

Every read comes from 161.35.64.36. The worker runs on one server in Frankfurt and nothing else makes requests on Entitled's behalf. Allowlist that address and a leaked token or credential is useless from anywhere else.

stripe restricted key: scopes requested

  • CustomersRead
  • SubscriptionsRead
  • ChargesRead
  • DisputesRead
  • EventsRead
  • Products & Pricesonly used to compare plan names, skip it and everything else still runsRead, optional
  • Account (name only)shown once to confirm which account connected, skip it and we just won't show the nameRead, optional

No write permission is requested anywhere. Stripe can’t tell us which permissions a key carries, so we test each one directly and refuse the key if any are missing. Every request we make is a read; there is no way for this to write, even by accident.

technical detail

This check probes every scope it needs against the key you paste and refuses one missing any, rejects a full secret key on paste, and issues every request as a hard-coded GET, so a write scope on the key is never exercised.

using your own Postgres?

for a self-managed database (Supabase, Neon, RDS)

If your app runs on a database you manage yourself rather than a Lovable Cloud project, you can grant Entitled a read-only role directly instead of using the function above. Run this in your own SQL editor; nothing here is run for you.

the grant you run yourself

create role drift_reader with login password '••••••••';
grant connect on database postgres to drift_reader;
grant usage   on schema public     to drift_reader;
grant select  on public.subscribers to drift_reader;

-- required, or the reader sees zero rows
-- and the scan is silently wrong
create policy drift_reader_read on public.subscribers
  for select to drift_reader using (true);

Read-only database

Entitled cannot write to your database.

technical detail

default_transaction_read_only = on is set as a connection parameter on every session, not wrapped in application logic that could be changed by mistake. A write fails at your server, independent of anything this app does.

One table, named columns

Entitled reads only the table and columns you mapped, nothing else in your database.

technical detail

The table and column names you mapped are checked against a strict pattern before they are ever placed into a query: only ordinary letters, numbers and underscores are accepted, nothing that could change what the query does. The only statement the connector can ever issue is a single read against the one table you mapped, with a fixed row limit.

Bounded queries

A slow or unbounded query on your database cannot hang or run away.

technical detail

Every session sets a 30-second statement timeout as a connection setting, enforced by your own database server, not by our application logic. A query that runs longer is killed at your server and the run fails loudly, rather than a connection being held open indefinitely.

Read the DPARun a free scan