nymChat - Python Messenger

Hello everyone! I’m excited to share nymChat, a python based messaging client that uses Nym’s mixnet via WebSocket. It’s a small start, but the goal is to create the most secure messaging platform on the planet.

You can find the code and README here: GitHub - code-zm/nymChat: Python Messaging Client for secure, asynchronous communication over the Nym Mixnet.

Ensure the nym-client is configured & running before you run the python script.

To send a message, simply run nymChat.py and enter the recipient Nym Client Address.

Some goals I have for this project:

  • client side encryption/decryption
  • usernames + user search
  • groupchats
  • clean front end
  • ios / android apps

Feel free to fork the repo and make any changes you’d like. Also any critiques on what I have so far are much appreciated!

Thanks,
code-zm

5 Likes

hey @code-zm, this is excellent work, thank you for the contribution. Let’s turn your future goals into a roadmap and get you a grant for this project!

How long do you think you’d need to build apps on different platforms with the features you listed above?

  • client side encryption/decryption
  • usernames + user search
  • groupchats
  • clean front end
  • ios / android apps

Keep up the good work :slight_smile:

3 Likes

Hello! I’ve devised a 12 week road map to guide me to release.

Link to roadmap: Blocks And Arrows

2 Likes

Here is the technical diagram, let me know if I am missing anything important!

Link to diagram: Blocks And Arrows

2 Likes

Hey there, cool work! :slight_smile:

As per my post to you in the element chat I think you could do something easy with the address book by parsing something like the incoming SURB sender tag and/or a random nickname assignment pretty easy.

I’d like to hear more re: the key management, since this would be an extra layer of encryption at the app level above the encryption that’s happening at the network level with the client themselves.

Its really cool to see someone writing in Python as well, I’m wondering whether this might be a good opportunity to look into some Rust-Python FFI as we have already done with c++ & Go so you don’t have to rely on having 2 processes running with the standalone binary, but it could all be bundled together. Might this be interesting to you? No worries if not, just thought I’d bring it up.

I’m wondering what your plan is regarding group chats, which I saw you mention in the element chat but I don’t see here.

1 Like

SURBs, User Search, and Key Management

My initial idea was to utilize SURB’s as a “secret chat” feature that replies directly using the SURB instead of a recipient nym address. From my understanding, the initial request must still be sent directly to a nym address. From that point on message routing can be done strictly through SURB’s. This essentially means you have to be doxxed (nym-client address) in order to to communicate through SURBs, which is not good.

A solution could be a centralized server that acts as a registry. Each client will publish a batch of SURBs, an ephemeral public key, and a session ID (sender tag)

For example:
Client A creates and publishes a batch of SURBs to a registry with an associated ephemeral public key + session ID.

Client B could then query this registry using Client A’s session ID and return associated SURBs. This way Client B can initiate conversation with Client A without ever knowing their nym-client address.

As for key management, I am thinking of using ephemeral public keys tied to each batch of SURBs. Each time a batch of SURBs is exhausted, the associated subkey is revoked. The client will then automatically generate new SURBs + subkey and publish them to the registry. The private key will be generated and stored securely on the local machine.

This setup prioritizes anonymity and usability but also introduces the inherent risks of any centralized service.

I am not sure how to implement groupchats yet, I want to focus on the messaging itself first. If anyone has any insights I am all ears.

Thanks,
code-zm

So I really like where this is going, this sort of ‘SURB broadcast / deaddrop’ service! I want to throw a bunch of points in here as its bringing up a few concurrent trains of thought… Let me know if any of these bring up ideas/questions :slight_smile:

What is revealed by a Nym address

This essentially means you have to be doxxed (nym-client address) in order to to communicate through SURBs, which is not good.

Yes the SURB model sort of relies on a client - server model in which you have a client talking to a backend service of some kind you don’t want to doxx yourself too, not necessarily in a situation where your comms/addressing is more peer to peer. It sort of relies on one party (the client) always initiating the connection/request to the other (the server).

Remember that ‘doxxing’ a Nym address only shows which Gateway your client is using as an entry gateway, so overall not a whole lot is being revealed. That said, it is still showing which gateway a client is using, so depending on your threat model could still be undesirable.

Sending SURBs around

Client B could then query this registry using Client A’s session ID and return associated SURBs. This way Client B can initiate conversation with Client A without ever knowing their nym-client address.

You would probably not need to send the SURBs from the server to B to then package and send to A, just have B send their messages to A through the mixnet to the server already encrypted for A, and then the server can just package them and A can decrypt them on the ‘app’ level after they’ve done the Nym Client encryption → Mixnet → NC decryption dance.

MVD

This has the potential to grow into quite a big thing alongside the chat, so I think working out some sort of minimum viable version might be good to contain it a little :slight_smile: That said, I think this is also starting to hone in on an interesting problem that we don’t immediately have a solution for right now, which is how to communicate in a p2p-ish context without having to reveal your Nym address. I also think this might deserve a grant on its own, using the chat as a ‘proof of concept’ that it works. Really interesting stuff.

SURB requests

We might have to expose some method on the client which would allow for SURBs to be ‘manually’ requested, which wouldn’t necessarily be an issue, but this is not something you can do right now.

A high level example: for the moment, assuming that B has had 100 SURBs sent to it from A (B does not know A’s address), but the reply it wants to send requires 120 SURBs, it will use SURB 100 to request more SURBs from A before sending the full 120SURB reply.

