Skip to content

Repository files navigation

RLSee

A self-hostable dashboard for inspecting and understanding PostgreSQL Row-Level Security (RLS) policies. Connect to any PostgreSQL database, see which tables have RLS enabled, view policy definitions, and optionally get AI-powered explanations of what each policy does.

Features

  • RLS policy viewer -- Browse all tables in a database, see which have RLS enabled/forced, and expand to view individual policy definitions (USING and WITH CHECK expressions).
  • Policy & function comments -- Displays PostgreSQL COMMENT ON POLICY and COMMENT ON FUNCTION annotations prominently alongside each definition.
  • Function resolution -- Automatically discovers functions referenced by RLS policies via pg_depend, shows their full definitions, and flags security concerns (e.g., SECURITY DEFINER without a pinned search_path).
  • AI explanations -- Optionally configure an Anthropic or OpenAI API key to get plain-language explanations of policies and their referenced functions. Rendered as formatted markdown.
  • Connection management -- Store multiple PostgreSQL connections with encrypted credentials (Fernet). Test connections before saving.
  • Schema browser -- Switch between schemas to view tables and policies across your database.
  • TLS by default -- Self-signed certificates are generated automatically. Bring your own certs or disable TLS for plain HTTP.
  • Single-container deployment -- Ships as one Docker image running Caddy, the API, and the frontend.

Quick Start

One-line install

curl -sSL https://raw.githubusercontent.com/jasondcamp/rlsee/main/bin/install.sh | bash

This installs RLSee to /opt/rlsee, generates secrets, pulls the Docker image, and starts the stack with self-signed TLS.

Manual setup

# Clone the repo
git clone https://github.com/jasondcamp/rlsee.git
cd rlsee

# Generate secrets
make keys
# Copy the output into a .env file

# Start with Docker Compose
cp .env.example .env
# Edit .env with your generated keys
docker compose up --build

The app will be available at https://localhost:8765. On first visit you'll create an admin account, then add a PostgreSQL connection.

Development

# Start both backend and frontend with hot-reload
make dev

This runs docker-compose.dev.yml which starts:

  • Backend (Flask) on port 8766 with auto-reload
  • Frontend (Next.js) on port 8765 with hot module replacement

Running without Docker

Backend:

cd backend
pip install -r requirements.txt
export SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
export FERNET_KEY=$(python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
python run.py

Frontend:

cd frontend
npm install
NEXT_PUBLIC_API_URL=http://localhost:8766 npm run dev

Configuration

All configuration is via environment variables. See .env.example for the full list.

Variable Required Description
SECRET_KEY Yes Flask session secret. Generate with python3 -c "import secrets; print(secrets.token_hex(32))"
FERNET_KEY Yes Encryption key for stored database credentials. Generate with python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
DATABASE_URL No SQLite connection string for app data. Default: sqlite:///rlsee.db
TLS_CERT No TLS mode. internal (default) = self-signed, off = plain HTTP, or path to cert file
TLS_KEY No Path to TLS key file (when using custom certs)
TOKENTALLY_API_KEY No TokenTally API key for tracking LLM usage

LLM provider settings (Anthropic/OpenAI API keys and model selection) are configured through the Settings page in the UI.

Architecture

├── backend/             Flask API server
│   ├── app/
│   │   ├── models/      SQLAlchemy models (User, Connection, Setting)
│   │   ├── routes/      API endpoints (auth, connections, database, settings)
│   │   └── services/    Business logic (auth, crypto, database, LLM)
│   └── run.py           Entry point
├── frontend/            Next.js 15 app
│   └── src/
│       ├── app/         Pages (login, dashboard, connections, database, settings)
│       ├── components/  UI components
│       ├── stores/      Zustand state management
│       └── lib/         API client
├── bin/install.sh       One-line installer
├── Dockerfile           Multi-stage production build
├── start.sh             Container entrypoint (Caddy + Gunicorn + Node)
├── docker-compose.yml   Production deployment
└── docker-compose.dev.yml  Development with hot-reload

Container layout

A single container runs three processes:

  • Caddy (foreground) -- Reverse proxy with TLS termination on ports 8765 (frontend) and 8766 (API).
  • Gunicorn -- Flask API server on 127.0.0.1:8000.
  • Node.js -- Next.js standalone server on 127.0.0.1:3000.

The browser auto-detects the API URL from the current hostname, so no build-time configuration is needed.

How RLS introspection works

RLSee queries PostgreSQL system catalogs directly:

  • pg_policy + pg_get_expr() -- Extracts policy definitions (USING/WITH CHECK expressions) as human-readable SQL.
  • obj_description() -- Retrieves COMMENT ON POLICY and COMMENT ON FUNCTION annotations.
  • pg_depend -- Resolves which functions are referenced by each policy, without relying on fragile text parsing of policy expressions.
  • pg_proc + pg_get_functiondef() -- Retrieves full function source code for display and inclusion in LLM prompts.
  • pg_class / pg_namespace -- Enumerates tables and schemas with RLS status.

Security

  • All traffic is encrypted with TLS by default (self-signed certificates generated on first start).
  • Database credentials are encrypted at rest using Fernet symmetric encryption.
  • Passwords are hashed with PBKDF2-SHA256 (via Werkzeug).
  • The app connects to your PostgreSQL databases as a read-only observer -- it never modifies your data or schema.
  • Functions flagged as SECURITY DEFINER without a pinned search_path are highlighted as a potential security risk.

License

AGPL-3.0 -- see LICENSE.

About

RLSee - Postgres RLS viewer and explanation engine

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages