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
- Open Storage in the environment and pick the engine.
- Create the tables and the writer user, either way:
- CLI (the admin URL stays on your machine):
For MySQLnpx byotalk db migrate --url "$ADMIN_DATABASE_URL" --schema byotalk--schemais 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.
- CLI (the admin URL stays on your machine):
- If you used the CLI, paste the writer's runtime URL into slot A (optional read replica for history reads).
- Press Test connection. It reports TLS, server version, the user, schema version, permissions per table, round trip time and the egress IP to allowlist.
- 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
seqwithin 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.