What you would maybe want to do in this instance is something more like have the server / intermediary service keep track of how many SURBs remain per ‘bucket’ (the random alphanumeric string used to differentiate SURBs for a session) and then keep sending requests to keep this ‘topped up’ on its own. This might be jumping the gun a bit though in relation to my next point…

SURB lifetimes

For the moment the SURB is valid indefinitely, but the client will purge its local DB of SURBs that are older than a day on restart. We still have a few features to add to the Mixnet to add some extra security which will lower the validity of the actual SURB though: once we implement proper key rotation and replay protection, then SURBs will only be valid for the length of the key epoch. This length is still to be decided.

This means that you shouldn’t assume a situation in which Client A sends a bunch of SURBs to the server which can be used over the next e.g. day, but instead it will be more of a back-and-forth flow of SURB requests and topups.

As such, you’re possibly adding a few back and forth trips through the Mixnet for a message, if we assume that Client B wants to send a message to Client A but requires more SURBs than A current has stored on the server. Not a problem per se, just something to be aware of.

Potential attack

One thing to bear in mind is that there is the possibility of an (active) attack that the server, assuming it is untrusted, could try and perform against the clients. This would involve consistently requesting more and more SURBs from the clients, then sending all of these through the network at once, in order to try and create a ‘burst’ effect in the traffic and monitor which Gateways exhibit this behaviour, in order to work out which Gateways the clients of the SURBs were connected to. This is not necessarily a problem with this design, more a general active attack which it is good to be aware of here.

1 Like

Nym combined with GNUnet architecture development is a good choice

2 Likes

Update #1 01/12/2025

Nym Directory

The nym directory (server) acts as a remailer / directory, enabling user discovery, groupchats, and messaging without ever revealing nym-client addresses. Clients will still have the option to send messages directly p2p in a “secret chat”.

Database

I’ll be using NoSQL with MongoDB. The database will contain the following:
USERS Collection:
username
public key
sender tag
bio (optional)

GROUPS Collection:
group ID
user list

Custom Methods

The server receives messages from the mixnet which have json data encapsulated into the ‘message’ field to specify certain actions for the server.

send: send a message to a given username, signed.
sendAnon: send a message to a given username unsigned.
sendGroup: sends a message to each user in a given group signed, specified by groupID.
confirm: sends a confirmation, used by clients to determine their request was successful or not.
register: adds a user to the db (given a username, publicKey, and senderTag), replies with confirm or deny.
query: searches db for a given username, replies with confirm.
update: update the db for a specified field, signed, replies with confirm.
createGroup: adds a group to the db, generates a groupID and adds initial client to the list.
sendInvite: sends a message containing a groupID and group name, allowing a user to join.
joinGroup: adds a username to userList of a given group specified by groupID.

How it Works

When registering, clients submit the following information to the server, which is added to the db:
Username, sender tag, public key, and an optional bio.

User Discovery + Messages

Alice wants to send a message to Bob.

  1. Alice sends a ‘query’ containing Bob’s username to the Server.
  2. Server responds to Alice with a confirmation including Bob’s username and bio.
  3. Alice sends a ‘send’ to the server including Bob’s username and the message to send.
  4. The server finds Bob’s senderTag in the local DB and forwards Alice’s message to him using a reply type mixnet message.
  5. Bob receives the message, then sends a ‘send’ to the server including the recipient Alice and the message to send.
  6. The server finds Alice’s senderTag in the DB and forwards Bob’s message to her using a reply type mixnet message.
  7. Repeat steps 3 - 6.

Groupchats

Alice wants to create a groupchat with Bob and Charlie:

  1. Alice sends a ‘createGroup’ to the server.
  2. The server adds a new group to the collection.
  3. Alice sends a ‘sendInvite’ to both Bob and Charlie.
  4. Bob and Charlie receive the invite, then join by sending a ‘joinGroup’ to the server.
  5. The server adds Bob and Charlie to the userList. The userList now contains Alice, Bob, and Charlie.
  6. Alice sends a ‘sendGroup’ to the server, specifying the groupID and the message to send.
  7. The server queries the DB for the given group’s userList.
  8. The server sends the message to Bob and Charlie.

SURB Topups

I’m still not set on how this will work.

The server could keep track of the number of SURBs in each ‘bucket’. When the number of SURBs remaining is healthy, the user state is set to 1. When it reaches a certain threshold, the state is set to 0 and a ‘request’ is sent to the user to send more SURBs. While a user’s state is set to 0, messages intended for them will be placed in a MESSAGES collection, containing the username and a list of messages. When the user sends more SURBs, the state is set back to 1 and the messages in the queue get sent FIFO.

Client Mockups

Feel free to ask any questions! Currently, the codebase is in a private repo. I plan on making it public soon after a few more refinements!

4 Likes

This happens under the hood anyway, on a message trying to be sent. So as soon as your server runs out of SURBs it will request more anyway! There shouldn’t be a need for you to do / implement anything here.

1 Like

This might complicate your model, but what sticks out here is: how will a user, if they have to change client address (e.g. a gateway goes down) be able to update ?

Perfect! Shout out to whoever implemented that!

I’m going to use ECDSA to validate the updates. When registering, clients generate a long term key pair using NIST P-256. The public key gets sent to the server and stored in the local db. For updates, clients will sign the changes using their private key, and the server will verify the signature using the stored public key. This will only be used for validating updates / other methods that are signed, all communications will still rely on MLS.