Zero-Trust SDK Architecture for Headless Embedded Analytics
How QueryPanel's headless Node SDK keeps database credentials and raw query results in your backend while generating tenant-aware SQL and charts.
Zero-trust embedded analytics is not "no external service ever sees anything." It is a specific data boundary: your database credentials stay in your backend, SQL runs through a database adapter you control, and raw result rows return directly to your application.
Last reviewed August 10, 2026 against the current Node SDK query engine and API v2 tenant-verification path.
For the product decision between a ready-made React workspace and a custom UI path, read Headful vs Headless Embedded Analytics SDKs first, then return here for the credential and callback model.
Short answer: QueryPanel's primary product is its headful React SDK for teams that want a complete customer analytics workspace quickly. This article covers the secondary headless Node SDK path. In that architecture, QueryPanel receives schema metadata, the user's question, tenant context, and anonymized result shapes needed for chart generation, but it does not receive your database credentials or raw query values. Your backend retains final control over validation and execution.
Key takeaways
- The zero-trust claim applies to the headless Node SDK path. Do not extend it to every QueryPanel deployment or embed model.
- Credentials stay in your infrastructure. Only the database callback or adapter attached to the Node SDK uses them.
- Raw query values stay in your application. Chart generation gets field names and anonymized value types, not the original result values.
- QueryPanel Cloud still receives necessary context: schema metadata, natural-language questions, tenant settings, generated SQL, and request/session metadata.
- Tenant isolation is layered. The v2 API instructs generation to use the configured tenant field, verifies the filter deterministically, and the local query engine applies its configured tenant safeguard before execution.
- Database permissions remain authoritative. Read-only roles, allowed tables, RLS or warehouse policies, query limits, and audit logging should reinforce the SDK controls.
Where this architecture fits
QueryPanel offers two delivery paths:
- Headful React SDK: the default for a complete Notion-like dashboard workspace, built-in AI assistant, and tenant-level customization.
- Headless Node SDK: the custom-control path when your product owns the interface and requires backend-controlled query execution.
Choose the headless path when:
- database credentials must stay in customer-controlled infrastructure
- raw result rows must not pass through the QueryPanel API
- the product needs a fully custom analytics interface
- your backend must apply additional SQL review, cost controls, or audit behavior
- network policy prevents an external service from connecting directly to the database
The tradeoff is product scope. Your team owns the input, chart rendering, saved state, errors, follow-up behavior, and the rest of the customer experience.
The headless request flow
The current flow separates generation from execution:
This diagram makes the boundary explicit:
- QueryPanel generates and reviews SQL using the schema and business context you synced.
- Your Node process validates and executes that SQL through the adapter you supplied.
- The database connection string or password is never sent to QueryPanel.
- Raw values remain local; only their field names and value types are used for remote chart-spec generation.
What QueryPanel receives
Be precise about what "zero trust" covers here. In the headless path, QueryPanel receives the information needed to generate a useful query:
- table, column, type, relationship, and schema metadata
- annotations, glossary terms, and gold SQL examples you choose to sync
- the natural-language question
- database dialect and logical database name
- tenant field configuration and the tenant context for the request
- generated SQL, parameter metadata, rationale, and session identifiers
- field names and anonymized result shapes for chart generation
QueryPanel does not receive:
- database connection strings
- database usernames or passwords
- private database keys
- raw query result values in the headless
ask()flow
If your policy prohibits sending schema names, user questions, tenant identifiers, or anonymized result shapes to an external service, the standard headless cloud flow still needs additional review. "Credentials and rows stay local" is not the same as "no metadata leaves the system."
Attach a database through a controlled callback
The Node SDK accepts a function that executes SQL with your existing server-side database client:
import {
type PostgresClientFn,
QueryPanelSdkAPI,
} from "@querypanel/node-sdk";
const executePostgres: PostgresClientFn = async (sql, params) => {
const result = await pool.query(sql, params);
return {
rows: result.rows,
fields: result.fields.map((field) => ({ name: field.name })),
};
};
const qp = new QueryPanelSdkAPI(
"https://api.querypanel.io",
process.env.QUERYPANEL_PRIVATE_KEY!,
process.env.QUERYPANEL_WORKSPACE_ID!,
);
qp.attachPostgres("analytics", executePostgres, {
database: "product_analytics",
defaultSchema: "public",
allowedTables: ["public.orders", "public.accounts"],
tenantFieldName: "tenant_id",
tenantFieldType: "String",
enforceTenantIsolation: true,
});
The callback closes over your connection pool. QueryPanel receives neither the pool nor its credentials.
Use a read-only database role with access only to the required schemas and tables. The SDK's allowedTables option adds another restriction, but it should complement database permissions rather than replace them.
Sync only the schema you intend to query
Schema sync introspects the attached adapter and sends metadata to QueryPanel's ingest API:
await qp.syncSchema("analytics", {
tenantId: "schema-sync",
tables: ["orders", "accounts"],
});
The synced payload can include schema names, tables, columns, types, relationships, descriptions, and configured tenant settings. It does not include database credentials or table rows.
Treat schema metadata as potentially sensitive. Limit the tables you expose, avoid putting secrets into comments or annotations, and review glossary or gold-query content before syncing it.
Ask a question from authenticated server context
Resolve the customer in your backend before calling ask():
const customer = await requireCustomerSession(request);
const result = await qp.ask("Show revenue by month", {
database: "analytics",
tenantId: customer.tenantId,
pipeline: "v2",
chartType: "vizspec",
debug: false,
});
return {
rows: result.rows,
chart: result.chart,
rationale: result.rationale,
querypanelSessionId: result.querypanelSessionId,
};
Do not accept the final tenant ID from request JSON or a URL parameter. The user may ask the question in the browser, but your authenticated backend must decide which tenant is allowed to answer it.
Tenant isolation uses multiple checks
The older version of this article described tenant filtering as only a local SDK injection. The current path is layered.
1. Generation receives explicit tenant rules
The Node SDK forwards the configured tenantFieldName, field type, and enforcement flag. The v2 SQL generator is instructed to include that field as a bind parameter rather than a literal value.
2. The API verifies generated SQL
After generation and reflection, the v2 pipeline deterministically checks that required SQL contains the configured tenant field in a filter and that the tenant value is parameterized. A missing or literal tenant filter fails before the query returns to the SDK.
3. The Node SDK applies its local safeguard
Before execution, the local query engine applies the configured tenant safeguard, validates SQL through the attached adapter, and executes through your callback.
4. Your database remains the final boundary
Use the controls that match your architecture:
- read-only database identities
- Postgres RLS or warehouse row policies where appropriate
- schema or database routing for isolated tenants
- allowed tables and views
- statement timeouts and row/byte limits
- tenant-aware cache keys
- audit logs and two-tenant regression tests
No single regex, prompt instruction, or dashboard filter should be treated as the entire security model.
Local validation and execution controls
The adapter validates and executes inside your Node process. For Postgres, validation runs EXPLAIN with the bound parameters before normal execution. Table allowlists reject referenced tables outside the configured set.
Because the callback is application-owned, you can add controls such as:
- statement timeouts
- read-only transactions
- query cost or byte limits
- additional SQL parsing or policy checks
- structured audit logging
- cancellation when the user disconnects
Document which controls come from QueryPanel, which come from your callback, and which the database enforces. Security reviews fail when those layers collapse into one vague guarantee.
What this architecture does not guarantee
The headless zero-trust path does not automatically provide:
- compliance certification for your application
- safe database permissions
- correct business metric definitions
- complete protection from expensive but valid queries
- secure cache or export behavior in your own code
- authorization for tenant IDs supplied by an untrusted browser
You get a narrower, useful guarantee: QueryPanel does not need database credentials or raw result values to generate SQL and chart specs, and your backend keeps the execution boundary.
A two-tenant verification plan
Before production:
- seed Tenant A and Tenant B with visibly different totals
- resolve both tenants through real authenticated server sessions
- ask broad questions that do not mention a tenant
- inspect generated SQL and bound parameters
- try a missing tenant, altered browser value, and expired session
- test saved results, exports, caches, and follow-up questions in your own UI
- force invalid and expensive queries in staging
- confirm logs do not contain credentials or raw sensitive values
The callback architecture gives your code a final checkpoint. Use it.
FAQ
What does zero-trust mean in QueryPanel's headless SDK?
It means database credentials and raw query values remain in your backend. QueryPanel generates SQL and chart specifications from schema, question, tenant, and anonymized shape context while your attached adapter validates and executes locally.
Does no data leave my infrastructure?
Raw database result values stay local in the headless ask() flow. Schema metadata, questions, tenant context, SQL, field names, and anonymized value types are sent to QueryPanel. Evaluate those metadata flows against your policy.
Is the headful React SDK also zero trust?
Do not apply the headless claim automatically to the headful embed. The React SDK is QueryPanel's primary product for a complete workspace; its managed API flow has a different data path. Choose the headless Node SDK when local execution and raw-result boundaries are required.
How is tenant isolation enforced?
The v2 generator receives the tenant configuration, the API verifies the required parameterized filter, and the Node SDK applies its configured local safeguard before adapter validation and execution. Database permissions and application auth should reinforce those controls.
Does QueryPanel store my database password?
Not in the headless callback architecture. Your callback or adapter uses credentials held by your backend, and the SDK does not send those credentials to QueryPanel.
Can I restrict which tables QueryPanel queries?
Yes. Configure allowedTables and use a database identity restricted to the same tables or views. The database permission should remain the authoritative control.
Which QueryPanel path should I start with?
Start with the headful React SDK when you want the fastest complete dashboard workspace and AI-assisted customer customization. Choose the headless Node SDK when you require custom UI or backend-controlled zero-trust execution.
What should I read next?
Use the getting-started guide for the headful embed path, the React and Postgres tutorial for implementation detail, and the tenant-isolation buyer guide for a broader proof-of-concept checklist.
Zero trust is useful only when the boundary is explicit. In QueryPanel's headless Node SDK, credentials and raw result values stay in your backend; schema, question, tenant, SQL, and anonymized chart-shape context support generation outside it.