Deadman Vault uses Supabase as its PostgreSQL backend to store vault state, beneficiary records, heartbeat logs, and notification history. The schema is managed through four versioned migration files inDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/natureloved/DeadMan-Vault/llms.txt
Use this file to discover all available pages before exploring further.
supabase/migrations/ — each one builds on the last and must be applied in order. Row Level Security is enabled on every table with zero permissive policies, meaning nothing can be read or written through the browser-facing anon key; all database access flows through server-side API routes that use the privileged service role key.
Prerequisites
- A Supabase account and project (the free tier is sufficient for development and moderate production workloads)
- Supabase CLI installed locally
- Your project’s URL, anon key, and service role key — available in Project Settings → API
Migration Steps
Create a Supabase project
Go to supabase.com and create a new project. Choose a region close to your Vercel deployment for lowest latency. Once the project is provisioned, navigate to Project Settings → API and copy three values:
- Project URL →
NEXT_PUBLIC_SUPABASE_URL - Anon / public key →
NEXT_PUBLIC_SUPABASE_ANON_KEY - Service role key →
SUPABASE_SERVICE_ROLE_KEY
.env.local.Link your local project to Supabase
From the repository root, log in and link the CLI to your remote project:Your project ref is the subdomain of your Supabase URL — for
https://xyzabcde.supabase.co it is xyzabcde.Run the migrations
supabase db push, which applies all pending migration files in supabase/migrations/ in filename order against your remote database.You should see output confirming all four migrations were applied successfully.Verify the schema
Open the Supabase Dashboard → Table Editor and confirm the following tables exist:
vaults— core vault state, keeper escrow columns, status lifecyclebeneficiaries— per-vault recipients with allocation and vesting configheartbeats— immutable log of every owner check-innotifications— deduplication log for emails sent by the keeper
Migration Reference
001_initial_schema.sql — Core Tables
Creates the four foundational tables and their indexes:
vaults— one row per vault. Tracks the owner’s Stacks address, heartbeat interval (days), last and next deadline block heights, total deposited balance, lifecycle status, and optional metadata (name, message to beneficiaries, owner email).beneficiaries— one row per beneficiary per vault. Stores the recipient’s address, name, email, percentage allocation, optional vesting period (days), and claim state.heartbeats— append-only log of every successful check-in. Each row records the block height, transaction ID, and deposited amount for audit purposes.notifications— deduplication log used by the keeper to avoid re-sending the same email for the same vault event.
vaults table:
vaults(owner_address), vaults(status), beneficiaries(vault_id), beneficiaries(address), and heartbeats(vault_id).
002_keeper_escrow.sql — Custodial Keeper Columns
Adds four columns to vaults to support the testnet custodial keeper design:
| Column | Type | Purpose |
|---|---|---|
tx_id | TEXT | Transaction ID of the vault creation deposit on-chain |
keeper_address | TEXT | The dedicated Stacks address generated for this vault’s keeper |
keeper_key_ciphertext | TEXT | AES-256-GCM encrypted keeper private key (iv:authTag:ciphertext) |
gas_funded | BOOLEAN | Whether the keeper address has been funded with STX for gas |
status constraint to include the funding state — the initial state before the keeper address receives its STX gas drip and the owner makes their first deposit.
003_enable_rls.sql — Row Level Security Lockdown
Enables Row Level Security on all four tables with no permissive policies:
SUPABASE_SERVICE_ROLE_KEY, which bypasses RLS by design. This prevents any direct-database attack vector from a compromised browser session or a leaked anon key.
004_vesting_schedule.sql — Vesting Enforcement
Adds the columns needed to enforce per-beneficiary vesting cliffs precisely:
| Table | Column | Type | Purpose |
|---|---|---|---|
vaults | triggered_at | TIMESTAMPTZ | The wall-clock timestamp when the vault flatlined. Provides a stable reference point for vesting calculations independent of when the keeper cron next runs. |
beneficiaries | vesting_release_at | TIMESTAMPTZ | The earliest timestamp at which this beneficiary’s share may be released, calculated as triggered_at + vesting_days. |
RLS Design Rationale
Deadman Vault deliberately takes a “zero trust from the browser” stance on its database. Here is why:- The
NEXT_PUBLIC_SUPABASE_ANON_KEYis public — it ships in every user’s browser and in the built JavaScript bundle. - Enabling RLS with no policies means that even if someone extracts the anon key, they cannot query vault data, beneficiary addresses, or keeper ciphertexts.
- All reads and writes are mediated by Next.js API routes running on Vercel’s server-side runtime, where the
SUPABASE_SERVICE_ROLE_KEYis available as a secret environment variable. - This means there is no Supabase Realtime subscription or client-side query anywhere in the codebase — every data fetch goes through an API route.