Skip to content

Using Voynich to Create Chat Applications

Jack Lund edited this page Mar 14, 2024 · 2 revisions

Voynich is mainly aimed at allowing people to create chat applications using it as a library. There is example code for a simple terminal-based chat application that you can use as a template.

Basic Structure of the Code

Your code should do the following, roughly in this order:

  • Parse the command line and/or read in any configuration files
  • Create the StandardLogger object (see below)
  • Connect to Tor using whatever connection options (host/port, authentication, etc) to get a control connection. This will be used to create the onion service(s)
  • Create the onion service you'll be using using the control connection
  • Run an (optional) connection test. This basically tries to connect to the onion service you just created to make sure it's accessible. This step is optional, but recommended, in case there is any issue with the Tor connection.
  • Create the Engine (see below)
  • Hook the Engine and the Onion service listener created when you created your onion service into your UI's event loop and run it.

Example code for the terminal application is here

Configuration

These are the values that are configurable within Voynich:

  • Tor address - the address of the Tor control connection (default: 127.0.0.1:9051)
  • Tor proxy address - the address of the Tor proxy for outgoing Tor connections (default: 127.0.0.1:9050)
  • Tor onion service port - the port to associate with your onion service
  • Listen address - the local address to listen for incoming connections to your onion service (default: 127.0.0.1:<service-port>)
  • Tor authentication - how to authenticate to the Tor control connection. Possible values are "safe-cookie", "hashed-password" or "null". See the "Authentication" section below for more information. (default: null)
  • Onion service type - either "transient" or "persistent". If "persistent", and a service doesn't exist with the same onion address, it will be created; otherwise Voynich will just use the existing one (no default)

There's an optional configuration file format which you can use, in the Config struct. By default, it looks for a TOML file that looks something like this. There's also a file with the values filled in here.

There's a utility function for reading the config file, an example of using it is here. If you don't pass in an explicit location for the file, it, by default, looks for it in the following directories, in the given order:

  • $XDG_CONFIG_HOME/voynich/config.toml
  • ~/.config/voynich/config.toml

If it doesn't find it in either place, it sets reasonable defaults.

Tor Authentication

Tor has three different methods for authenticating to the control connection (used for creating the onion services):

  • Null - No authentication. Anyone who can connect to the control connection can issue commands
  • Hashed Password - A password is configured (see the Tor docs) and that password has to be passed to the Tor control connection when connecting
  • Safe Cookie - A random cookie value is created and stored in a file. To authenticate you must pass the cookie value when connecting to the Tor control connection. Voynich can query the control connection for the location of this cookie file and, if it has read permissions, can read the file and pass the cookie value to Tor without you having to give it to it.

Logging

The StandardLogger is an asynchronous logging object used throughout the Voynich code. It's basically a circular buffer of log messages written by the various parts of Voynich, which you can display to the user as you see fit. It's intended for both application- and system-level messages which will be displayed to the user. If you set the debug option, troubleshooting messages will also be displayed.

Onion Service Listener

The Onion service listener is a standard network listener which listens for incoming connection via your onion service using the OnionServiceListener::accept() method. It is created when the onion service is created, and should be one of the things your UI event handler should be multiplexing on.

Engine

The Engine is the heart of the Voynich application. It's how you connect to other users, send and receive messages, and it serves as the event manager for the whole system. Your UI should make calls to Engine::get_event() to receive and handle Voynich network events (new incoming connections, incoming messages and closed connections), and call Engine::handle_incoming_connection() when receiving connections through the Onion service listener accept() method.

An example of putting this all together can be found here.