NeyLivo Русский

Security

NeyLivo Security

This page describes what actually protects your data in NeyLivo, and what does not. Where a protection is optional, off by default, or unproven, that is written here rather than left out.

Last updated: August 12, 2026 · NeyLivo 1.563.0

Security philosophy

Three rules the project holds itself to.

Message protection

Direct messages between two people can be encrypted end to end. When it is on, the server stores ciphertext it has no key for.

Key agreement
ECDH on the P-256 curve. X25519 would be preferable, but it is not available in the WebCrypto implementation the app must run on.
Key derivation
HKDF-SHA256, with the purpose and the pair of devices bound into the derivation, so a key from one context is useless in another.
Encryption
AES-256-GCM. GCM also authenticates: ciphertext altered on the server fails to decrypt rather than decrypting into something else.
Keys belong to devices
Each device has its own key pair. The private part is stored in IndexedDB as a CryptoKey and is never sent anywhere in the clear.
Length hiding
Message text is padded into fixed-size blocks before encryption, so ciphertext size no longer reveals whether you wrote “yes” or a page of text.
Verification
Each key has a fingerprint — five groups of five digits — that two people can read out to each other to confirm nobody is in the middle.
Multiple recipients
A per-message content key is wrapped separately for the other person’s devices and for your own, so your own second device can read your history.

This is a setting, and it is off until you turn it on (Settings → Privacy). It applies to one-to-one direct messages only.

Not end-to-end encrypted: group conversations and server channels. Those messages are stored so that the server can read them, and are protected by database access rules — the same model as most group chat products. If you need a conversation the server cannot read, use a one-to-one direct message with encryption on.

If your correspondent has no published key — for example they have never opened a version that supports encryption — the message is not sent, and the app says so. It never falls back to plaintext behind your back.

Transport security

All traffic between the app and its backend goes over HTTPS/TLS, including the realtime connection (secure WebSocket) and file uploads. Plugins may only reach the network over https: and wss:; plain HTTP is refused.

The Windows app is downloaded over HTTPS and updates itself over HTTPS, checking the SHA-512 checksum published with the release. That checksum proves the file arrived intact — it is not a code-signing signature, and the installer is not code-signed.

Account security

Accounts are handled by Supabase Auth. Sessions are JWT-based with refresh tokens, stored locally on your device (and additionally in native storage on Android so that an app update does not log you out).

There is no two-factor authentication with an authenticator app yet. Trusted devices and the recovery code are a partial substitute, not a replacement.

Password storage

NeyLivo does not store passwords. They are handled by Supabase Auth, which stores them as bcrypt hashes; the project writes no password storage code of its own.

The minimum password length is six characters, which is the platform default. That is short, and raising it is on the list of things to fix.

One consequence worth understanding. Your encryption key can be backed up to the server, locked with your account password (PBKDF2-SHA256, 250 000 iterations, AES-256-GCM). This exists so that logging out does not destroy your message history. The cost: the same password is also what you type into the login form, so a malicious or compromised authentication server could capture it and unlock that backup. NeyLivo protects your messages from outsiders and from a database leak; it does not protect them from whoever controls the server itself.

Attachments

Attachments are the weakest part of NeyLivo today, and it is being fixed. Files you send are uploaded to cloud storage that is configured as public: anyone who has, or can construct, the link to a file can download it without logging in. Access rules protect the database, not this storage bucket.

There is a switch that fixes this for direct messages: encrypt attachments. With it on, each file is encrypted in your browser with its own AES-256-GCM key before upload, stored under a neutral name with no file type, and the key travels inside the encrypted message — so it can only be obtained by decrypting the conversation. Like the other encryption switches, it is off by default.

Until the storage layer is closed, treat anything you send without attachment encryption as a file that is technically public. This limitation is listed below and in the project’s own audit.

Voice and video

Calls run over LiveKit. Permission to call someone is decided on the server, not in the app: blocking and your “who can call me” setting are enforced by the service that issues the call token, so the check cannot be bypassed by a modified client.

Calls between two people can be end-to-end encrypted, as a separate setting. The key is generated in your browser and delivered to the other person inside an encrypted message, so the call server never sees it.

Marked experimental. The encryption path is covered by tests in code, but it has never been verified in a real call between two devices. Until it has, it is not presented as proven. If the key cannot be delivered, the call proceeds unencrypted and says so on screen.

