Leadvesta

Trust

How credentials and backups are kept

Customer database credentials

Customer database credentials are not stored as plain text. src/lib/actions.ts saveAppAction encrypts the Postgres URL, the Supabase service key, and the optional Stripe key with encryptSecret from src/lib/safety/secrets.ts before insert. That function uses AES-256-GCM (createCipheriv with aes-256-gcm), version v1, a 12-byte IV, and an auth tag. The key is the environment variable CREDENTIALS_ENCRYPTION_KEY. It must be base64 that decodes to 32 bytes, and it is not written into the database. The ciphertext is stored on watched_app as database_url_cipher, service_key_cipher, and stripe_key_cipher (src/lib/db/schema.ts). Error logs call scrub() in src/lib/safety/secrets.ts, which replaces a postgres URL, a JWT, and a key before the line is printed. src/lib/safety/dump.ts passes the database password as PGPASSWORD to the child process, not on the command line, and scrubs stderr. Removing an app runs deleteAppAction in src/lib/actions.ts, which deletes the backup files, the backup rows, and then the app row, so the ciphertext is deleted. Deleting the account runs deleteAccount in src/lib/account.ts, which deletes every backup file for that account, the app rows, and the user row. Canceling a subscription does not delete the credentials. They remain until the app is removed or the account is deleted.

The ciphertext is stored in Leadvesta's own Postgres database on Neon. Neon encrypts that database at rest. The dump file is a separate private object, not a row in that database.

Read-only database role

Use a read-only Postgres role for the connection string. Leadvesta needs SELECT for the dump and the security checks. Create the role in the Supabase SQL editor, replace the password, and paste that role's connection string into settings.

create role leadvesta_read with login password 'choose-a-long-password';
grant connect on database postgres to leadvesta_read;
grant usage on schema public to leadvesta_read;
grant select on all tables in schema public to leadvesta_read;
alter default privileges in schema public grant select on tables to leadvesta_read;

Where backups live

Each dump is one private object. If S3_BUCKET, S3_ACCESS_KEY_ID, and S3_SECRET_ACCESS_KEY are set, the object is stored in that S3 bucket. Otherwise, if BLOB_READ_WRITE_TOKEN is set, it is stored in Vercel Blob with access set to private. Otherwise it is a file under data/backups on the server disk, which is only for local development. Nothing in this code marks a dump public.

How long backups are kept

Starter keeps each dump for 30 days. Pro and the free trial keep each dump for 90 days. That expiry is written when the dump is created and stays put if you change plans later.

Canceling does not delete dumps immediately. New backups and monitoring stop once the subscription status is canceled, or once the trial has ended without a subscription. A dump that already exists stays until the expiry saved on that backup. The daily backup job then deletes the file and the backup row. Removing an app deletes its stored dumps right away.

Contact

Questions about this go to hello@leadvesta.com.