How to Build a True "Burn-After-Reading" App with Supabase
Many privacy-focused messaging apps claim to offer "burn-after-reading" functionality. The reality is often much darker: they utilize soft-deletes. The data disappears from your UI, but it remains fully intact in the backend database, simply flagged as is_deleted = true.
If you are building a secure payload delivery system, soft deletes are a massive security vulnerability. In this tutorial, we will explore how to architect a true zero-retention database using PostgreSQL Row Level Security (RLS) via Supabase, controlled by Vercel Serverless Functions.
The Danger of Public APIs
A common mistake developers make when using Backend-as-a-Service (BaaS) platforms like Supabase or Firebase is leaving the database accessible via the public anonymous key. If an attacker discovers your table schema, they can query the database directly, bypassing your frontend application entirely.
To build a secure vault, we must completely lock down the database. No public reads. No public writes.
Step 1: PostgreSQL Row Level Security (RLS)
Supabase sits on top of a powerful PostgreSQL database. We can use RLS to ensure that the public internet cannot interact with our secrets table under any circumstances.
-- 1. Create the table for encrypted payloads
CREATE TABLE public.secrets (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
encrypted_payload TEXT NOT NULL,
iv TEXT NOT NULL,
salt TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 2. Enable Row Level Security
ALTER TABLE public.secrets ENABLE ROW LEVEL SECURITY;
-- 3. Intentionally omit creating any public policies.
-- Result: The table is 100% locked.
By enabling RLS but creating zero public policies, we have created a vault. The only way to bypass this lock is by using a secure Service Role Key, which should never be exposed to the frontend.
Step 2: The Serverless "Executioner"
Since the browser cannot talk to the database directly, we use a Vercel Serverless Function as a secure middleman. This function holds the Service Role Key in its secure environment variables.
When the frontend requests a secret, the serverless function executes a strict two-step process: Fetch, then immediately Hard Delete.
// api/getAndDestroy.js (Vercel Serverless Function)
import { createClient } from '@supabase/supabase-js';
export default async function handler(req, res) {
// Initialize Supabase bypassing RLS with the Service Key
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_ROLE_KEY
);
const { id } = req.body;
// 1. Fetch the payload
const { data, error } = await supabase
.from('secrets')
.select('*')
.eq('id', id)
.single();
if (error || !data) {
return res.status(404).json({ error: 'Vault destroyed or not found' });
}
// 2. HARD DELETE the row immediately
await supabase.from('secrets').delete().eq('id', id);
// 3. Return the ciphertext to the client for local decryption
return res.status(200).json(data);
}
Handling Edge Cases: What if they never open it?
A true burn-after-reading system also needs a garbage collector. If a user generates a secure link but never sends it, the encrypted payload will sit in your database forever, wasting storage space.
We can solve this natively inside PostgreSQL using the pg_cron extension to automatically wipe unread payloads after 7 days.
-- Enable cron scheduling
CREATE EXTENSION IF NOT EXISTS pg_cron;
-- Schedule a nightly purge of stale secrets
SELECT cron.schedule(
'cleanup-stale-secrets',
'0 0 * * *', -- Runs at midnight
$$ DELETE FROM public.secrets WHERE created_at < NOW() - INTERVAL '7 days'; $$
);
See this architecture in action
We used this exact Supabase + Vercel architecture to build ZeroKey, an open-source payload delivery system.
If you want to test the destruction protocol yourself, generate a payload, open the link, and then refresh the page. You will see a 404 error because the database row has been permanently purged.
Summary
Trusting a frontend client to simply "hide" a message is not security. True burn-after-reading requires strict backend enforcement. By leveraging Supabase RLS to block public access and Vercel Serverless functions to act as the executioner, you can guarantee absolute zero data retention for your users.