RLS Parameters (Row-Level Security)

Restrict which rows of data an organization can see using RLS parameters: how to define them in SBT, what subscribers see at install, and how to write model code that filters correctly.

New to SBT? Start with the SBT Overview. Everything on this page was verified working in production on sbt 1.0.13 (August 2026).

Two things called "RLS" at Seek

Seek currently has two different mechanisms people call row-level security. Do not mix them up.

  1. Sigma user attributes — security applied at the dashboard layer, per user, using Sigma attribute values (the SEEK_ORG_FILTERS pattern). This is how existing GTM apps restrict data today.

  2. SBT RLS parameters — security applied at the data layer, per organization, using a parameter marked rls: true in your model config. The app's output tables are physically filtered by the values the organization chose at install time.

This article covers the second one.

What an RLS parameter does

A normal parameter is a knob anyone with variant access can turn. An RLS parameter is different in three ways:

  • It gates the install. When an organization installs (or upgrades to) an app version with an RLS parameter, they see a Set Data Access screen and cannot proceed until they choose values. The screen says: "This app requires [PARAM] selection(s) to define the data you will have access to in this app."

  • Its values come from a list you control. The subscriber picks from a dropdown populated by your values_query. They cannot type arbitrary values.

  • It is multi-select and stored as a list. The chosen values appear on the variant card with a shield icon, as a list (for example ['West']). Your model code must handle a list.

Defining an RLS parameter

An RLS parameter is a normal model variable with two extra requirements: rls: true and a values_query.

In your model config yaml:

models:
  - name: region_share
    config:
      language: python
      materialized: table
      type: output
      write_mode: overwrite
      packages:
        - "snowflake-snowpark-python"
      variables:
        - name: REGION_FILTER
          display_name: Region Filter
          type: string
          default: East
          rls: true
          values_query: regions

And the helper file helpers/regions.sql:

select distinct REGION as value, REGION as label
from <YOUR_DB>.<YOUR_SCHEMA>.<YOUR_TABLE>

Both column aliases are required — a values query must return exactly two columns named value and label (the rule is explained in Helpers & the Run Context).

⚠️ Known issue: if your values query is missing, or returns columns with any other names, the Set Data Access dropdown renders empty and the page returns a 500 error. The install cannot proceed and there is no helpful error message. sbt will happily build and publish the broken app. Until validation is added, triple-check the value / label aliases before publishing.

Writing model code that uses an RLS parameter

RLS values arrive as a list, because subscribers can select more than one. Filter with isin, never with ==. An equality comparison against a list silently matches nothing and your output table comes back empty with no error.

def model(session, seek):
    from snowflake.snowpark.functions import col
    import ast

    region = seek.params['REGION_FILTER']
    # The value may arrive as a list or as a stringified list. Handle both.
    if isinstance(region, str) and region.strip().startswith('['):
        region = ast.literal_eval(region)
    if not isinstance(region, (list, tuple)):
        region = [region]

    df = session.table(seek.models['upstream_model'])
    return df.filter(col('REGION').isin(list(region)))

What the subscriber experiences

  1. They install the app (or upgrade to a version that has an RLS parameter).

  2. The Set Data Access screen appears with a required multi-select for each RLS parameter. The options come from your values query, which the platform runs at that moment.

  3. They pick values and click Save & Manage Variant.

  4. Saving triggers a sync engine reconciliation and completes the install. The chosen values appear on the variant card with a shield icon.

  5. Runs filter data to the chosen values.

Current limitations (verified August 2026)

  • Selections are not remembered across versions. Every upgrade to a new app version re-opens Set Data Access with an empty selection, even if the organization already chose values. Re-select on every upgrade. (Improvement requested.)

  • The values query must be able to run against real tables at install time. If the tables behind it are missing or inaccessible to the platform, the dropdown will be empty.

  • Values in your source data that you may not expect (including placeholder values like "NA") will appear as options, because the values query faithfully returns whatever is in the data.

Troubleshooting

Symptom

Cause

Fix

Set Data Access dropdown is empty, page may show 500

values_query missing, or its columns are not aliased value and label

Fix the helper SQL, bump version, rebuild, republish

Output table has zero rows, run succeeded

Model compares the RLS value with ==

Use .isin() — values are a list

Run fails with KeyError: '<PARAM>' right after an upgrade

Variant still carries the old parameter set

Run again; the variant's parameters refresh after the first attempt (known issue)

Asked to Set Data Access again after upgrading

Current behavior; selections are per-version

Re-select and save

Related articles

⏭️ Next: Publisher Snowflake Setup (Grants)