Bring your own database

With your own database enabled, your database is the permanent record of every message. ByoTalk replicates users, conversations, members and messages into tables you own, using a least-privilege user that can read, insert and update but never delete. We keep an encrypted buffer of recent messages (7 days by default, 24 hours to 30 days) to deliver, sync and retry; it is never purged before replication.

Engine Versions Where the data goes
PostgreSQL 14+ Tables in a schema, e.g. byotalk.messages
MySQL / MariaDB MySQL 8.0+, MariaDB 10.6+ Tables with a prefix, e.g. byotalk_messages
MongoDB 6.0+ (Atlas or self-hosted) Collections with a prefix, e.g. byotalk_messages
HTTP endpoint any Signed batches POSTed to an HTTPS endpoint you run, in front of any database. See HTTP endpoint (any database)

Setup

  1. Open Storage in the environment and pick the engine.
  2. Create the tables and the writer user, either way:
    • CLI (the admin URL stays on your machine):
      npx byotalk db migrate --url "$ADMIN_DATABASE_URL" --schema byotalk
      
      For MySQL --schema is the table prefix; for MongoDB it is the collection prefix.
    • Paste an admin URL once in the dashboard. We run the migration and discard the URL (never stored, never logged). The writer's URL is saved to slot A for you.
  3. If you used the CLI, paste the writer's runtime URL into slot A (optional read replica for history reads).
  4. Press Test connection. It reports TLS, server version, the user, schema version, permissions per table, round trip time and the egress IP to allowlist.
  5. Press Enable your own database and choose the buffer retention.

Runtime URL formats

Engine Runtime URL
PostgreSQL postgres://user:pass@host:5432/db?sslmode=verify-full
MySQL / MariaDB mysql://user:pass@host:3306/db?ssl-mode=VERIFY_IDENTITY
MongoDB mongodb+srv://user:pass@cluster/db
HTTP endpoint https://your-api.example.com/byotalk/sink

Always use TLS in production. The test warns when the connection is not encrypted.

Least-privilege users

The migration creates a writer with exactly these permissions; create it yourself with the same rights if you prefer.

PostgreSQL:

CREATE ROLE byotalk_writer LOGIN PASSWORD '...';
GRANT USAGE ON SCHEMA byotalk TO byotalk_writer;
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA byotalk TO byotalk_writer;
-- no DELETE, no TRUNCATE, no DDL, nothing outside the schema

MySQL / MariaDB:

CREATE USER 'byotalk_writer'@'%' IDENTIFIED BY '...';
GRANT SELECT, INSERT, UPDATE ON db.byotalk_users TO 'byotalk_writer'@'%';
GRANT SELECT, INSERT, UPDATE ON db.byotalk_conversations TO 'byotalk_writer'@'%';
GRANT SELECT, INSERT, UPDATE ON db.byotalk_conversation_members TO 'byotalk_writer'@'%';
GRANT SELECT, INSERT, UPDATE ON db.byotalk_messages TO 'byotalk_writer'@'%';

MongoDB: a role with find, insert and update on the byotalk_* collections of your database, and a user with that role. Where the admin user cannot create users (for example MongoDB Atlas, where users are managed in the Atlas UI), the migration still creates the collections and indexes, then tells you to create the user yourself (Database Access) and paste its connection string.

Hard deletes requested through our API (GDPR erasure, hard=true) are written as tombstones with content nulled (an update), so delete permission is never needed. The test warns if the user can delete.

What lives where

Data Your database Our infrastructure Retention on our side
Message content, metadata, attachment refs Permanent system of record Encrypted buffer Until replicated and older than the window (default 7 d; 24 h–30 d). Never purged while un-replicated
Edits, deletes Version-guarded upserts, tombstones Buffer Same
Conversations, membership Mirror Authoritative Lifetime
Users Mirror (id, name, image URL, metadata) User ID required; name/avatar optional Lifetime
Read watermarks Mirror (last_read_seq) Authoritative Lifetime of membership
Attachment files Your own media storage when connected Object key and metadata only n/a
Presence, typing No Memory Seconds
Webhook payloads, logs No Yes 72 h / 14 d

Sink states

The Storage page shows the replicator (sink) state, lag and last error. healthy means writes are current; degraded means lag above 5 minutes; failing means no successful write for over an hour. Chat keeps working while your database is down: data stays in our buffer and is retried. Subscribe to the sink.degraded, sink.failing and sink.recovered webhooks to be alerted.

FAQ

  • Can I query the tables? Yes, that's the point. Order by seq within a conversation.
  • Can I write to them? Don't. Use our API so realtime clients see the change. Direct writes will be overwritten by later versions.
  • What if I restore a backup? Press Resync to rewrite everything still in our buffer.
  • How much load? A few small transactions per second at typical volumes.
  • Which providers? Any PostgreSQL 14+, MySQL 8+/MariaDB 10.6+ or MongoDB 6+ reachable with TLS: RDS, Aurora, Cloud SQL, Azure, Neon, Supabase, PlanetScale, Atlas, self-managed. For anything else (SQL Server, Oracle, DynamoDB, Firestore…) use an HTTP endpoint.