wardcore

joined 1 month ago
[–] wardcore@lemmy.world 3 points 5 days ago (1 children)

Just to clarify — LAN mode is an optional feature, not the only way the app works. By default ONYX works like any other messenger through a central server, with E2EE for private chats (unlike Telegram, where only Secret Chats are E2EE). LAN mode is an additional feature for people on the same local network who want to communicate without any internet or server involved at all — useful for office environments, local events, or situations where internet access isn't available or isn't wanted. So to answer your question: in normal mode, the server handles routing. In LAN mode, UDP broadcast handles discovery automatically — no manual IP tracking needed.

[–] wardcore@lemmy.world 5 points 5 days ago (3 children)

Fair question. The honest answer is: it depends on what you need. Matrix is a protocol with federation, a large ecosystem, and years of development behind it. If you need federation or interoperability with other services, Matrix is the better choice. ONYX is different in a few ways:

LAN mode — works entirely without internet via UDP broadcast, no server needed

Simpler self-hosting — one binary, runs from the command line, no complicated setup

Native Flutter client — single codebase for Android, iOS, Windows, macOS, Linux

The tradeoff is that ONYX is less mature, still in beta, and doesn't have federation yet (it's on the roadmap). If you're already happy with Matrix, there's probably no reason to switch. If you want something lighter and simpler with a focus on privacy and offline capability, it might be worth trying.

 

ONYX development update: v1.2a and v1.3-beta

A quick update on what's changed since my last post. Two beta dropped over the past few weeks. I would like to share this here so that I can possibly find my audience.


[1.2a-beta] — 2026-04-07

Added

  • Application autostart — the app can now start automatically on system boot, Windows.
  • Message animations in external groups and channels — animations were missing in external (self-hosted) instances, now consistent across all chat types

Improved

  • Editing and deleting messages in groups and channels — this was broken or limited in non-external groups/channels, now works properly

Fixed

  • Favorite icon color display in edit mode
  • Voice message duration display in send preview

[1.3-beta] — 2026-04-21

Added

  • Message reactions — react to messages
  • User blocking — block list and the ability to block individual users
  • Hide content in notifications — shows "New message" instead of actual content on the lock screen
  • PIN and biometric prompt when switching accounts — multi-account users now need to authenticate when switching, prevents unauthorized access if someone has physical access to the device
  • Relog notification — notifies when session token is expiring instead of silently failing
  • Video download progress — progress indicator when downloading videos from the server

About the project

ONYX is an open source messenger with E2EE private chats, LAN mode (works without internet via UDP broadcast), and self-hostable groups and channels.

Bugs and issues — reach out to @support directly in ONYX.

[–] wardcore@lemmy.world 3 points 1 week ago (1 children)

Messages are stored on the server only until the recipient comes online. Once they connect, a 30-second timer starts — after that the message is permanently deleted from the server. There's no copy left server-side after that point. Since private chats use E2EE, the server only ever sees ciphertext anyway. After delivery, messages exist only locally on both devices. If you want no local record either, you can delete the conversation manually from within the app.

 

ONYX 1.3-beta — reactions, blocking, and notification privacy

A follow-up to my previous post. The last post got a decent amount of feedback — some might call it small, but every response genuinely matters and it was great to see people's reactions to the project.

New beta is out today. I would like to share some changes.


ONYX — what's new

Message reactions — you can now react to messages. Basic, but frequently requested.

User blocking — block list and the ability to block users. Should have been there from the start, honestly.

Hide content in notifications — notifications now show "New message" instead of the actual content. Relevant if you do not want your decrypted messages to be stored in the device cache.

PIN and biometric prompt when switching accounts — switching between accounts now requires authentication. Prevents someone with physical access to your phone from jumping between accounts freely. This only works if you have PIN or biometric unlocking enabled in settings.

Relog notification — the app now notifies you when your session token is about to expire.

Video download progress — progress indicator when receiving videos from the server.


ONYX Server

The self-hosted server software also got an update — reaction support has been added, along with a handful of bug fixes.


Bugs and issues — reach out to @support directly in ONYX.

