Snowflake Integration Guide
This guide covers how to connect a Snowflake warehouse to BurnLedger, configure query templates, and set up a read-only role. The connector is implemented and registered as connector type snowflake.
Connection Configuration
BurnLedger connects to Snowflake using a standard DSN with key-pair or password authentication.
Configuration fields:
| Field | Required | Description |
|---|---|---|
account |
Yes | Snowflake account identifier (e.g., xy12345.us-east-1). |
username |
Yes | Snowflake username. |
password |
Yes* | Password (for password auth). |
private_key |
No* | Base64-encoded RSA private key (for key-pair auth). |
database |
Yes | Database name. |
schema |
Yes | Schema name. |
warehouse |
Yes | Warehouse name. |
role |
No | Snowflake role (defaults to user's default role). |
*Provide either password or private_key. The private key must be PKCS8 (raw base64-encoded DER, or base64-encoded PEM); key-pair auth switches the driver to JWT.
These are the exact JSON keys the connector reads. private_key is snake_case in both SDKs — do not write privateKey. There is no read_only key for Snowflake: privileges are always introspected (see Read-Only User Setup).
TypeScript
const system = await bl.registerSystem({
name: "analytics-users",
connectorType: "snowflake",
connectionConfig: {
account: "xy12345.us-east-1",
username: "BURNLEDGER_RO",
password: process.env.SNOWFLAKE_PASSWORD,
database: "ANALYTICS",
schema: "PUBLIC",
warehouse: "BURNLEDGER_WH",
role: "BURNLEDGER_READONLY",
},
subjectQuery: "SELECT * FROM users WHERE email = ?",
});
Python
system = bl.register_system(
name="analytics-users",
connector_type="snowflake",
connection_config={
"account": "xy12345.us-east-1",
"username": "BURNLEDGER_RO",
"password": os.environ["SNOWFLAKE_PASSWORD"],
"database": "ANALYTICS",
"schema": "PUBLIC",
"warehouse": "BURNLEDGER_WH",
"role": "BURNLEDGER_READONLY",
},
subject_query="SELECT * FROM users WHERE email = ?",
)
Query Template Format
Standard SQL with the ? positional placeholder, which is what the gosnowflake driver binds (the same placeholder as MySQL — PostgreSQL and Redshift use $1 instead). Snowflake SQL syntax is supported.
Rules, all enforced at registration:
- Must be a single read-only statement:
SELECT, orWITH … SELECT. Stacked statements (;), dollar-quoted strings,FOR UPDATE/FOR SHARElocking clauses, and any write/DDL keyword (INSERT,UPDATE,DELETE,MERGE,CREATE,INTO,CALL,SET, …) are rejected — including inside a CTE. - Must contain a
WHEREclause. - Must contain the
?placeholder. A template without it is rejected; it would match every row and could certify a false "0 records" result for the subject. SELECT *is allowed but produces a warning — listing explicit columns keeps hashes stable across schema changes.
SELECT * FROM users WHERE email = ?
SELECT u.id, u.email, u.name, o.order_id
FROM users u JOIN orders o ON o.user_id = u.id
WHERE u.email = ?
Read-Only User Setup
BurnLedger verifies the credential is read-only during connection validation — without ever writing. It runs SHOW GRANTS TO ROLE IDENTIFIER(CURRENT_ROLE()) and SHOW GRANTS TO USER IDENTIFIER(CURRENT_USER()) and refuses the credential if any grant is write-capable: INSERT, UPDATE, DELETE, TRUNCATE, MODIFY, OWNERSHIP, WRITE, ALL [PRIVILEGES], or any CREATE <object> privilege.
There is no read_only assertion escape hatch for Snowflake. The role must be able to run SHOW GRANTS on itself and on the connecting user — if those statements fail, BurnLedger cannot prove the credential is read-only and refuses the connection (fails closed).
-- Create a dedicated warehouse (X-Small to minimize cost)
CREATE WAREHOUSE BURNLEDGER_WH
WAREHOUSE_SIZE = 'XSMALL'
AUTO_SUSPEND = 60
AUTO_RESUME = TRUE
INITIALLY_SUSPENDED = TRUE;
-- Create a read-only role
CREATE ROLE BURNLEDGER_READONLY;
-- Grant usage on the warehouse, database, and schema
GRANT USAGE ON WAREHOUSE BURNLEDGER_WH TO ROLE BURNLEDGER_READONLY;
GRANT USAGE ON DATABASE ANALYTICS TO ROLE BURNLEDGER_READONLY;
GRANT USAGE ON SCHEMA ANALYTICS.PUBLIC TO ROLE BURNLEDGER_READONLY;
-- Grant SELECT on specific tables
GRANT SELECT ON TABLE ANALYTICS.PUBLIC.USERS TO ROLE BURNLEDGER_READONLY;
GRANT SELECT ON TABLE ANALYTICS.PUBLIC.ORDERS TO ROLE BURNLEDGER_READONLY;
-- Or grant SELECT on all tables in the schema
GRANT SELECT ON ALL TABLES IN SCHEMA ANALYTICS.PUBLIC TO ROLE BURNLEDGER_READONLY;
GRANT SELECT ON FUTURE TABLES IN SCHEMA ANALYTICS.PUBLIC TO ROLE BURNLEDGER_READONLY;
-- Create the user
CREATE USER BURNLEDGER_RO
PASSWORD = 'your-secure-password'
DEFAULT_ROLE = BURNLEDGER_READONLY
DEFAULT_WAREHOUSE = BURNLEDGER_WH;
GRANT ROLE BURNLEDGER_READONLY TO USER BURNLEDGER_RO;
Snowflake-Specific Considerations
Warehouse costs
BurnLedger queries trigger warehouse compute. Use an X-Small warehouse with auto-suspend to minimize cost. Each attestation/verification is a single query — compute time is typically seconds.
Time Travel
Snowflake retains historical data via Time Travel. BurnLedger queries the current state only. If your compliance requires proving data was removed from Time Travel, set the table's DATA_RETENTION_TIME_IN_DAYS to 0 after deletion.
Fail-safe
Snowflake's 7-day fail-safe period retains data after Time Travel expires. BurnLedger cannot observe fail-safe data. Discuss with your compliance team whether fail-safe constitutes "storage" under your applicable regulation.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
CONNECTION_FAILED |
Invalid account identifier or credentials | Verify the account ID format and credentials. |
WRITE_ACCESS_DETECTED |
A grant on the current role or user is write-capable (INSERT/UPDATE/DELETE/OWNERSHIP/CREATE <object>/…) |
Revoke write privileges from the role, or connect with a role that has only SELECT and USAGE. |
CONNECTION_FAILED with "failed to verify Snowflake credential is read-only" |
SHOW GRANTS TO ROLE/TO USER could not run for this credential |
Supply a role whose grants can be read. There is no read_only config flag for Snowflake. |
INVALID_QUERY_TEMPLATE |
Template is not a single read-only SELECT, has no WHERE, or has no ? placeholder |
Rewrite per Query Template Format. |
CONNECTION_FAILED with "warehouse suspended" |
Warehouse is suspended and AUTO_RESUME is false | Set AUTO_RESUME = TRUE on the warehouse. |