Without call encryption, media is decrypted at the media server, as in any ordinary group call. Server voice channels are not end-to-end encrypted.

Plugin isolation

Plugin code does not run in the app. It runs in a Web Worker, which the browser gives no DOM, no cookies and no access to the page — so a plugin cannot read your session, cannot draw a fake login window, and cannot reach into the interface.

Plugins that provide a full page (for games, visualisers, 3D scenes) get an <iframe sandbox="allow-scripts"> without allow-same-origin. That gives the page a unique opaque origin: it has its own DOM and can use WebGL, WebAssembly and audio, but it is a different origin from NeyLivo and can touch neither the app’s document nor its storage.

These are browser boundaries, not checks in our code that a clever plugin could talk its way around. Everything a plugin can do arrives through one dispatcher, and every call is checked against the permissions the plugin declared. There is a dedicated test suite that attacks these boundaries deliberately.

Plugin permissions

Before installing anything, you see the plugin’s name, author, version and the exact list of what it is asking for, in plain language — for example “Send messages on your behalf” or “Reach the internet (only the listed sites)”. There are 23 such permissions.

Two things to know. A plugin that declares * — or that declares nothing at all — asks for every permission, and the install screen shows this in red. And there are no quantitative limits: an installed plugin may send messages, make requests and run in the background as much as it likes. The app states this before you install, because the meaningful protection here is your decision, not a quota.

A plugin can be switched off or removed at any time.

Network permissions

A plugin can only reach the internet if it has the network permission, and only the domains it declared in its header. The rules are enforced in one place for every way out — ordinary requests, streaming responses and persistent connections — so a second code path cannot drift out of sync with the first:

Metadata

Encryption hides what you wrote. It does not hide that you wrote. Even with everything switched on, the server knows:

Message length is hidden by padding, and encrypted attachments carry no file name or type. The rest of the list above is metadata that NeyLivo does not currently minimise.

Infrastructure

NeyLivo is not self-hosted infrastructure. It runs on:

The privacy page lists every third-party service the app talks to, including the ones that only see an IP address.

Threat model

What NeyLivo protects against

  • Someone else reading your one-to-one messages, with encryption on — including the operator of the database
  • Another user reading data that is not theirs: access rules are enforced in the database, not in the app
  • A malicious plugin stealing your session, faking the interface or calling home to an undeclared server
  • Passive collection: there is no analytics, no crash reporting and no advertising code to leak anything
  • Someone with your database dump reading your recovery code, or your key backup without your password

What NeyLivo does not protect against

  • A malicious operator of the authentication server, who could capture your password as you log in and unlock your key backup
  • Anyone reading group conversations or server channels at the database — those are not end-to-end encrypted
  • Anyone downloading unencrypted attachments, which are in public storage today
  • A compromised device: malware or someone with your unlocked computer sees what you see
  • Traffic analysis: who talks to whom and when is visible to the server
  • A photograph of your screen taken with another camera, whatever the capture protection setting says

Known limitations

The list the project keeps of its own weak spots, in the order it intends to fix them:

  1. Attachments are in public storage. Files sent without attachment encryption can be downloaded by anyone with the link. The fix — closing the bucket and moving to signed links without breaking the millions of links already inside old messages — is planned in stages.
  2. Encryption is off by default. Privacy that only applies when found in the settings is privacy most people never get.
  3. Group and channel messages are not end-to-end encrypted. Group end-to-end encryption is a separate, larger design task and is not going to be improvised.
  4. Call encryption is unverified in the field. It works in tests; it has not been proven in a real call.
  5. No Content-Security-Policy. There is no known injection path today — messages are rendered as React nodes and there is no raw HTML insertion anywhere in the app — but CSP is the second line that catches tomorrow’s mistake.
  6. Emoji images load from a public CDN. That means a third party sees your IP address simply because the interface contains emoji. Bundling them is straightforward and planned.
  7. Password minimum is six characters. Too short.
  8. No external security audit. Nobody outside the project has reviewed any of this.

A full technical write-up of the same findings, with file references, is published in the repository as SECURITY_ARCHITECTURE_AUDIT.md. Open the audit →

Reporting vulnerabilities

If you find a vulnerability, please report it privately first, and give it a chance to be fixed before it is public. That protects the people using NeyLivo, not the project’s reputation.

There is no bug bounty. The project has no money to pay one, and promising one it cannot honour would be worse than saying this plainly.

Reporting policy in the repository →  ·  Privacy page →