Skip to content

Connect Snowflake

Set up key-pair authentication for a read-only Snowflake user, add the data source, and keep the per-refresh credit cost predictable.

Snowflake, via key-pair authentication. Dashies signs a JWT with your private key and calls the SQL API. There is no password and no host to enter.

Before you start

Two things about Snowflake catch people out more than anything else on this page, so read them first.

Use the hyphenated account identifier

The identifier must be the hyphen form, myorg-myaccount. The dotted org form myorg.myaccount is rejected outright with:

account_identifier is invalid. Use the hyphenated form (for example myorg-myaccount), not the dotted org form.

A dotted identifier is accepted only in the legacy locator-with-region shape, such as xy12345.ap-southeast-2.aws.

An account network policy will look like an auth failure

If your account has a NETWORK POLICY that allowlists office or VPN addresses, it blocks Dashies, and Snowflake reports that as a rejected sign-in rather than a blocked address. Check for one before you go hunting for a key problem. The Dashies error text names this possibility for the same reason.

1. Create the key pair

Run these on your own machine. The private key stays with you until you paste it into the Dashies form; the public key goes on the Snowflake user.

openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out dashies_key.p8 -nocrypt
openssl rsa -in dashies_key.p8 -pubout -out dashies_key.pub

dashies_key.p8 is the PKCS#8 private key you paste into Dashies. dashies_key.pub holds the public key you register on the Snowflake user in the next step. When you register it, paste only the base64 body, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- lines.

2. Create the warehouse, role, and user

Run this in a Snowflake worksheet as ACCOUNTADMIN. It creates a dedicated X-Small warehouse, a read-only role, and a service user carrying the public key.

create warehouse if not exists DASHIES_WH
  warehouse_size = XSMALL
  auto_suspend = 60
  auto_resume = true
  initially_suspended = true;

create role if not exists DASHIES_RO;

grant usage on warehouse DASHIES_WH to role DASHIES_RO;
grant usage on database ANALYTICS to role DASHIES_RO;
grant usage on schema ANALYTICS.PUBLIC to role DASHIES_RO;
grant select on all tables in schema ANALYTICS.PUBLIC to role DASHIES_RO;
grant select on future tables in schema ANALYTICS.PUBLIC to role DASHIES_RO;

create user if not exists DASHIES_SVC
  type = SERVICE
  login_name = 'DASHIES_SVC'
  default_role = DASHIES_RO
  default_warehouse = DASHIES_WH;

grant role DASHIES_RO to user DASHIES_SVC;

alter user DASHIES_SVC set rsa_public_key = 'PASTE_THE_BASE64_BODY_OF_dashies_key.pub';

Replace ANALYTICS and ANALYTICS.PUBLIC with your own database and schema, and repeat the two grant select lines for every schema you want readable.

LOGIN_NAME must match what you type into Dashies

The JWT identity Dashies presents is derived from the user name you enter. If the user's LOGIN_NAME differs from it, Snowflake rejects the sign-in. Keeping them identical, as above, avoids the whole class of problem.

The grant select on future tables line matters for the same reason alter default privileges does on Postgres: without it, a table created later is invisible and the refresh that needed it breaks with no warning.

3. Add the data source

Open dashies.xyz/app/connections, click to add a data source, and pick Snowflake.

FieldRequiredExampleNotes
Account identifieryesmyorg-myaccountHyphen form. See above.
UseryesDASHIES_SVCThe login name of the read-only user.
WarehouseyesDASHIES_WHThe warehouse refresh queries run on.
RolenoDASHIES_RO
DatabasenoANALYTICS
Private keyyesa PKCS#8 PEM blockThe contents of dashies_key.p8.
Display namenoAnalytics warehouseUp to 120 characters.

Warehouse, role, and database are upper-cased on the way in, because an unquoted Snowflake identifier resolves upper case.

The host is derived from the account identifier, so there is nothing to enter and no address to allowlist on our side.

4. It verifies in one step

Snowflake is a single-step connect. Creating the data source stores the key, mints the key-pair JWT, and runs two metadata probes, then comes back active with the databases it found, or error with a class. Up to 200 databases are stored as the allowlist. There is no Resync button on Snowflake.

If it fails:

MessageMeaning
Snowflake denied access. Check that the key pair is registered on the user (RSA_PUBLIC_KEY) and that the account identifier is correct. An account-level network policy can also block access.Any authentication failure.
The warehouse was not found or the user's role cannot use it. Check the warehouse name and its grants.The warehouse name is wrong, or the role has no usage on it.
We couldn't read that account. Check the account identifier and try again.The account identifier resolves but the probes did not succeed.

Cost

A cold refresh costs at least 60 seconds of credits

Snowflake bills a 60-second minimum every time a warehouse auto-resumes. A scheduled refresh that finds the warehouse suspended therefore costs at least one minute of that warehouse's credits, however short the query is.

Two things keep this predictable:

  • Use a dedicated X-Small warehouse with auto_suspend = 60, as the script above creates. Do not point Dashies at a large shared warehouse.
  • Match the refresh cadence to how fast the data actually moves. Hourly refresh of a month-grained cube buys nothing and pays the 60-second floor 24 times a day. See Set a refresh schedule.

Connecting itself is free. The verification runs SHOW DATABASES and SHOW WAREHOUSES LIKE, which are metadata-only and burn zero warehouse credits.

Caps

LimitValueWhat happens past it
Rows per dataset query100,000Refused before any partition is fetched.
Inline result partitions32Query errors.
Statement wait30 seconds, then pollingQuery errors if it never completes.
Data island, whole dashboard8,388,608 bytesPublish is refused.
Compiled dashboard body5,242,880 bytesPublish is refused. Usually binds first.

There is no execution-time byte cap the way there is on Postgres. The adapter reads Snowflake's own row total up front and refuses before fetching. A wide cube is still bounded, one layer later, by the island and body limits.

Snowflake supports every materialization, including Parquet offload for a row-level dataset.

How these ceilings relate to each other, and which one binds first, is in sizes and ceilings.

Dialect notes

Cube SQL is Snowflake SQL.

  • Table references are database-qualified: from ANALYTICS.PUBLIC.ORDERS.

  • Bucket dates in your business time zone, in the SQL:

    select date_trunc('MONTH', convert_timezone('UTC', 'America/Los_Angeles', ordered_at)) as month,
           sum(amount) as revenue
    from ANALYTICS.PUBLIC.ORDERS
    group by 1
    order by 1
  • Relative window: dateadd('month', -12, current_timestamp()). Conditional count: count_if(c). Exact median: percentile_cont(0.5) within group (order by x).

  • Snowflake folds an unquoted output alias to upper case. That is handled for you: a case-only difference between the alias and your declared measure key is canonicalized at publish and at refresh, so sum(amount) as revenue against a measure named revenue is correct. You do not need upper-case manifest keys. What is refused, loudly and naming both, is two output columns that differ only by case landing on one declared key.

  • A VARIANT, OBJECT, or ARRAY column has to be addressed into before it is usable, and lateral flatten multiplies rows. Nothing rejects that, so the cube runs and the numbers are simply wrong. Aggregate back to the grain you meant. See Verify your numbers.

Rotating the key

Edit the data source and paste a new PKCS#8 PEM under Private key, then register the matching public key on the Snowflake user.

Check it worked

  1. The data source reads active on dashies.xyz/app/connections, and lists the databases it discovered.

  2. Ask your AI tool to introspect it and run one query:

    Introspect my Snowflake data source, then validate this cube SQL against it: select 1 as ok

  3. Then author a dashboard against it.