Skip to main content

Overview

Quail BI is designed with security as a core principle. This page explains the security architecture, encryption mechanisms, and best practices for protecting your data.
Quail enforces read-only database queries, encrypts all credentials, and stores connection strings securely using AES-256-GCM encryption.

Core Security Principles

Read-Only Access

All database queries are executed with read-only permissions

Encrypted Storage

Database credentials are encrypted before storage

Zero Data Retention

Query results are not stored server-side

Encryption Architecture

AES-256-GCM Encryption

Quail uses AES-256-GCM (Advanced Encryption Standard with Galois/Counter Mode) to encrypt sensitive data:
lib/utils/encryption.ts
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';

const ALGORITHM = 'aes-256-gcm';
const IV_LENGTH = 16;
const AUTH_TAG_LENGTH = 16;
const KEY_LENGTH = 32;

export function encrypt(text: string, encryptionKey: string): string {
  const iv = randomBytes(IV_LENGTH);
  const key = Buffer.from(encryptionKey, 'base64').slice(0, KEY_LENGTH);
  
  const cipher = createCipheriv(ALGORITHM, key, iv);
  let encrypted = cipher.update(text, 'utf8', 'hex');
  encrypted += cipher.final('hex');
  
  const authTag = cipher.getAuthTag();
  return `${iv.toString('hex')}:${encrypted}:${authTag.toString('hex')}`;
}

export function decrypt(encryptedData: string, encryptionKey: string): string {
  const [ivHex, encrypted, authTagHex] = encryptedData.split(':');
  const iv = Buffer.from(ivHex, 'hex');
  const authTag = Buffer.from(authTagHex, 'hex');
  const key = Buffer.from(encryptionKey, 'base64').slice(0, KEY_LENGTH);
  
  const decipher = createDecipheriv(ALGORITHM, key, iv);
  decipher.setAuthTag(authTag);
  
  let decrypted = decipher.update(encrypted, 'hex', 'utf8');
  decrypted += decipher.final('utf8');
  
  return decrypted;
}
AES-256-GCM provides both encryption and authentication, protecting against tampering.

Encryption Key Management

The encryption key is configured via the NEXT_PUBLIC_ENCRYPTION_KEY environment variable:
openssl rand -base64 32
Critical: The encryption key must be exactly 32 characters (256 bits) when decoded from base64. Never commit this key to version control.

What Gets Encrypted

All database connection strings are encrypted before being stored in MongoDB. This includes:
  • PostgreSQL connection strings
  • MySQL connection strings
  • SQL Server connection strings
  • Any custom database URLs
The encryption happens in the browser before sending to the server:
const encryptedConnectionString = encrypt(
  connectionString,
  process.env.NEXT_PUBLIC_ENCRYPTION_KEY!
);
Database usernames and passwords embedded in connection strings are encrypted as part of the full connection string.
Optional third-party API keys (Stripe, Azure AI) are stored as environment variables and never exposed to the client.

Read-Only Database Access

Enforcement Mechanism

Quail enforces read-only access at the application level:
lib/actions/query.ts
export async function executeQuery(connectionId: string, sql: string) {
  // Validate query is read-only
  const isReadOnly = validateReadOnlyQuery(sql);
  if (!isReadOnly) {
    throw new Error('Only SELECT queries are allowed');
  }
  
  // Execute with read-only connection
  const connection = await getConnection(connectionId);
  const results = await connection.query(sql);
  
  return results;
}

function validateReadOnlyQuery(sql: string): boolean {
  const normalized = sql.trim().toUpperCase();
  const writeOperations = ['INSERT', 'UPDATE', 'DELETE', 'DROP', 'CREATE', 'ALTER', 'TRUNCATE'];
  
  return !writeOperations.some(op => normalized.startsWith(op));
}
For maximum security, we recommend creating database users with read-only permissions. See Security Best Practices.

Read-Only User Creation

-- Create read-only user
CREATE USER quail_readonly WITH PASSWORD 'your_secure_password';

-- Grant connect permission
GRANT CONNECT ON DATABASE your_database TO quail_readonly;

-- Grant usage on schema
GRANT USAGE ON SCHEMA public TO quail_readonly;

-- Grant select on all tables
GRANT SELECT ON ALL TABLES IN SCHEMA public TO quail_readonly;

-- Grant select on future tables
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO quail_readonly;

Authentication & Authorization

Supabase Authentication

Quail uses Supabase for user authentication:
lib/auth/supabase.ts
import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

export async function signIn(email: string, password: string) {
  const { data, error } = await supabase.auth.signInWithPassword({
    email,
    password,
  });
  
  if (error) throw error;
  return data;
}
Supabase provides built-in protection against common authentication vulnerabilities like brute force attacks and credential stuffing.

Session Management

Sessions are managed via HTTP-only cookies:
middleware.ts
import { createServerClient } from '@supabase/ssr';
import { NextResponse, type NextRequest } from 'next/server';

export async function middleware(request: NextRequest) {
  let response = NextResponse.next();
  
  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return request.cookies.get(name)?.value;
        },
        set(name: string, value: string, options: any) {
          response.cookies.set({ name, value, ...options });
        },
        remove(name: string, options: any) {
          response.cookies.set({ name, value: '', ...options });
        },
      },
    }
  );
  
  await supabase.auth.getSession();
  return response;
}

Network Security

SSL/TLS Connections

Quail supports SSL/TLS connections to databases:
const connectionString = 
  'postgresql://user:pass@host:5432/db?sslmode=require';
SSL modes:
  • disable: No SSL
  • require: Require SSL (recommended)
  • verify-ca: Verify certificate authority
  • verify-full: Verify CA and hostname

IP Whitelisting

For production deployments, configure IP whitelisting on your database server. See Database Connections for provider-specific instructions.

Data Privacy

What Quail Does NOT Store

Query Results

Results are returned directly to the user’s browser and never stored server-side

Raw Connection Strings

Only encrypted versions are stored in the database

Database Contents

Quail never stores or caches table data

User Queries

SQL queries are not logged or retained after execution

What Quail DOES Store

User Accounts

Email, hashed password (via Supabase), and profile information

Encrypted Credentials

AES-256 encrypted database connection strings

Dashboard Configurations

Dashboard layouts, chart configurations, and filter settings

Chat History

Conversation history for the chat interface (without query results)

Security Checklist

Before deploying Quail to production:
1

Generate Strong Encryption Key

Use openssl rand -base64 32 to generate a secure encryption key
2

Configure Read-Only Users

Create database users with SELECT-only permissions
3

Enable SSL/TLS

Configure SSL connections for all database connections
4

Whitelist IPs

Configure IP whitelisting on database servers
5

Secure Environment Variables

Use secure secret management (Vercel Secrets, AWS Secrets Manager, etc.)
6

Enable Supabase RLS

Configure Row Level Security policies in Supabase

Deployment Security Guide

Complete guide to securing your Quail deployment

Reporting Security Issues

Note: Quail is a discontinued project. For self-hosted deployments, you are responsible for security updates and patches.
If you discover a security vulnerability in the Quail codebase:
  1. Do not open a public GitHub issue
  2. Review the code at github.com/O1af/quail
  3. Implement fixes in your own fork
  4. Consider contributing patches via pull request

Environment Variables

Configure encryption keys and security settings

Security Best Practices

Production security checklist and guidelines

Database Setup

Configure secure database connections

Troubleshooting

Resolve encryption and authentication issues

Build docs developers (and LLMs) love