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:
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:
Generate encryption key
.env.local
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
Database Connection Strings
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:
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
PostgreSQL
MySQL
SQL Server
-- 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;
-- Create read-only user
CREATE USER ' quail_readonly '@ '%' IDENTIFIED BY 'your_secure_password' ;
-- Grant select permission
GRANT SELECT ON your_database. * TO 'quail_readonly' @ '%' ;
-- Apply changes
FLUSH PRIVILEGES;
-- Create read-only user
CREATE LOGIN quail_readonly WITH PASSWORD = 'your_secure_password' ;
USE your_database;
CREATE USER quail_readonly FOR LOGIN quail_readonly;
-- Add to db_datareader role
ALTER ROLE db_datareader ADD MEMBER quail_readonly;
Authentication & Authorization
Supabase Authentication
Quail uses Supabase for user authentication:
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:
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:
PostgreSQL
MySQL
SQL Server
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
const connectionString =
'mysql://user:pass@host:3306/db?ssl={"rejectUnauthorized":true}' ;
const connectionString =
'Server=host;Database=db;User=user;Password=pass;Encrypt=true;TrustServerCertificate=false' ;
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:
Generate Strong Encryption Key
Use openssl rand -base64 32 to generate a secure encryption key
Configure Read-Only Users
Create database users with SELECT-only permissions
Enable SSL/TLS
Configure SSL connections for all database connections
Whitelist IPs
Configure IP whitelisting on database servers
Secure Environment Variables
Use secure secret management (Vercel Secrets, AWS Secrets Manager, etc.)
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:
Do not open a public GitHub issue
Review the code at github.com/O1af/quail
Implement fixes in your own fork
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