Snowflake privileges

SnowClaw splits Snowflake access into two roles so that a compromise of the running container never lets an attacker alter your Snowflake account.

  • Admin role — used by the CLI on your machine. Provisions infrastructure: database, schema, image repo, stage, secrets, compute pool, network rule, external access integration.
  • Runtime role — owns the SPCS service. Has only the grants needed to read secrets, read the image, read/write the state stage, use the compute pool, reach the network through the EAI, and use Cortex. Nothing else.

You create both roles yourself, once per Snowflake account, before running snowclaw setup. SnowClaw does not auto-provision them — minting roles would require giving the admin role CREATE ROLE ON ACCOUNT, which is not a privilege most accounts will hand out for a CLI.

Each role has its own Snowflake PAT:

  • The admin PAT lives in .env (as SNOWFLAKE_TOKEN) on your machine. It is never uploaded to Snowflake as a secret.
  • The runtime PAT is ROLE_RESTRICTION-scoped to the runtime role. It is stored as the {prefix}_sf_token Snowflake secret and bound into both containers. A compromised container holds only this PAT, so the blast radius is the runtime role's grants — no more.

Setup prompts for both.

Step 1 — Create the runtime role

USE ROLE USERADMIN;
CREATE ROLE IF NOT EXISTS SNOWCLAW_RUNTIME_ROLE;

That's it. SnowClaw will apply the minimal USAGE/READ grants during snowclaw setup — you don't need to pre-grant anything else to this role.

Step 2 — Create the admin role

ACCOUNTADMIN has everything below and works out of the box. If that's acceptable for your account, skip ahead. Otherwise, run this once as ACCOUNTADMIN (or any role with MANAGE GRANTS), substituting <your_user>:

-- Create the SnowClaw admin role itself.
USE ROLE USERADMIN;
CREATE ROLE IF NOT EXISTS SNOWCLAW_ADMIN_ROLE;
GRANT ROLE SNOWCLAW_ADMIN_ROLE TO USER <your_user>;

-- Let admin impersonate runtime (needed for the transient CREATE SERVICE step).
GRANT ROLE SNOWCLAW_RUNTIME_ROLE TO ROLE SNOWCLAW_ADMIN_ROLE;

-- Account-level privileges the admin role needs for provisioning.
USE ROLE ACCOUNTADMIN;
GRANT CREATE DATABASE         ON ACCOUNT TO ROLE SNOWCLAW_ADMIN_ROLE;
GRANT CREATE COMPUTE POOL     ON ACCOUNT TO ROLE SNOWCLAW_ADMIN_ROLE;
GRANT CREATE INTEGRATION      ON ACCOUNT TO ROLE SNOWCLAW_ADMIN_ROLE;
GRANT BIND SERVICE ENDPOINT   ON ACCOUNT TO ROLE SNOWCLAW_ADMIN_ROLE;
GRANT MANAGE GRANTS           ON ACCOUNT TO ROLE SNOWCLAW_ADMIN_ROLE;

-- Mint a role-restricted PAT for the admin role.
ALTER USER <your_user> ADD PROGRAMMATIC ACCESS TOKEN snowclaw_admin_pat
  ROLE_RESTRICTION = 'SNOWCLAW_ADMIN_ROLE'
  DAYS_TO_EXPIRY = 90;
-- Paste the returned token value into `snowclaw setup` when prompted for your PAT.

When snowclaw setup asks for the admin role, enter SNOWCLAW_ADMIN_ROLE. When it asks for the runtime role, enter SNOWCLAW_RUNTIME_ROLE.

The second token — the runtime-scoped PAT

After snowclaw setup validates the runtime role, it prints a second ALTER USER ... ADD PROGRAMMATIC ACCESS TOKEN command and prompts you to paste the resulting token. Run it in Snowsight (or the SQL IDE of your choice):

ALTER USER <your_user> ADD PROGRAMMATIC ACCESS TOKEN snowclaw_runtime_pat
  ROLE_RESTRICTION = '<your_runtime_role>'
  DAYS_TO_EXPIRY = 90;

SnowClaw stores the returned token as the {prefix}_sf_token Snowflake secret and binds it into both containers at deploy time. Cortex Code, snowsql, and the Cortex proxy all read it. Because it is ROLE_RESTRICTION-scoped to the runtime role, leaking it grants only runtime-role privileges.

Admin role privileges

