Security model

SnowClaw is designed to be safe to run in your Snowflake account by default. Every security decision below is on by default — you don't have to opt in.

Network egress control

All outbound traffic is deny-by-default. Only explicitly approved hosts (managed via snowclaw network) get Snowflake network rules and external access integrations. The container cannot reach the internet unless you allow it.

Setup auto-detects required hosts from your provider, channel, and tool selections, shows you the diff, and prompts before applying.

SPCS ingress control

Snowflake handles TLS termination and authentication on the single public endpoint (port 18789). There are no open ports or exposed services beyond what SPCS declares — the ingress surface is managed entirely by Snowflake's infrastructure.

The Cortex proxy sidecar is internal only. It does not bind to any public port.

File permissions at runtime

The Docker entrypoint runs as root, locks down config files, and drops to the node user (UID 1000) before starting OpenClaw:

  • openclaw.jsonroot:node 440 (read-only for gateway, no write for agent)
  • credentials/root:node 440
  • secrets.jsonroot:node 440
  • workspace/ and skills/node:node (writable)
  • .snowflake/connections.toml → owned by node so Cortex can read/write connection state

The agent process cannot modify its own config or credentials. Two independent layers enforce this: OS permissions and the tools.fs.workspaceOnly: true policy in the generated openclaw.json, which restricts agent file tools to the workspace directory.

Role separation

  • Admin role (default: SYSADMIN) — used by the CLI for provisioning: creates the database, schema, image repo, stage, compute pool, secrets, network rule, and external access integration. Lives on your machine; never enters the container.
  • Runtime role — owns the SPCS service. Gets only the grants it needs at runtime: USAGE on DB/schema/EAI/pool, READ + WRITE on the state stage, READ on the image repo, MONITOR on the pool, READ on each secret, BIND SERVICE ENDPOINT ON ACCOUNT, and DATABASE ROLE SNOWFLAKE.CORTEX_USER. Nothing else. You create this role yourself once per Snowflake account, before running snowclaw setup, and grant it to the admin role. See Snowflake privileges for the one-time setup SQL.

The container runs under the runtime role. Every Snowflake PAT that lives inside the container ({prefix}_sf_token) is ROLE_RESTRICTION-scoped to the runtime role, so a compromise of that token cannot alter network rules, mint new secrets, or create sibling services.

Snowflake blocks GRANT OWNERSHIP ON SERVICE, so snowclaw deploy hands ownership to the runtime role by having the runtime role create the service in the first place — via a transient CREATE SERVICE grant that's revoked immediately afterwards. See Snowflake privileges for the exact grant matrix, the "transient CREATE SERVICE dance", and the admin-PAT recipe.

Two PATs

SnowClaw uses two separate Snowflake PATs, each minted by you during setup:

  • Admin PAT — in .env as SNOWFLAKE_TOKEN. Used by the CLI on your machine for provisioning and snowclaw push / pull. Never uploaded to Snowflake as a secret. Should be ROLE_RESTRICTION-scoped to the admin role.
  • Runtime PAT — stored as the {prefix}_sf_token Snowflake secret and bound into both containers as SNOWFLAKE_TOKEN. Used by Cortex Code, snowsql, and the Cortex proxy inside the service. ROLE_RESTRICTION-scoped to the runtime role. The setup wizard prints the exact ALTER USER ... ADD PROGRAMMATIC ACCESS TOKEN command and prompts for the returned value.

SNOWCLAW_MASK_VARS always includes SNOWFLAKE_TOKEN so that if an agent ever smuggled the runtime PAT into an LLM request body, the proxy masker would redact it before it left the sidecar.

Secret masking

The Cortex proxy sidecar scans all outbound LLM messages and replaces known secret values with [REDACTED:VAR_NAME]. Credentials never reach the model, even if the agent tries to include them in a prompt.

Variables listed in SNOWCLAW_MASK_VARS (auto-generated from your .env) are added to the masking set. By default, all token and API key variables are masked. You can extend this list manually.

The masker uses two shape-specific walkers — one for OpenAI-style messages[].content + tool calls, and one for Anthropic-style messages[].content blocks + top-level system. Both preserve cache_control markers and other block metadata.

User-managed secrets

Every KEY=value in .env becomes an individual Snowflake SECRET object at deploy time — never baked into the Docker image. CUSTOM_-prefixed variables are auto-registered with no extra config. Secrets are mounted into the container as env vars at runtime.

Secrets are never retrievable — Snowflake stores them as encrypted blobs. .env is your source of truth.