[–] wardcore@lemmy.world 2 points 3 weeks ago

I understand it's not for everyone. But the 16-character minimum is there for a reason — your password is the only key to your account, no fallbacks, no recovery via phone or email. That requires a strong password. There's a built-in password generator in the app — one tap, cryptographically secure, 16 characters, done. Save it once and you won't need to type it again. Think of it like a crypto wallet seed phrase — you store it once somewhere safe and that's it. If the priority is speed over security, Telegram is a better fit. ONYX was built for people who actually care about privacy, and that comes with a slightly higher entry bar. That said, I'll consider dropping the hard minimum to 8 characters with a strong recommendation to use 16 — so people have the choice but know the tradeoffs.

[–] wardcore@lemmy.world 1 points 3 weeks ago (4 children)

Yes, 16 characters minimum. Since there's no phone number, no email, and no alternative recovery method - the password is the only thing protecting your account. A weak password with no fallback is a real risk, so I set the bar higher intentionally. It also reduces brute force viability. Passphrases are supported but currently not used for login - just the password for now.

 

ONYX v1.2-beta - now actually comfortable to use

I've been working on ONYX for a while now, and v1.1 was honest about one thing — it was secure, but using it every day felt like a chore. This update is mostly me fixing that.


What's new

  • File uploads now show a preview and progress.
  • Message forwarding — took longer than it should have, but it's in
  • Keyword search across chats
  • Smooth send/receive animations — on by default if you search the settings, and I liked having them
  • Tap a reply to jump to the original message
  • Message pinning (local only for now — I want to add it for both sides in next patches)

Improved

  • LAN mode stability — this one was long overdue
  • General performance

Fixed

  • LAN mode connection issues
  • Various bug fixes

Still a lot to do.

If you want to follow what's coming — there's an update channel inside the app: 12e01467-c154-447b-84f8-133ae76684a1 (channel token)

https://github.com/wardcore-dev/onyx/releases/tag/v1.2-beta

Feedback in the comments is welcome.

[–] wardcore@lemmy.world 3 points 1 month ago

Write to @support directly in ONYX, using the search field, and we'll discuss this in detail.

[–] wardcore@lemmy.world 4 points 1 month ago

Honestly, the main motivation was just curiosity - I wanted to see if I could build something like this, and then put it out there to see if anyone actually cares.

[–] wardcore@lemmy.world 2 points 1 month ago

Fair point, I just missed adding it to .gitignore.

 

I got tired of every messenger asking for a phone number before letting you talk to anyone. Most privacy-focused messengers make you choose between convenience and privacy — either you get a polished experience with questionable data practices, or you get strong privacy with a painful setup. I tried to find the balance between the two. The result is ONYX. I want to be upfront about what it does and doesn't do privacy-wise — because "private messenger" is a claim that deserves scrutiny.


What the server actually knows about you

  • Your username (not tied to phone, email, or anything real-world)
  • That your devices exist (but not their private keys)
  • Your IP address when connected (unavoidable)
  • Message content in groups and channels — these are not E2EE encrypted, that's a real limitation

What it doesn't know:

  • Your phone number or email — never collected
  • Content of private chats — server only sees ciphertext
  • Your favorites — stored locally only, server has no knowledge of them

How the E2EE actually works

Private chats use X25519 ECDH for key exchange (ephemeral per session) and HKDF-SHA256 for key derivation. I chose XChaCha20-Poly1305 over AES-GCM because it runs in constant time without AES-NI hardware acceleration — relevant for mobile devices — and has a 192-bit nonce vs 96-bit in GCM, which reduces collision risk in long sessions. LAN mode uses AES-GCM-256 — sessions are short-lived and never leave the local network.

Packet format (internet):

E2EEv1:[base64 JSON envelope: {eph pubkey 32B, nonce 24B, ciphertext, mac 16B}]

Packet format (LAN):

[pubkey 32B] [nonce 12B] [ciphertext] [mac 16B]

Multi-device works without the server touching your keys — new device sends an auth request to an existing trusted device, which must explicitly approve it before any key exchange happens.


