What the developer collects: nothing
No personal data is collected, transmitted to, or held by the developer of Concord.
There is no exception to hedge and no "except as described below". Concord has no company
behind it, no backend of its own, and no account you could be a row in.
- No analytics, no crash reporting, no advertising, no attribution. Not disabled by
default — absent. The Android app's entire third-party dependency list is
apps/mobile/package.json:
Capacitor itself, four of its plugins (app lifecycle, haptics, keyboard, push) and a biometric
one. The interface's is
frontend/package.json:
an emoji set and two QR-code libraries. Nothing in either reports anything anywhere, and no
advertising or attribution identifier is read.
- The Firebase library ships but does not start. Push notifications are written and
unprovisioned: the push plugin brings Firebase Cloud Messaging with it, but there is no
Firebase project behind it and no configuration file in the build, so its initialisation is
switched off outright and nothing registers, reports or wakes.
- No accounts. Nothing to sign up for, so no email address, no phone number and no name
held anywhere but on your own device and on the devices of the people you talk to.
- No default server. Concord ships pointing at nothing. Any rendezvous node you use is
one you or a friend configured, and it is untrusted by construction. The project operates no
infrastructure that your copy of the app connects to automatically.
- No device or advertising identifiers are read or transmitted. Your identity is a
keypair you generated.
The rest of this page is about data that stays on your device, data that goes to the people you
are talking to, and a short, named, exhaustive list of connections that go somewhere else — each
with what it reveals and, where one exists, the switch that turns it off.
The model in one paragraph
There is no company, no account database, and no server that stores your messages. Your
identity is a keypair on your device; your history is an encrypted SQLite database on your
device; your messages travel directly between group members, encrypted end-to-end with
MLS (RFC 9420). Any helper infrastructure
Concord can use is untrusted by construction: it forwards ciphertext and is
cryptographically incapable of reading it.
What is end-to-end encrypted
- Messages — including edits, deletes, reactions, pins and offline history-sync batches
— are MLS-encrypted to the group. Only current members hold the keys. MLS provides forward
secrecy (past epochs' keys are deleted) and post-compromise security (removing a member rekeys
the group).
- Voice and video are a browser-to-browser WebRTC mesh; media is DTLS-SRTP encrypted
between participants.
- Transport hops are additionally Noise/DTLS-encrypted, so a network eavesdropper —
Wi-Fi, an ISP — sees only ciphertext either way.
Where your data lives
On your own devices, and nowhere else. Locally it is an encrypted SQLite database:
- Message bodies are sealed with NaCl secretbox under a key derived from your identity seed; a
stolen database file yields no readable messages.
- Your identity seed is itself encrypted at rest with Argon2id + secretbox, unlocked by your
passphrase.
- Full-text search runs entirely on your machine; queries never leave it.
- Export is one click to Markdown — your data is yours.
The untrusted rendezvous node
The one optional piece of infrastructure is a small rendezvous host: a DHT phone-book, a
circuit relay, an offline mailbox, and optionally a TURN relay for calls. It is deliberately
designed to learn as little as possible.
- It only ever sees ciphertext. It is never a group member and never holds message
keys.
- The offline mailbox is addressed by an opaque tag derived by salted hash from the
recipient's public key. Senders can compute it; the node cannot reverse it to an identity.
Deposits are end-to-end-encrypted blobs, drained and deleted when the recipient
reconnects.
- Optional push wakes are contentless — "you have mail", never message content. Tokens
are keyed by the opaque mailbox tag, not by identity.
- Its two plain-HTTPS doors are the exception to "ciphertext only". A browser guest
joining a meeting, and a visitor booking a slot on a booking link, have no Concord install and
no keys, so their leg terminates at the node before continuing to the host. It therefore sees
a guest's own messages, and a booking visitor's IP plus whatever they submit. It stores none
of it, and everything else passing through stays ciphertext.
- You can self-host the whole thing.
Every outbound call that is not to your peers
Nearly every connection the app makes is to your peers or to whatever rendezvous node you
configure — but nearly is not only. Naming the exceptions is the point of this
section, and this list is exhaustive.
Two that can happen without you asking
- api.github.com, once at launch, on desktop and web only. The app asks the project's
own repository for its latest release tag so it can tell you an update exists. It is
unauthenticated and sends no identifier beyond what any HTTP request carries — your IP and a
concord-updater user agent — and any error is a silent no-op. Builds without a
release version stamp skip it entirely.
The mobile app never makes this request automatically. A sideloaded Android
build offers a manual "Check for updates" button, because someone who downloaded an APK from a
GitHub release has no other route to a fix; nothing happens until they press it. A build
installed from Google Play does not offer it at all — the store owns updates there, and the app
asks the running install where it came from to decide.
- stun.l.google.com, at call time only. Starting a call needs to learn your own external
address. If the rendezvous you bootstrap through serves ICE configuration, that is used and
Google is never contacted; only when it does not is there a fallback to this hardcoded public
STUN server. It learns your IP and that you are starting a call — no media, no identity, no
group, and nothing at all if you never place a call.
Two searches that are yours to switch on or off
These are the only two features where something you type is sent to a search service. Both are
switches in Settings → Privacy & safety, and both are enforced in the backend rather
than in the interface: switched off, the request is not made, not merely not displayed.
- Game title search → Valve, off by default. The collection editor can suggest real game
titles from Steam's public storefront search, and it asks again as you type — so a half-typed
title, your IP and the fact that you were online reach Valve. Switched off, the editor still
works: type a title and it is added as you wrote it, and nothing leaves the device. An install
that already had a game collection when this switch was introduced keeps it on, because it had
been using the feature; every other install starts closed.
- GIF search → your own rendezvous, on by default. The GIF picker's search tab sends
what you type to the rendezvous node you configured. That node asks a public GIF
service on your behalf — Giphy in the node this project ships, though the operator chooses —
and sends the pictures back through itself, so the service sees the node's IP and never yours,
and your browser never connects to it. What it costs is that the operator of your
rendezvous can see your search terms. It is on by default because it reaches exactly one
machine you had already chosen to route your traffic through; off is one switch away if that
is not a trade you want with whoever runs your node. A guild's own GIF pack is unaffected
either way — it never leaves your machine.
Three more that load a picture, all off by default
Game box art, link previews and YouTube embeds have the same shape: rendering someone else's
message would otherwise make your browser fetch something from a third party, telling them your
IP and the moment you were online with no click at all. So all three are off by default, with a
switch each.
How long anything is kept
There is no retention schedule to publish, because there is nobody holding a copy to schedule.
Your history is kept on your device until you delete it, and on the devices of the people you
sent it to until they delete it. Three things shorten that on purpose:
- Disappearing messages. An author can put a timer on a message. The timer travels
inside the encrypted content, so every device erases the body at the same wall-clock instant
without coordinating.
- A guild retention policy. A guild can set how long messages are kept, per guild or per
channel, and each member's app prunes its own copy. This is local enforcement and nothing
else: two members with different uptimes forget at slightly different moments, and a member
running a modified client need not forget at all. The interface says so beside the switch.
- The offline mailbox. A deposit waiting at a rendezvous is an encrypted blob addressed
to an opaque tag; it is drained and deleted when you next connect, and nodes expire undrained
deposits on their own schedule.
Deleting your data
In the app: Settings → Privacy & safety → Delete everything on this device. It asks
you to type your profile name, and then erases your identity keystore, the encrypted database
holding every message stored here, the MLS group state and the plaintext peer cache. The app is
left exactly as it was before you first opened it. Nothing is retained, anywhere, for any
period, because there is nowhere for it to be retained.
Smaller erasures, for when the whole device is not what you meant: delete one message and the
deletion travels to the group like any other message; Privacy & safety → Empty trash
scrubs the retained text behind every deleted message on this device; Settings → Devices →
Unlink tells one linked device to erase itself.
What deletion cannot do, and no feature of any peer-to-peer messenger can, is reach the
copies other people already have. A message you sent is on the recipient's device, sealed to
them. There is no server holding the authoritative copy that could be told to forget — which is
the same fact that makes the rest of this page true.
Because there is no account, there is also no "delete my account" request to send anyone, and no
data-subject request the developer could answer if you made one: we hold nothing about you to
disclose, correct or erase.
Children
Concord is a general-audience communication app. It has no age gate, because it has no accounts
and collects no personal information from anyone, of any age, to gate. It is not directed to
children, and it is not designed, marketed or featured for them.
It does carry user-generated content — anything a member of a guild you join chooses to send —
so it is rated accordingly, and it provides blocking, a report flow, and message requests that
hold a stranger's first direct message unopened. If you are responsible for a young person's
device, treat Concord as you would any other open communication tool.
What is not protected
Stating this is part of the policy, not a footnote to it.
- Metadata is not hidden. Inherent to peer-to-peer: the DHT and relay can observe that
your peer ID connects and roughly when, never content, and peers you connect to directly learn
your IP address. Calls can opt into relay-only mode to hide IPs from other participants.
- Concord is not an anonymity tool. It authenticates identities; it does not hide them.
If you need anonymity, use a network layer built for it.
- Group members are trusted. Encryption protects the group from outsiders; it cannot
stop a member you invited from copying or screenshotting what you sent them.
- Your passphrase is the at-rest anchor. Configure convenience auto-unlock and at-rest
encryption no longer protects your history from someone who compromises that machine. The
default is to ask for the passphrase.
- Not everything on your disk is encrypted. The remembered-peer list and each group's
MLS state, including its leaf private keys, sit beside the database in the clear. Your message
bodies stay sealed; those do not.
- Verification matters. First-contact impersonation is defeated by comparing key
fingerprints — "safety numbers" — out of band. Actually do it.
Changes to this policy
The policy lives in the repository next to the code it describes, so its history is the change
log: every revision is a commit with a message saying what changed and why. The date at the top
is the last substantive revision.
A change that alters what leaves your device — a new outbound connection, a default flipped
from off to on — lands in the same release as the change itself and is named in that release's
notes, not slipped in quietly.
There is no support address, because there is no company to staff one. Both routes are the
repository:
- Anything about this policy, or the app in general:
github.com/ZahakJ/concord — open an issue.
- A security or privacy vulnerability: use GitHub's private vulnerability reporting —
the Security tab on the repository, then Report a vulnerability — which opens
a private thread.
SECURITY.md has the
scope, the response times, and what is already a documented known gap. Please do not post a
vulnerability as a public issue.
The implementation is the authoritative source and is meant to be read as one. The full policy,
with a link to the file behind each claim, is
PRIVACY.md.