Docs

Tutorial: prove your table is private before you ship

Take the Quickstart notes table, check who can read and change it with a simulation, catch a risky policy before it goes live, and make the check part of your deploys.

Updated Oct 16, 2026Open in console

What you will build: a repeatable check that proves anonymous visitors can't read or change your notes table, and that signed-in users only reach their own notes — run by hand, on a draft change, and before every deploy.

Why: row rules are easy to get almost right. A single using (true) makes every note public, and nothing in your app will show it until someone looks. A simulation looks for you, as every kind of caller, without touching data.

Prerequisites

  • The project and notes table from the Quickstart (with row-level security enabled and forced).
  • A Pro plan or higher — simulation runs are not included in Free (Plan limits).
  • Python 3.10+ for the CLI: pip install sovaria-cli, then sov login.

1. Run a simulation for the table

sov sim run --resource public.notes

What to look for: for Not signed in (__guest__), read, add, update and delete should all be *Denied (expected)*. For Signed-in user, they should be *Allowed* — the row rule then limits them to the user's own notes. The summary ends with the number of findings; there should be no critical ones.

The same result is in the console under Simulation → System map: pick Step by step, choose *Not signed in*, then the Read chip on notes, and press Simulate this request to see the path stop at the policy check.

2. Preview a risky change

Suppose someone proposes this to "fix" an empty list on a public page:

create policy "anyone reads notes" on notes
  for select using (true);

Try it without applying it. The script runs with every check and is rolled back; the answer lists what it would add — here, a new row policy on notes.

sov sim change --sql-file risky.sql

Because using (true) applies to everyone, including visitors, this policy would make every note readable without signing in. Don't apply it; if a page needs public content, put that content in a separate table with its own rules.

3. Check every deploy

--fail-on critical exits with status 1 when a run has critical findings, so it can stop a pipeline. In CI the CLI reads its token from SOVARIA_ACCESS_TOKEN (store it as a CI secret; use a team account set aside for CI) and takes the organization and project as flags.

jobs:
  check-access:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-python@v5
        with: { python-version: '3.12' }
      - run: pip install sovaria-cli
      - run: sov sim run --org "$ORG_ID" --project "$PROJECT_ID" --fail-on critical
        env:
          SOVARIA_ACCESS_TOKEN: ${{ secrets.SOVARIA_ACCESS_TOKEN }}
          ORG_ID: ${{ vars.SOVARIA_ORG_ID }}
          PROJECT_ID: ${{ vars.SOVARIA_PROJECT_ID }}

Verify

  • sov sim run --resource public.notes shows no critical findings, and visitors are denied on every action.
  • In the console, Simulation → Issues lists nothing for notes.
  • The CI job fails if you temporarily apply the risky policy in a staging environment and run it there (--env <environment-id>).

Common errors

You seeWhyFix
PLAN_FEATURE_REQUIRED / PLAN_LIMIT_REACHEDSimulations aren't in your plan or this month's runs are used.Upgrade or wait for the monthly reset — see Plans, usage and limits.
Signed-in users denied tooRLS is enabled but not forced, or no access policy decides.Run alter table notes force row level security; — see Row-level security and access.
401 in CINo token, or it expired.Refresh the SOVARIA_ACCESS_TOKEN secret.

Production notes

  • Simulation never writes data, so it is safe against production — but run it on staging first when you are changing rules.
  • Every run, including single-request checks, counts towards the monthly simulation limit.
  • Keep the CI token's account to the smallest team role that can run simulations.

Next steps

Something unclear or missing on this page?Tell us