The honest limitations

  • Groups and channels have no E2EE — the only way to get full control over group communication is to self-host your own instance, so at least no third party has access to the data.
  • Only incoming messages sync across devices — outgoing stay on the sending device only
  • The server knows your IP when you're connected

LAN mode

There's a mode that works with no server and no internet at all — devices discover each other via UDP broadcast on the local network, exchange X25519 public keys in the broadcast packet itself, and communicate directly encrypted. Just hold the "Send" button in a private chat to choose this mode. Useful if you don't want any data leaving your network.


Self-hosted groups and channels

If you don't want to rely on the central ONYX server at all, you can run your own. There's a separate server software written in Rust — you download it, run it, and host your own groups or channels for your community or just a small group of friends. No web interface, runs entirely from the command line. Available for both Windows and Linux.

https://github.com/wardcore-dev/onyx-server/releases


Registration and account deletion

Username and password only. No phone, no email. Accounts can be deleted at any time and take all server-side data and media with them.


The project is still in early beta — rough edges are expected. If you run into any bugs or weird behavior, feel free to reach out directly to @support in ONYX. Just write your issue there and I'll look into it.

Available for Windows, Linux, macOS, and Android: https://github.com/wardcore-dev/onyx/releases

[–] wardcore@lemmy.world 2 points 1 month ago

that's a great idea, I'll consider adding it in one of the upcoming updates.

[–] wardcore@lemmy.world 0 points 1 month ago (4 children)

Fair skepticism, but no - I used AI for the English translation of my post, since I'm not a native speaker.

[–] wardcore@lemmy.world -1 points 1 month ago

Just to clarify — E2EE in ONYX is only for private chats. Groups and channels (both built-in and self-hosted) don't have E2EE, which is actually closer to your point — for groups it's a deliberate tradeoff for simplicity and reliable sync. So you're right, for that use case TLS is enough.

[–] wardcore@lemmy.world 21 points 1 month ago (7 children)

Fair point! Yes, Claude was used as a coding assistant throughout the project. That said, every single line went through strict manual review — nothing was blindly copy-pasted into the codebase. All architectural decisions, the crypto stack choices, and the overall design are my own. Claude helped with boilerplate and speeding things up, but the project is not "vibe-coded".

 

When you look at existing self-hosted messengers, you usually see one of two things: either complex infrastructure that's hard to deploy (Matrix/Synapse), or minimalism with no encryption. ONYX is an attempt to find the middle ground: easy to deploy, real E2E encryption, and the ability to work entirely in a local network without internet at all.


Project architecture