PrivilegeNeeded because
CREATE DATABASE ON ACCOUNTsnowclaw setup creates {prefix}_db. Skip if you're supplying an existing DB — you need USAGE on it + CREATE SCHEMA on it instead (see below).
CREATE COMPUTE POOL ON ACCOUNTThe SPCS service runs on its own pool ({prefix}_pool) to isolate billing and keep it independently suspendable.
CREATE INTEGRATION ON ACCOUNTExternal access integrations are account-level objects. SnowClaw creates one ({prefix}_external_access) that wraps the approved network rules.
BIND SERVICE ENDPOINT ON ACCOUNTSPCS requires this to expose the public OpenClaw endpoint on port 18789. Without it, CREATE SERVICE succeeds but the endpoint is unreachable.
MANAGE GRANTS ON ACCOUNTLets the admin role grant privileges onward to the runtime role (including BIND SERVICE ENDPOINT and DATABASE ROLE SNOWFLAKE.CORTEX_USER) and handle the transient CREATE SERVICE grant during deploy.

Using a pre-existing database / schema

If you already have a database and schema you want SnowClaw to live in, skip CREATE DATABASE ON ACCOUNT and grant these on the existing objects instead:

USE ROLE ACCOUNTADMIN;
GRANT USAGE                    ON DATABASE <your_db> TO ROLE SNOWCLAW_ADMIN_ROLE;
GRANT USAGE                    ON SCHEMA   <your_db>.<your_schema> TO ROLE SNOWCLAW_ADMIN_ROLE;
GRANT CREATE STAGE             ON SCHEMA   <your_db>.<your_schema> TO ROLE SNOWCLAW_ADMIN_ROLE;
GRANT CREATE IMAGE REPOSITORY  ON SCHEMA   <your_db>.<your_schema> TO ROLE SNOWCLAW_ADMIN_ROLE;
GRANT CREATE SECRET            ON SCHEMA   <your_db>.<your_schema> TO ROLE SNOWCLAW_ADMIN_ROLE;
GRANT CREATE NETWORK RULE      ON SCHEMA   <your_db>.<your_schema> TO ROLE SNOWCLAW_ADMIN_ROLE;
GRANT CREATE SERVICE           ON SCHEMA   <your_db>.<your_schema> TO ROLE SNOWCLAW_ADMIN_ROLE;

Why SYSADMIN alone isn't enough

SYSADMIN holds CREATE DATABASE by default but not CREATE INTEGRATION, CREATE COMPUTE POOL, or BIND SERVICE ENDPOINT — those require ACCOUNTADMIN or an explicit grant. If you enter SYSADMIN at the admin-role prompt without first granting the privileges above, snowclaw setup will fail when it tries to create the compute pool or the EAI.

Runtime role privileges (managed by SnowClaw)

You don't configure these yourself — snowclaw setup and snowclaw deploy apply them for you. They're listed here so you can audit what the runtime PAT actually carries:

  • USAGE on the database, schema, compute pool, external access integration
  • READ + WRITE on the state stage (for workspace file I/O)
  • READ on the image repository (to pull container images)
  • MONITOR on the compute pool (for snowclaw status)
  • READ on each channel / tool / custom secret — this is what SPCS's CREATE SERVICE actually checks when resolving snowflakeSecret: bindings. USAGE looks right by name (it's what UDFs and stored procs use) but SPCS rejects a spec whose creator only holds USAGE.
  • BIND SERVICE ENDPOINT ON ACCOUNT — required because the service exposes a public endpoint on port 18789. Without this, CREATE SERVICE fails with "Please grant BIND SERVICE ENDPOINT to service owner role".
  • DATABASE ROLE SNOWFLAKE.CORTEX_USER — Cortex entitlement for the runtime PAT to hit Cortex LLM endpoints.
  • OWNERSHIP of the SPCS service itself — acquired by CREATE SERVICE, which runs under the runtime role with a transient CREATE SERVICE grant that's revoked immediately after.

Explicitly not granted to the runtime role:

  • Any privilege on the network rule (the runtime role reaches the network through the EAI; direct NR access is unnecessary and would be a privilege escalation path)
  • CREATE NETWORK RULE
  • CREATE SECRET
  • CREATE INTEGRATION
  • Permanent CREATE SERVICE
  • CREATE COMPUTE POOL
  • USAGE on other pools or warehouses
  • OWNERSHIP of anything other than its own service

A compromised runtime PAT inside the container cannot alter network rules, mint new secrets, stand up sibling services, or escalate to admin.

Service ownership — the transient CREATE SERVICE dance

Snowflake blocks GRANT OWNERSHIP ON SERVICE, so the only way for the runtime role to own the service is to create it as the runtime role. snowclaw deploy handles this by:

  1. Issuing GRANT CREATE SERVICE ON SCHEMA … TO ROLE <runtime> (transient).
  2. Calling CREATE SERVICE under the runtime role (the admin holds that role via the GRANT ROLE <runtime> TO ROLE <admin> you ran during Step 2).
  3. Running REVOKE CREATE SERVICE ON SCHEMA … FROM ROLE <runtime> in a finally block — even if CREATE SERVICE failed.

Net result: the runtime role owns the service, cannot create another one, and admin can still USE ROLE <runtime> for break-glass. The same flow runs on both fresh installs and the security_version < 2 migration in snowclaw deploy.