The lock is a read-only role
DB SDK is designed to read, not write. It should not store credentials. Treat every query — especially a model-generated one — as untrusted.
Read, not write
query() is a bounded read. Writes are never part of DB SDK.
In-memory credentials
Secrets are input, never persisted.
Untrusted queries
Prompts and SDK SQL checks are not a security boundary.
Assume the query is hostile
The dangerous case is a host that runs SQL it did not write: customer input, admin consoles, or a model. No single layer is enough.
- 01
Queries are untrusted
- 02
Credentials are powerful unless the customer restricts them
- 03
A sanitizer or SQL classifier can miss an edge case
- 04
A compromised host can use whatever role it stored
Four layers. One lock.
SDK and provider checks are extra. If the stored user can DELETE, a missed validation case is a write.
SDK
extraRead-oriented API, default time and row limits. Fail closed when unsure. Not a security boundary.
await db.query()
Provider
extraEngine-specific checks. SQL policy on the Postgres driver. Read APIs only on Firestore. Not a security boundary.
npm i @db-sdk/postgres
Runtime
extraTimeouts, required or injected limits, abort signals, and bounded result payloads.
AbortSignal.timeout()
Database
the lockThe lock: a dedicated read-only role or IAM principal, restricted schemas, TLS.
GRANT SELECT
Allowed vs not allowed
If a statement cannot be shown to be a read, it should not run. That fail-closed rule is extra — not a substitute for a read-only role.
Allowed
Credentials
In memory, supplied by the host
SQL
SELECT / WITH … SELECT
Documents
get / list / find
Results
A bounded result set
Sessions
Time out a slow query
Not allowed
Credentials
Persist, log, or send to a third party
SQL
INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, GRANT
Documents
set / update / delete / writeBatch
Results
Dump an entire table or collection
Sessions
Hold an unbounded session
Who does what
If a host skips these, that is a host bug — not “DB SDK has write access.” Treat a DB SDK host like any BI or support tool.
Host must
- 01
Encrypt credentials at rest
Decrypt only on the server, for the duration of a test or query.
- 02
Never send secrets to the browser
Clients may see connection names and catalogs, not URLs or keys.
- 03
Scope by tenant
An agent or user may only use connections that tenant attached.
- 04
Prefer a dedicated role
Ask customers for a read-only user, not a superuser URL.
- 05
Do not treat the SDK as a warehouse
Avoid archiving full result dumps.
Customer should
- 01
Create a read-only role
SELECT only, or Firestore / IAM that can only read.
- 02
Restrict schemas and collections
Do not expose payroll or secrets tables if the product does not need them.
- 03
Use TLS
Do not disable SSL in production.
- 04
Network-restrict if you can
Allowlist, VPC, tunnel, or private link.
- 05
Rotate and revoke
Rotate credentials when people leave, and revoke access in the product when you disconnect.
Typical read-only roles
Prefer credentials that cannot write even if the SDK is wrong.
Postgres
CREATE ROLE db_sdk_reader LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE app TO db_sdk_reader;
GRANT USAGE ON SCHEMA public TO db_sdk_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO db_sdk_reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO db_sdk_reader;MySQL
CREATE USER 'db_sdk_reader'@'%' IDENTIFIED BY '...';
GRANT SELECT ON app.* TO 'db_sdk_reader'@'%';Firestore
title: DB SDK reader
includedPermissions:
- datastore.entities.get
- datastore.entities.listHow a query is checked
Default numeric caps will be chosen in implementation. Hosts should be able to tighten them.
Relational
Postgres first, then other SQL drivers
- Reject statements that are not SELECT or WITH … SELECT
- Reject mutation, DDL, and multi-statement batches
- Parameterized queries — values are never concatenated
- Row cap even if the query omits LIMIT / TOP
- Statement timeout
- Dialect details stay in the driver
Document
Firestore first
- Call read APIs only
- Collection from the catalog or an allow list
- Limit required on every query
- No write aggregations, $out / $merge, or write transactions
- Sample for schema — do not download the collection
AI-generated queries
DB SDK does not call a model and does not take an AI API key. Model output is untrusted input.
- 01
Use introspect() as context — do not give the model raw credentials.
- 02
Pass the model output into query() as untrusted input.
- 03
Never skip provider checks because “the model was instructed to only SELECT”.
- 04
Keep the AI API key in the host; the SDK does not need it.
Credentials stay in memory
| Provider | Typical | Prefer |
|---|---|---|
| Postgres | URI or host / user / password | CONNECT + SELECT only |
| Firestore | Google OAuth, then IAM | datastore get / list only, not Editor |
| Later drivers | URI or provider config | Equivalent read-only role |
Data flow
Nothing in this path should write to the customer database.
01
Client
Never sees the connection secret.
02
Host
Decrypts credentials, checks tenant access. A model may draft a query from the catalog.
03
DB SDK
Validate, then a bounded read-only query.
04
Database
Returns a bounded result. Nothing in this path should write.
05
Client
An answer and/or a small result table.
If a host is compromised
An attacker may read whatever the stored role can read. They should not be able to change data through DB SDK. Stolen credentials should still be read-only.
This project does not
- Train models on customer data
- Sell or share rows
- Bypass RLS, grants, or Firestore rules
- Require a public IP
A write that runs through query() is a vulnerability. The SDK authenticates as the principal you provided — it does not bypass RLS, grants, or Firestore rules.
Report privately
If you believe DB SDK can write, bypass read-only policy, or leak credentials, do not open a public GitHub issue with an exploit.