Component Technology
Client Flutter (Android, Windows, macOS, Linux)
Server Node.js — Express + express-ws + ws
Database MariaDB + Redis (sessions, cache)
File storage S3-compatible (AWS SDK v3)
Transport WebSocket (wss://) + HTTP/REST
Encryption X25519 + XChaCha20-Poly1305 + AES-256-GCM

LAN mode: works without internet

One of the key features of ONYX is the ability to communicate in a local network without internet at all. A custom device auto-discovery mechanism handles this entirely.

Discovery protocol via UDP broadcast

Every client broadcasts a JSON packet to 255.255.255.255:45678 every 5 seconds:

{
  "username": "alice",
  "timestamp": 1710000000000,
  "pubkey": "<X25519 public key, base64>"
}

All other clients listen on that port and update two local tables:

  • username → source IP address
  • username → X25519 public key

No mDNS, no manual IP entry — just pure UDP broadcast. The public key is included directly in the broadcast packet, so encrypted communication can start immediately without an additional handshake.

Media transfer in LAN

Media files go through a separate channel on port 45679, split into ~32 KB chunks. Each chunk is encrypted independently with AES-256-GCM, which allows decryption and rendering to begin before the full file is received.


Encryption: two layers on elliptic curves

No RSA anywhere in the project — only a modern elliptic curve stack.

E2EE scheme for private chats

  1. Key exchange: X25519 ECDH with an ephemeral key pair per session
  2. Key derivation: HKDF-SHA256 with a context label (onyx-lan-v2 for LAN, separate labels for E2EE chats)
  3. Encryption: XChaCha20-Poly1305 AEAD

Packet format:

[pubkey 32B] [nonce 12B] [ciphertext] [mac 16B]

Why XChaCha20-Poly1305 and not AES-GCM?

AES-GCM requires hardware acceleration (AES-NI) for decent performance. XChaCha20-Poly1305 runs in constant time on any hardware — important for mobile devices without AES-NI. It also has a wider nonce (192 bits vs 96 for GCM), which reduces collision risk in long sessions.

AES-256-GCM is used for LAN media transfers — chunked delivery, and hardware acceleration is available on most desktops.


Multi-device and E2EE

When a new device connects to an account, it sends an authorization request to a trusted device. The trusted device must explicitly approve the new one — only then does the key exchange happen. The server never has access to decrypted content, even when adding a new device.

Multi-device sync

When a message arrives, the server sends it to each of your devices separately — encrypted with that device's specific public key. Technically these are different encrypted messages for each device, just with the same plaintext inside.

One honest limitation: only incoming messages sync across devices. Outgoing messages are visible only on the device they were sent from. Full bidirectional sync with E2EE requires either a "copy to self" encryption mechanism or server-side plaintext storage — both are worse tradeoffs.


Why Flutter and not Electron or native development

The requirement from day one: one codebase for Windows, macOS, Linux and Android. Three options were considered:

Option Problem
Native development 3–5 separate codebases, constant desync
Electron +150–200 MB RAM, DOM rendering
Flutter Single codebase, Skia/Impeller, real 60fps

Flutter Desktop required writing 10+ separate optimization modules (fps_booster, fps_optimizer, fps_stimulator, message_load_optimizer, chat_preloader) — Flutter on desktop lags noticeably without tuning. But the result is smooth UI across all four platforms from one repo.


Desktop-specific integrations

  • System tray — app minimizes to tray instead of closing.
  • Single-instance — prevents multiple copies via IPC. A second launch focuses the existing window.
  • Custom titlebar — system titlebar hidden, custom header with drag zone
  • Windows-native notifications — separate module, not Flutter overlay
  • Autostart on system boot

Security beyond E2EE

  • PIN + biometrics — Face ID / fingerprint via Flutter Secure Storage
  • Proxy supportproxy_manager.dart, routing through any proxy
  • Secure storage — all sensitive data through OS secure storage (Keychain / Android Keystore)
  • Active session management — all connected devices visible, any session can be terminated remotely — but only from a trusted device

Self-hosted groups and channels

Two types of groups and channels in ONYX — fundamentally different models.

Built-in (via ONYX server)

Standard groups and channels work through the central ONYX server and are not encrypted — a deliberate tradeoff for reliable sync. Suitable for open communities where E2EE is not a requirement.

External (self-hosted)

Anyone can run their own instance — on a VPS, home server, or local network:

Use case Description
Local network File sharing and chat within office/home network, no internet
Private community Closed group on your VPS, join by invite
Public channel You host it, subscribers read posts
  • Group — two-way, all participants can write
  • Channel — one-way broadcast, only admins publish

Connect to any external server directly from the app — enter the instance address and join.

Deploying your own instance

Server software — ONYX Server: github.com/wardcore-dev/onyx-server


Favorites: local notes and storage

ONYX has a dedicated Favorites tab — not a "Saved Messages" clone, but a proper local notebook. Create any number of favorite chats, each with its own avatar and name, as separate categories: passwords, ideas, saved media, links.

Everything is stored locally on the device — nothing sent to the server, nothing synced. The server knows nothing about your favorites.


Accounts: anonymity, multi-account and deletion

  • Registration — username and password only. No phone number, no email.
  • Username — chosen once, permanent. Can never be changed. You can change your display name, but not the username itself.
  • Multi-account — register and hold any number of accounts, switch freely.
  • Account deletion — delete at any time with all media and server-side data. No traces left.

Current state

Project is in working beta. Development is ongoing. Happy to answer questions in the comments.

Try it out

view more: next ›