{"id":19973250,"url":"https://github.com/zeromq/zyre","last_synced_at":"2025-05-15T22:12:09.208Z","repository":{"id":37752316,"uuid":"6257879","full_name":"zeromq/zyre","owner":"zeromq","description":"Zyre - an open-source framework for proximity-based peer-to-peer applications","archived":false,"fork":false,"pushed_at":"2024-10-03T20:09:58.000Z","size":5547,"stargazers_count":904,"open_issues_count":8,"forks_count":176,"subscribers_count":59,"default_branch":"master","last_synced_at":"2025-05-13T21:00:25.770Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zeromq.png","metadata":{"files":{"readme":"README.md","changelog":"NEWS","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2012-10-17T07:59:01.000Z","updated_at":"2025-05-09T06:54:01.000Z","dependencies_parsed_at":"2022-07-13T03:50:39.757Z","dependency_job_id":"8a54990b-99af-4a4c-aa5d-837538aff434","html_url":"https://github.com/zeromq/zyre","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeromq%2Fzyre","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeromq%2Fzyre/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeromq%2Fzyre/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeromq%2Fzyre/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zeromq","download_url":"https://codeload.github.com/zeromq/zyre/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254430335,"owners_count":22069909,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-13T03:10:47.631Z","updated_at":"2025-05-15T22:12:04.198Z","avatar_url":"https://github.com/zeromq.png","language":"C","funding_links":[],"categories":["Networking","Networking and Internet","网络和互联网"],"sub_categories":["Advanced books","高级书籍"],"readme":"\n[![GitHub release](https://img.shields.io/github/release/zeromq/zyre.svg)](https://github.com/zeromq/zyre/releases)\n[![OBS draft](https://img.shields.io/badge/OBS%20master-draft-yellow.svg)](http://software.opensuse.org/download.html?project=network%3Amessaging%3Azeromq%3Agit-draft\u0026package=zyre)\n[![OBS stable](https://img.shields.io/badge/OBS%20master-stable-yellow.svg)](http://software.opensuse.org/download.html?project=network%3Amessaging%3Azeromq%3Agit-stable\u0026package=zyre)\n\u003ca target=\"_blank\" href=\"http://webchat.freenode.net?channels=%23zeromq\u0026uio=d4\"\u003e\u003cimg src=\"https://cloud.githubusercontent.com/assets/493242/14886493/5c660ea2-0d51-11e6-8249-502e6c71e9f2.png\" height = \"20\" /\u003e\u003c/a\u003e\n[![license](https://img.shields.io/badge/license-MPL%202.0-green.svg)](https://github.com/zeromq/zyre/blob/master/LICENSE)\n\n# Zyre - Local Area Clustering for Peer-to-Peer Applications\n\n| Linux \u0026 MacOSX | Windows  |\n|:--------------:|:--------:|\n|[![Build Status](https://travis-ci.org/zeromq/zyre.png?branch=master)](https://travis-ci.org/zeromq/zyre)|[![Build status](https://ci.appveyor.com/api/projects/status/kuugogjji97yblqe?svg=true)](https://ci.appveyor.com/project/zeromq/zyre)|\n## Contents\n\n\n**[Overview](#overview)**\n\n**[Scope and Goals](#scope-and-goals)**\n\n**[Ownership and License](#ownership-and-license)**\n\n**[Using Zyre](#using-zyre)**\n\n**[Building on Linux and macOS](#building-on-linux-and-macos)**\n\n**[Building on Windows](#building-on-windows)**\n\n**[Building on Windows](#building-on-windows)**\n*  [Using CMake](#using-cmake)\n\n**[Linking with an Application](#linking-with-an-application)**\n\n**[Use from Other Languages](#use-from-other-languages)**\n\n**[API Summary](#api-summary)**\n*  [zyre - API wrapping one Zyre node](#zyre---api-wrapping-one-zyre-node)\n*  [zyre_event - no title found](#zyre_event---no-title-found)\n\n**[Hints to Contributors](#hints-to-contributors)**\n\n**[This Document](#this-document)**\n\n## Overview\n\n### Scope and Goals\n\nZyre provides reliable group messaging over local area networks. It has these key characteristics:\n\n* Zyre needs no administration or configuration.\n* Peers may join and leave the network at any time.\n* Peers talk to each other without any central brokers or servers.\n* Peers can talk directly to each other.\n* Peers can join groups, and then talk to groups.\n* Zyre is reliable, and loses no messages even when the network is heavily loaded.\n* Zyre is fast and has low latency, requiring no consensus protocols.\n* Zyre is designed for WiFi networks, yet also works well on Ethernet networks.\n* Time for a new peer to join a network is about one second.\n\nTypical use cases for Zyre are:\n\n* Local service discovery.\n* Clustering of a set of services on the same Ethernet network.\n* Controlling a network of smart devices (Internet of Things).\n* Multi-user mobile applications (like smart classrooms).\n\nTechnical details:\n\n* Uses RFC 36 (http://rfc.zeromq.org/spec:36/ZRE) protocol for discovery and heartbeating.\n* Uses reliable Dealer-Router pattern for interconnection, assuring that messages are not lost unless a peer application terminates.\n* Optimized for WiFi, using UDP broadcasts for discovery and heartbeating…\n* Offers alternative discovery mechanism (gossip) for Ethernet networks.\n\n### Ownership and License\n\nThe contributors are listed in AUTHORS. This project uses the MPL v2 license, see LICENSE.\n\nZyre uses the [C4.1 (Collective Code Construction Contract)](http://rfc.zeromq.org/spec:22) process for contributions.\n\nZyre uses the [CLASS (C Language Style for Scalabilty)](http://rfc.zeromq.org/spec:21) guide for code style.\n\nTo report an issue, use the [Zyre issue tracker](https://github.com/zeromq/zyre/issues) at github.com.\n\n## Using Zyre\n\n### Building on Linux and macOS\n\nTo start with, you need at least these packages:\n* `git` -- git is how we share code with other people.\n* `build-essential`, `libtool`, `pkg-config` - the C compiler and related tools.\n* `autotools-dev`, `autoconf`, `automake` - the GNU autoconf makefile generators.\n* `cmake` - the CMake makefile generators (an alternative to autoconf).\n\nPlus some others:\n* `uuid-dev`, `libpcre3-dev` - utility libraries.\n* `valgrind` - a useful tool for checking your code.\n* `pkg-config` - an optional useful tool to make building with dependencies easier.\n\nWhich we install like this (using the Debian-style apt-get package manager):\n\n```\n    sudo apt-get update\n    sudo apt-get install -y \\\n    git build-essential libtool \\\n    pkg-config autotools-dev autoconf automake cmake \\\n    uuid-dev libpcre3-dev valgrind\n\n    # only execute this next line if interested in updating the man pages as\n    # well (adds to build time):\n    sudo apt-get install -y asciidoc\n```\nHere's how to build Zyre from GitHub (building from packages is very similar, you don't clone a repo but unpack a tarball), including the libsodium (for security) and libzmq (ZeroMQ core) libraries:\n\n```\n    git clone --depth 1 -b stable https://github.com/jedisct1/libsodium.git\n    cd libsodium\n    ./autogen.sh \u0026\u0026 ./configure \u0026\u0026 make check\n    sudo make install\n    cd ..\n\n    git clone https://github.com/zeromq/libzmq.git\n    cd libzmq\n    ./autogen.sh\n    # do not specify \"--with-libsodium\" if you prefer to use internal tweetnacl\n    # security implementation (recommended for development)\n    ./configure --with-libsodium\n    make check\n    sudo make install\n    sudo ldconfig\n    cd ..\n\n    git clone https://github.com/zeromq/czmq.git\n    cd czmq\n    ./autogen.sh \u0026\u0026 ./configure \u0026\u0026 make check\n    sudo make install\n    sudo ldconfig\n    cd ..\n\n    git clone https://github.com/zeromq/zyre.git\n    cd zyre\n    ./autogen.sh \u0026\u0026 ./configure \u0026\u0026 make check\n    sudo make install\n    sudo ldconfig\n    cd ..\n```\n\n\nTest by running the `zyre_selftest` command:\n\n    zyre/src/.libs/zyre_selftest\n\nTest by running the `zpinger` command, from two or more PCs.\n\n    zyre/src/.libs/zpinger\n\n### Building on Windows\n\nTo start with, you need MS Visual Studio (C/C++). The free community edition works well.\n\nThen, install git, and make sure it works from a DevStudio command prompt:\n\n```\ngit\n```\n\nNow let's build Zyre from GitHub:\n\n```\n    git clone --depth 1 -b stable https://github.com/jedisct1/libsodium.git\n    git clone https://github.com/zeromq/libzmq.git\n    git clone https://github.com/zeromq/czmq.git\n    git clone https://github.com/zeromq/zyre.git\n    cd zyre\\builds\\msvc\n    configure.bat\n    cd build\n    buildall.bat\n    cd ..\\..\\..\\..\n```\n\nTest by running the `zyre_selftest` command:\n```\n    dir/s/b zyre_selftest.exe\n    zyre\\builds\\msvc\\vs2013\\DebugDEXE\\zyre_selftest.exe\n    zyre\\builds\\msvc\\vs2013\\ReleaseDEXE\\zyre_selftest.exe\n\n    :: select your choice and run it\n    zyre\\builds\\msvc\\vs2013\\DebugDEXE\\zyre_selftest.exe\n```\nTest by running `zpinger` from two or more PCs:\n\n```\n    dir/s/b zpinger.exe\n    zyre\\builds\\msvc\\vs2013\\DebugDEXE\\zpinger.exe\n    zyre\\builds\\msvc\\vs2013\\ReleaseDEXE\\zpinger.exe\n    zyre\\builds\\msvc\\vs2013\\x64\\DebugDEXE\\zpinger.exe\n\n    :: select your choice and run it\n    zyre\\builds\\msvc\\vs2013\\ReleaseDEXE\\zpinger.exe\n```\n\n### Building on Windows\n\nTo start with, you need MS Visual Studio (C/C++). The free community edition works well.\n\nThen, install git, and make sure it works from a DevStudio command prompt:\n\n```\ngit\n```\n\n#### Using CMake\n\n`zyre` requires `czmq` and `libzmq`, so we need to build `libzmq` first. For `libzmq`, you can optionally use [libsodium](https://github.com/jedisct1/libsodium) as the curve encryption library. So we will start from building `libsodium` in the following (and you can bypass the building of `libsodium` if you are ok with libzmq's default curve encryption library):\n\n```\ngit clone --depth 1 -b stable https://github.com/jedisct1/libsodium.git\ncd libsodium\\builds\\msvc\\build\nbuildall.bat\ncd ..\\..\\..\\..\n```\n\nOnce done, you can find the library files under `libsodium\\bin\\\u003cWin32|x64\u003e\\\u003cDebug|Release\u003e\\\u003cPlatform Toolset\u003e\\\u003cdynamic|ltcg|static\u003e`.\n\nHere, the `\u003cPlatform Toolset\u003e` is the platform toolset you are using: `v100` for `VS2010`, `v140` for `VS2015`, `v141` for `VS2017`, etc.\n\n```\ngit clone https://github.com/zeromq/libzmq.git\ncd libzmq\nmkdir build\ncd build\ncmake .. -DBUILD_STATIC=OFF -DBUILD_SHARED=ON -DZMQ_BUILD_TESTS=ON -DWITH_LIBSODIUM=ON -DCMAKE_INCLUDE_PATH=..\\libsodium\\src\\libsodium\\include -DCMAKE_LIBRARY_PATH=..\\libsodium\\bin\\Win32\\Release\\\u003cPlatform Toolset\u003e\\dynamic -DCMAKE_INSTALL_PREFIX=C:\\projects\\libs\ncmake --build . --config Release --target install\ncd ..\\..\\\n```\n`-DWITH_LIBSODIUM=ON` is necessary if you want to build `libzmq` with `libsodium`. `CMAKE_INCLUDE_PATH` option tells `libzmq` where to search for `libsodium`'s header files. And the `CMAKE_LIBRARY_PATH` option tells where to search for libsodium library files. If you don't need `libsodium` support, you can omit these three options.\n\n`-DCMAKE_INSTALL_PREFIX=C:\\libzmq` means we want to install `libzmq` into the `C:\\libzmq`. You may need to run your shell with administrator privilege in order to write to the system disk.\n\nNext, let's build `czmq`:\n\n```\ngit clone https://github.com/zeromq/czmq.git\ncd czmq\nmkdir build\ncd build\ncmake .. -DCZMQ_BUILD_SHARED=ON -DCZMQ_BUILD_STATIC=OFF -DCMAKE_PREFIX_PATH=C:\\projects\\libs -DCMAKE_INSTALL_PREFIX=C:\\projects\\libs\ncmake --build . --config Release --target install\n```\n\nRemember that we installed `libzmq` to `C:\\projects\\libs` through specifying `-DCMAKE_INSTALL_PREFIX` in the previous step. We here use `-DCMAKE_PREFIX_PATH=C:\\projects\\libs` to tell `czmq` where to search for `libzmq`.\n\nThat is not the whole story. We didn't mention the building of `libcurl`, `lz4`, `libuuid` and other `czmq` optional libraries above. In fact, to build all of these optional libraries successfully is really tricky. Please refer issue [#1972](https://github.com/zeromq/czmq/issues/1972) for more details.\n\nNow, it is time to build `zyre`:\n\n```\ngit clone https://github.com/zeromq/zyre.git\ncd zyre\nmkdir build\ncd build\ncmake .. -DZYRE_BUILD_SHARED=ON -DZYRE_BUILD_STATIC=OFF -DCMAKE_PREFIX_PATH=C:\\projects\\libs\ncmake --build . --config Release\nctest -C Release\n```\n\n### Linking with an Application\n\nInclude `zyre.h` in your application and link with libzyre. Here is a typical gcc link command:\n\n    gcc myapp.c -lzyre -lczmq -lzmq -o myapp\n\n### Use from Other Languages\n\nThis is a list of auto-generated bindings:\n\n* https://github.com/zeromq/zyre/tree/master/bindings/jni - Java ([Examples](https://github.com/zeromq/zyre/tree/master/examples/jni))\n* https://github.com/zeromq/zyre/tree/master/bindings/nodejs - NodeJS\n* https://github.com/zeromq/zyre/tree/master/bindings/python - Python\n* https://github.com/zeromq/zyre/tree/master/bindings/python_cffi - Python (cffi)\n* https://github.com/zeromq/zyre/tree/master/bindings/qml - QML\n* https://github.com/zeromq/zyre/tree/master/bindings/qt - Qt\n* https://github.com/zeromq/zyre/tree/master/bindings/ruby - Ruby (FFI)\n\n### API Summary\n\nThis is the API provided by Zyre 2.x, in alphabetical order.\n\n#### zyre - API wrapping one Zyre node\n\nZyre does local area discovery and clustering. A Zyre node broadcasts\nUDP beacons, and connects to peers that it finds. This class wraps a\nZyre node with a message-based API.\n\nAll incoming events are zmsg_t messages delivered via the zyre_recv\ncall. The first frame defines the type of the message, and following\nframes provide further values:\n\n    ENTER fromnode name headers ipaddress:port\n        a new peer has entered the network\n    EVASIVE fromnode name\n        a peer is being evasive (i.e. quiet) and will be pinged manually\n    SILENT fromnode name\n        a peer has been quiet and has not answered ping after 1 second\n    EXIT fromnode name\n        a peer has left the network\n    JOIN fromnode name groupname\n        a peer has joined a specific group\n    LEAVE fromnode name groupname\n        a peer has joined a specific group\n    WHISPER fromnode name message\n        a peer has sent this node a message\n    SHOUT fromnode name groupname message\n        a peer has sent one of our groups a message\n\nIn SHOUT and WHISPER the message is zero or more frames, and can hold\nany ZeroMQ message. In ENTER, the headers frame contains a packed\ndictionary, see zhash_pack/unpack.\n\nTo join or leave a group, use the zyre_join and zyre_leave methods.\nTo set a header value, use the zyre_set_header method. To send a message\nto a single peer, use zyre_whisper. To send a message to a group, use\nzyre_shout.\n\nTodo: allow multipart contents\n\nThis is the class interface:\n\n```h\n    //  This is a stable class, and may not change except for emergencies. It\n    //  is provided in stable builds.\n    //  This class has draft methods, which may change over time. They are not\n    //  in stable releases, by default. Use --enable-drafts to enable.\n    //  Constructor, creates a new Zyre node. Note that until you start the\n    //  node it is silent and invisible to other nodes on the network.\n    //  The node name is provided to other nodes during discovery. If you\n    //  specify NULL, Zyre generates a randomized node name from the UUID.\n    ZYRE_EXPORT zyre_t *\n        zyre_new (const char *name);\n    \n    //  Destructor, destroys a Zyre node. When you destroy a node, any\n    //  messages it is sending or receiving will be discarded.\n    ZYRE_EXPORT void\n        zyre_destroy (zyre_t **self_p);\n    \n    //  Return our node UUID string, after successful initialization\n    ZYRE_EXPORT const char *\n        zyre_uuid (zyre_t *self);\n    \n    //  Return our node name, after successful initialization. First 6\n    //  characters of UUID by default.\n    ZYRE_EXPORT const char *\n        zyre_name (zyre_t *self);\n    \n    //  Set the public name of this node overriding the default. The name is\n    //  provide during discovery and come in each ENTER message.\n    ZYRE_EXPORT void\n        zyre_set_name (zyre_t *self, const char *name);\n    \n    //  Set node header; these are provided to other nodes during discovery\n    //  and come in each ENTER message.\n    ZYRE_EXPORT void\n        zyre_set_header (zyre_t *self, const char *name, const char *format, ...) CHECK_PRINTF (3);\n    \n    //  Set verbose mode; this tells the node to log all traffic as well as\n    //  all major events.\n    ZYRE_EXPORT void\n        zyre_set_verbose (zyre_t *self);\n    \n    //  Set UDP beacon discovery port; defaults to 5670, this call overrides\n    //  that so you can create independent clusters on the same network, for\n    //  e.g. development vs. production. Has no effect after zyre_start().\n    ZYRE_EXPORT void\n        zyre_set_port (zyre_t *self, int port_nbr);\n    \n    //  Set the peer evasiveness timeout, in milliseconds. Default is 5000.\n    //  This can be tuned in order to deal with expected network conditions\n    //  and the response time expected by the application. This is tied to\n    //  the beacon interval and rate of messages received.\n    ZYRE_EXPORT void\n        zyre_set_evasive_timeout (zyre_t *self, int interval);\n    \n    //  Set the peer silence timeout, in milliseconds. Default is 5000.\n    //  This can be tuned in order to deal with expected network conditions\n    //  and the response time expected by the application. This is tied to\n    //  the beacon interval and rate of messages received.\n    //  Silence is triggered one second after the timeout if peer has not\n    //  answered ping and has not sent any message.\n    //  NB: this is currently redundant with the evasiveness timeout. Both\n    //  affect the same timeout value.\n    ZYRE_EXPORT void\n        zyre_set_silent_timeout (zyre_t *self, int interval);\n    \n    //  Set the peer expiration timeout, in milliseconds. Default is 30000.\n    //  This can be tuned in order to deal with expected network conditions\n    //  and the response time expected by the application. This is tied to\n    //  the beacon interval and rate of messages received.\n    ZYRE_EXPORT void\n        zyre_set_expired_timeout (zyre_t *self, int interval);\n    \n    //  Set UDP beacon discovery interval, in milliseconds. Default is instant\n    //  beacon exploration followed by pinging every 1,000 msecs.\n    ZYRE_EXPORT void\n        zyre_set_interval (zyre_t *self, size_t interval);\n    \n    //  Set network interface for UDP beacons. If you do not set this, CZMQ will\n    //  choose an interface for you. On boxes with several interfaces you should\n    //  specify which one you want to use, or strange things can happen.\n    ZYRE_EXPORT void\n        zyre_set_interface (zyre_t *self, const char *value);\n    \n    //  By default, Zyre binds to an ephemeral TCP port and broadcasts the local\n    //  host name using UDP beaconing. When you call this method, Zyre will use\n    //  gossip discovery instead of UDP beaconing. You MUST set-up the gossip\n    //  service separately using zyre_gossip_bind() and _connect(). Note that the\n    //  endpoint MUST be valid for both bind and connect operations. You can use\n    //  inproc://, ipc://, or tcp:// transports (for tcp://, use an IP address\n    //  that is meaningful to remote as well as local nodes). Returns 0 if\n    //  the bind was successful, else -1.\n    ZYRE_EXPORT int\n        zyre_set_endpoint (zyre_t *self, const char *format, ...) CHECK_PRINTF (2);\n    \n    //  Set-up gossip discovery of other nodes. At least one node in the cluster\n    //  must bind to a well-known gossip endpoint, so other nodes can connect to\n    //  it. Note that gossip endpoints are completely distinct from Zyre node\n    //  endpoints, and should not overlap (they can use the same transport).\n    ZYRE_EXPORT void\n        zyre_gossip_bind (zyre_t *self, const char *format, ...) CHECK_PRINTF (2);\n    \n    //  Set-up gossip discovery of other nodes. A node may connect to multiple\n    //  other nodes, for redundancy paths. For details of the gossip network\n    //  design, see the CZMQ zgossip class.\n    ZYRE_EXPORT void\n        zyre_gossip_connect (zyre_t *self, const char *format, ...) CHECK_PRINTF (2);\n    \n    //  Start node, after setting header values. When you start a node it\n    //  begins discovery and connection. Returns 0 if OK, -1 if it wasn't\n    //  possible to start the node.\n    ZYRE_EXPORT int\n        zyre_start (zyre_t *self);\n    \n    //  Stop node; this signals to other peers that this node will go away.\n    //  This is polite; however you can also just destroy the node without\n    //  stopping it.\n    ZYRE_EXPORT void\n        zyre_stop (zyre_t *self);\n    \n    //  Join a named group; after joining a group you can send messages to\n    //  the group and all Zyre nodes in that group will receive them.\n    ZYRE_EXPORT int\n        zyre_join (zyre_t *self, const char *group);\n    \n    //  Leave a group\n    ZYRE_EXPORT int\n        zyre_leave (zyre_t *self, const char *group);\n    \n    //  Receive next message from network; the message may be a control\n    //  message (ENTER, EXIT, JOIN, LEAVE) or data (WHISPER, SHOUT).\n    //  Returns zmsg_t object, or NULL if interrupted\n    //  Caller owns return value and must destroy it when done.\n    ZYRE_EXPORT zmsg_t *\n        zyre_recv (zyre_t *self);\n    \n    //  Send message to single peer, specified as a UUID string\n    //  Destroys message after sending\n    ZYRE_EXPORT int\n        zyre_whisper (zyre_t *self, const char *peer, zmsg_t **msg_p);\n    \n    //  Send message to a named group\n    //  Destroys message after sending\n    ZYRE_EXPORT int\n        zyre_shout (zyre_t *self, const char *group, zmsg_t **msg_p);\n    \n    //  Send formatted string to a single peer specified as UUID string\n    ZYRE_EXPORT int\n        zyre_whispers (zyre_t *self, const char *peer, const char *format, ...) CHECK_PRINTF (3);\n    \n    //  Send formatted string to a named group\n    ZYRE_EXPORT int\n        zyre_shouts (zyre_t *self, const char *group, const char *format, ...) CHECK_PRINTF (3);\n    \n    //  Return zlist of current peer ids.\n    //  Caller owns return value and must destroy it when done.\n    ZYRE_EXPORT zlist_t *\n        zyre_peers (zyre_t *self);\n    \n    //  Return zlist of current peers of this group.\n    //  Caller owns return value and must destroy it when done.\n    ZYRE_EXPORT zlist_t *\n        zyre_peers_by_group (zyre_t *self, const char *name);\n    \n    //  Return zlist of currently joined groups.\n    //  Caller owns return value and must destroy it when done.\n    ZYRE_EXPORT zlist_t *\n        zyre_own_groups (zyre_t *self);\n    \n    //  Return zlist of groups known through connected peers.\n    //  Caller owns return value and must destroy it when done.\n    ZYRE_EXPORT zlist_t *\n        zyre_peer_groups (zyre_t *self);\n    \n    //  Return the endpoint of a connected peer.\n    //  Returns empty string if peer does not exist.\n    //  Caller owns return value and must destroy it when done.\n    ZYRE_EXPORT char *\n        zyre_peer_address (zyre_t *self, const char *peer);\n    \n    //  Return the value of a header of a conected peer.\n    //  Returns null if peer or key doesn't exits.\n    //  Caller owns return value and must destroy it when done.\n    ZYRE_EXPORT char *\n        zyre_peer_header_value (zyre_t *self, const char *peer, const char *name);\n    \n    //  Return socket for talking to the Zyre node, for polling\n    ZYRE_EXPORT zsock_t *\n        zyre_socket (zyre_t *self);\n    \n    //  Print zyre node information to stdout\n    ZYRE_EXPORT void\n        zyre_print (zyre_t *self);\n    \n    //  Return the Zyre version for run-time API detection; returns\n    //  major * 10000 + minor * 100 + patch, as a single integer.\n    ZYRE_EXPORT uint64_t\n        zyre_version (void);\n    \n    //  Self test of this class.\n    ZYRE_EXPORT void\n        zyre_test (bool verbose);\n    \n    #ifdef ZYRE_BUILD_DRAFT_API\n    //  *** Draft method, for development use, may change without warning ***\n    //  Set the TCP port bound by the ROUTER peer-to-peer socket (beacon mode).\n    //  Defaults to * (the port is randomly assigned by the system).\n    //  This call overrides this, to bypass some firewall issues when ports are\n    //  random. Has no effect after zyre_start().\n    ZYRE_EXPORT void\n        zyre_set_beacon_peer_port (zyre_t *self, int port_nbr);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  This options enables a peer to actively contest for leadership in the\n    //  given group. If this option is not set the peer will still participate in\n    //  elections but never gets elected. This ensures that a consent for a leader\n    //  is reached within a group even though not every peer is contesting for\n    //  leadership.\n    ZYRE_EXPORT void\n        zyre_set_contest_in_group (zyre_t *self, const char *group);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Set an alternative endpoint value when using GOSSIP ONLY. This is useful\n    //  if you're advertising an endpoint behind a NAT.\n    ZYRE_EXPORT void\n        zyre_set_advertised_endpoint (zyre_t *self, const char *value);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Apply a azcert to a Zyre node.\n    ZYRE_EXPORT void\n        zyre_set_zcert (zyre_t *self, zcert_t *zcert);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Specify the ZAP domain (for use with CURVE).\n    ZYRE_EXPORT void\n        zyre_set_zap_domain (zyre_t *self, const char *domain);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Set-up gossip discovery with CURVE enabled.\n    ZYRE_EXPORT void\n        zyre_gossip_connect_curve (zyre_t *self, const char *public_key, const char *format, ...) CHECK_PRINTF (3);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Unpublish a GOSSIP node from local list, useful in removing nodes from list when they EXIT\n    ZYRE_EXPORT void\n        zyre_gossip_unpublish (zyre_t *self, const char *node);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Explicitly connect to a peer\n    ZYRE_EXPORT int\n        zyre_require_peer (zyre_t *self, const char *uuid, const char *endpoint, const char *public_key);\n    \n    #endif // ZYRE_BUILD_DRAFT_API\n```\nPlease add '@interface' section in './../src/zyre.c'.\n\nThis is the class self test code:\n\n```c\n    //  We'll use inproc gossip discovery so that this works without networking\n    \n    uint64_t version = zyre_version ();\n    assert ((version / 10000) % 100 == ZYRE_VERSION_MAJOR);\n    assert ((version / 100) % 100 == ZYRE_VERSION_MINOR);\n    assert (version % 100 == ZYRE_VERSION_PATCH);\n    \n    //  Create two nodes\n    zyre_t *node1 = zyre_new (\"node1\");\n    assert (node1);\n    assert (streq (zyre_name (node1), \"node1\"));\n    zyre_set_header (node1, \"X-HELLO\", \"World\");\n    if (verbose)\n        zyre_set_verbose (node1);\n    \n    //  Set inproc endpoint for this node\n    int rc = zyre_set_endpoint (node1, \"inproc://zyre-node1\");\n    assert (rc == 0);\n    //  Set up gossip network for this node\n    zyre_gossip_bind (node1, \"inproc://gossip-hub\");\n    rc = zyre_start (node1);\n    assert (rc == 0);\n    \n    zyre_t *node2 = zyre_new (\"node2\");\n    assert (node2);\n    assert (streq (zyre_name (node2), \"node2\"));\n    if (verbose)\n        zyre_set_verbose (node2);\n    \n    //  Set inproc endpoint for this node\n    //  First, try to use existing name, it'll fail\n    rc = zyre_set_endpoint (node2, \"inproc://zyre-node1\");\n    assert (rc == -1);\n    //  Now use available name and confirm that it succeeds\n    rc = zyre_set_endpoint (node2, \"inproc://zyre-node2\");\n    assert (rc == 0);\n    \n    //  Set up gossip network for this node\n    zyre_gossip_connect (node2, \"inproc://gossip-hub\");\n    rc = zyre_start (node2);\n    assert (rc == 0);\n    assert (strneq (zyre_uuid (node1), zyre_uuid (node2)));\n    \n    zyre_join (node1, \"GLOBAL\");\n    zyre_join (node2, \"GLOBAL\");\n    \n    //  Give time for them to interconnect\n    zclock_sleep (250);\n    if (verbose)\n        zyre_dump (node1);\n    \n    zlist_t *peers = zyre_peers (node1);\n    assert (peers);\n    assert (zlist_size (peers) == 1);\n    zlist_destroy (\u0026peers);\n    \n    zyre_join (node1, \"node1 group of one\");\n    zyre_join (node2, \"node2 group of one\");\n    \n    // Give them time to join their groups\n    zclock_sleep (250);\n    \n    zlist_t *own_groups = zyre_own_groups (node1);\n    assert (own_groups);\n    assert (zlist_size (own_groups) == 2);\n    zlist_destroy (\u0026own_groups);\n    \n    zlist_t *peer_groups = zyre_peer_groups (node1);\n    assert (peer_groups);\n    assert (zlist_size (peer_groups) == 2);\n    zlist_destroy (\u0026peer_groups);\n    \n    char *value = zyre_peer_header_value (node2, zyre_uuid (node1), \"X-HELLO\");\n    assert (streq (value, \"World\"));\n    zstr_free (\u0026value);\n    \n    //  One node shouts to GLOBAL\n    zyre_shouts (node1, \"GLOBAL\", \"Hello, World\");\n    \n    //  Second node should receive ENTER, JOIN, and SHOUT\n    zmsg_t *msg = zyre_recv (node2);\n    assert (msg);\n    char *command = zmsg_popstr (msg);\n    assert (streq (command, \"ENTER\"));\n    zstr_free (\u0026command);\n    assert (zmsg_size (msg) == 4);\n    char *peerid = zmsg_popstr (msg);\n    char *name = zmsg_popstr (msg);\n    assert (streq (name, \"node1\"));\n    zstr_free (\u0026name);\n    zframe_t *headers_packed = zmsg_pop (msg);\n    \n    char *address = zmsg_popstr (msg);\n    char *endpoint = zyre_peer_address (node2, peerid);\n    assert (streq (address, endpoint));\n    zstr_free (\u0026peerid);\n    zstr_free (\u0026endpoint);\n    zstr_free (\u0026address);\n    \n    assert (headers_packed);\n    zhash_t *headers = zhash_unpack (headers_packed);\n    assert (headers);\n    zframe_destroy (\u0026headers_packed);\n    assert (streq ((char *) zhash_lookup (headers, \"X-HELLO\"), \"World\"));\n    zhash_destroy (\u0026headers);\n    zmsg_destroy (\u0026msg);\n    \n    msg = zyre_recv (node2);\n    assert (msg);\n    command = zmsg_popstr (msg);\n    assert (streq (command, \"JOIN\"));\n    zstr_free (\u0026command);\n    assert (zmsg_size (msg) == 3);\n    zmsg_destroy (\u0026msg);\n    \n    msg = zyre_recv (node2);\n    assert (msg);\n    command = zmsg_popstr (msg);\n    assert (streq (command, \"JOIN\"));\n    zstr_free (\u0026command);\n    assert (zmsg_size (msg) == 3);\n    zmsg_destroy (\u0026msg);\n    \n    msg = zyre_recv (node2);\n    assert (msg);\n    command = zmsg_popstr (msg);\n    assert (streq (command, \"SHOUT\"));\n    zstr_free (\u0026command);\n    zmsg_destroy (\u0026msg);\n    \n    zyre_stop (node2);\n    \n    msg = zyre_recv (node2);\n    assert (msg);\n    command = zmsg_popstr (msg);\n    assert (streq (command, \"STOP\"));\n    zstr_free (\u0026command);\n    zmsg_destroy (\u0026msg);\n    \n    zyre_stop (node1);\n    \n    zyre_destroy (\u0026node1);\n    zyre_destroy (\u0026node2);\n    \n    printf (\"OK\\n\");\n    \n    #ifdef ZYRE_BUILD_DRAFT_API\n    //  DRAFT-API: Security\n    if (zsys_has_curve()){\n    \n        printf (\" * zyre-curve: \");\n        if (verbose)\n            printf (\"\\n\");\n    \n        if (verbose)\n            zsys_debug(\"----------------TESTING CURVE --------------\");\n    \n        zactor_t *speaker = zactor_new (zbeacon, NULL);\n        assert (speaker);\n        if (verbose)\n            zstr_sendx (speaker, \"VERBOSE\", NULL);\n    \n        // ensuring we have a broadcast address\n        zsock_send (speaker, \"si\", \"CONFIGURE\", 9999);\n        char *hostname = zstr_recv (speaker);\n        if (!*hostname) {\n            printf (\"OK (skipping test, no UDP broadcasting)\\n\");\n            zactor_destroy (\u0026speaker);\n            freen (hostname);\n            return;\n        }\n        freen (hostname);\n        zactor_destroy (\u0026speaker);\n    \n    \n        // zap setup\n        zactor_t *auth = zactor_new(zauth, NULL);\n        assert (auth);\n    \n        if (verbose) {\n            zstr_sendx(auth, \"VERBOSE\", NULL);\n            zsock_wait(auth);\n        }\n    \n        zstr_sendx (auth, \"CURVE\", CURVE_ALLOW_ANY, NULL);\n        zsock_wait (auth);\n    \n        zyre_t *node3 = zyre_new (\"node3\");\n        zyre_t *node4 = zyre_new (\"node4\");\n    \n        assert (node3);\n        assert (node4);\n    \n        if (verbose) {\n            zyre_set_verbose (node3);\n            zyre_set_verbose (node4);\n        }\n    \n        zyre_set_zap_domain(node3, \"TEST\");\n        zyre_set_zap_domain(node4, \"TEST\");\n    \n        zsock_set_rcvtimeo(node3-\u003einbox, 10000);\n        zsock_set_rcvtimeo(node4-\u003einbox, 10000);\n    \n        zcert_t *node3_cert = zcert_new ();\n        zcert_t *node4_cert = zcert_new ();\n    \n        assert (node3_cert);\n        assert (node4_cert);\n    \n        zyre_set_zcert (node3, node3_cert);\n        zyre_set_zcert (node4, node4_cert);\n    \n        zyre_set_header (node3, \"X-PUBLICKEY\", \"%s\", zcert_public_txt (node3_cert));\n        zyre_set_header (node4, \"X-PUBLICKEY\", \"%s\", zcert_public_txt (node4_cert));\n    \n        // test beacon\n        if (verbose)\n            zsys_debug (\"----------------TESTING BEACON----------------\");\n    \n        rc = zyre_start (node3);\n        assert (rc == 0);\n    \n        rc = zyre_start (node4);\n        assert (rc == 0);\n    \n        zyre_join (node3, \"GLOBAL\");\n        zyre_join (node4, \"GLOBAL\");\n    \n        zclock_sleep (1500);\n    \n        if (verbose) {\n            zyre_dump (node3);\n            zyre_dump (node4);\n        }\n    \n        zyre_shouts (node3, \"GLOBAL\", \"Hello, World\");\n    \n        //  Second node should receive ENTER, JOIN, and SHOUT\n        msg = zyre_recv (node4);\n        assert (msg);\n        command = zmsg_popstr (msg);\n        assert (streq (command, \"ENTER\"));\n        zstr_free (\u0026command);\n    \n        char *peerid = zmsg_popstr (msg);\n        assert (peerid);\n        name = zmsg_popstr (msg);\n        assert (streq (name, \"node3\"));\n        zmsg_destroy (\u0026msg);\n    \n        msg = zyre_recv (node4);\n        assert (msg);\n        command = zmsg_popstr (msg);\n        assert (streq (command, \"JOIN\"));\n        zstr_free (\u0026command);\n        zmsg_destroy (\u0026msg);\n    \n        msg = zyre_recv (node4);\n        assert (msg);\n        command = zmsg_popstr (msg);\n        assert (streq (command, \"SHOUT\"));\n        zstr_free (\u0026command);\n        zmsg_destroy (\u0026msg);\n    \n        zyre_leave (node3, \"GLOBAL\");\n        zyre_leave (node4, \"GLOBAL\");\n    \n        zstr_free (\u0026name);\n        zstr_free (\u0026peerid);\n        zstr_free (\u0026command);\n    \n        zyre_stop (node3);\n        zyre_stop (node4);\n    \n        // give things a chance to settle...\n        zclock_sleep (250);\n    \n        zyre_destroy(\u0026node3);\n        zyre_destroy(\u0026node4);\n    \n        zcert_destroy(\u0026node3_cert);\n        zcert_destroy(\u0026node4_cert);\n    \n        // test gossip\n        if (verbose)\n            zsys_debug (\"----------------TESTING GOSSIP----------------\");\n    \n        zyre_t *node5 = zyre_new (\"node5\");\n        zyre_t *node6 = zyre_new (\"node6\");\n    \n        assert (node5);\n        assert (node6);\n    \n        if (verbose) {\n            zyre_set_verbose (node5);\n            zyre_set_verbose (node6);\n        }\n    \n        // if it takes more than 10s, something probably went terribly wrong\n        zsock_set_rcvtimeo(node5-\u003einbox, 10000);\n        zsock_set_rcvtimeo(node6-\u003einbox, 10000);\n    \n        zcert_t *node5_cert = zcert_new ();\n        zcert_t *node6_cert = zcert_new ();\n    \n        assert (node5_cert);\n        assert (node6_cert);\n    \n        zyre_set_zcert(node5, node5_cert);\n        zyre_set_zcert(node6, node6_cert);\n    \n        zyre_set_header(node5, \"X-PUBLICKEY\", \"%s\", zcert_public_txt(node5_cert));\n        zyre_set_header(node6, \"X-PUBLICKEY\", \"%s\", zcert_public_txt(node6_cert));\n    \n        // TODO- set_advertised_endpoint tests\n    //        zyre_set_endpoint(node5, \"tcp://127.0.0.1:9001\");\n    //        zyre_set_advertised_endpoint(node5, \"tcp://localhost:9001\");\n    \n        const char *gossip_cert = zcert_public_txt (node5_cert);\n    \n        // TODO- need to add zyre_gossip_port functions to get port from gossip bind(?)\n        zyre_gossip_bind(node5, \"tcp://127.0.0.1:9001\");\n        zyre_gossip_connect_curve(node6, gossip_cert, \"tcp://127.0.0.1:9001\");\n    \n        rc = zyre_start (node5);\n        assert (rc == 0);\n    \n        rc = zyre_start (node6);\n        assert (rc == 0);\n    \n        zyre_join (node5, \"GLOBAL\");\n        zyre_join (node6, \"GLOBAL\");\n    \n        // give things a chance to settle...\n        zclock_sleep (1500);\n    \n        if (verbose) {\n            zyre_dump (node5);\n            zyre_dump (node6);\n        }\n    \n        zyre_shouts (node5, \"GLOBAL\", \"Hello, World\");\n    \n        //  Second node should receive ENTER, JOIN, and SHOUT\n        msg = zyre_recv (node6);\n        assert (msg);\n        command = zmsg_popstr (msg);\n        assert (streq (command, \"ENTER\"));\n        zstr_free (\u0026command);\n    \n        peerid = zmsg_popstr (msg);\n        assert (peerid);\n        name = zmsg_popstr (msg);\n        zmsg_destroy (\u0026msg);\n    \n        assert (streq (name, \"node5\"));\n        zstr_free (\u0026name);\n    \n        zyre_leave (node5, \"GLOBAL\");\n        zyre_leave (node6, \"GLOBAL\");\n    \n        zyre_stop (node5);\n        zyre_stop (node6);\n    \n        // give things a chance to settle...\n        zclock_sleep (250);\n    \n        zstr_free (\u0026peerid);\n    \n        zcert_destroy (\u0026node5_cert);\n        zcert_destroy (\u0026node6_cert);\n    \n        zyre_destroy (\u0026node5);\n        zyre_destroy (\u0026node6);\n        zactor_destroy (\u0026auth);\n    \n        printf (\"OK\\n\");\n    \n    }\n    #endif\n    }\n```\n\n#### zyre_event - no title found\n\nThis class provides a higher-level API to the zyre_recv call, by doing\nwork that you will want to do in many cases, such as unpacking the peer\nheaders for each ENTER event received.\n\nPlease add '@discuss' section in './../src/zyre_event.c'.\n\nThis is the class interface:\n\n```h\n    //  This is a stable class, and may not change except for emergencies. It\n    //  is provided in stable builds.\n    //  Constructor: receive an event from the zyre node, wraps zyre_recv.\n    //  The event may be a control message (ENTER, EXIT, JOIN, LEAVE) or\n    //  data (WHISPER, SHOUT).\n    ZYRE_EXPORT zyre_event_t *\n        zyre_event_new (zyre_t *node);\n    \n    //  Destructor; destroys an event instance\n    ZYRE_EXPORT void\n        zyre_event_destroy (zyre_event_t **self_p);\n    \n    //  Returns event type, as printable uppercase string. Choices are:\n    //  \"ENTER\", \"EXIT\", \"JOIN\", \"LEAVE\", \"EVASIVE\", \"WHISPER\" and \"SHOUT\"\n    //  and for the local node: \"STOP\"\n    ZYRE_EXPORT const char *\n        zyre_event_type (zyre_event_t *self);\n    \n    //  Return the sending peer's uuid as a string\n    ZYRE_EXPORT const char *\n        zyre_event_peer_uuid (zyre_event_t *self);\n    \n    //  Return the sending peer's public name as a string\n    ZYRE_EXPORT const char *\n        zyre_event_peer_name (zyre_event_t *self);\n    \n    //  Return the sending peer's ipaddress as a string\n    ZYRE_EXPORT const char *\n        zyre_event_peer_addr (zyre_event_t *self);\n    \n    //  Returns the event headers, or NULL if there are none\n    ZYRE_EXPORT zhash_t *\n        zyre_event_headers (zyre_event_t *self);\n    \n    //  Returns value of a header from the message headers\n    //  obtained by ENTER. Return NULL if no value was found.\n    ZYRE_EXPORT const char *\n        zyre_event_header (zyre_event_t *self, const char *name);\n    \n    //  Returns the group name that a SHOUT event was sent to\n    ZYRE_EXPORT const char *\n        zyre_event_group (zyre_event_t *self);\n    \n    //  Returns the incoming message payload; the caller can modify the\n    //  message but does not own it and should not destroy it.\n    ZYRE_EXPORT zmsg_t *\n        zyre_event_msg (zyre_event_t *self);\n    \n    //  Returns the incoming message payload, and pass ownership to the\n    //  caller. The caller must destroy the message when finished with it.\n    //  After called on the given event, further calls will return NULL.\n    //  Caller owns return value and must destroy it when done.\n    ZYRE_EXPORT zmsg_t *\n        zyre_event_get_msg (zyre_event_t *self);\n    \n    //  Print event to zsys log\n    ZYRE_EXPORT void\n        zyre_event_print (zyre_event_t *self);\n    \n    //  Self test of this class.\n    ZYRE_EXPORT void\n        zyre_event_test (bool verbose);\n    \n```\nPlease add '@interface' section in './../src/zyre_event.c'.\n\nThis is the class self test code:\n\n```c\n    //  Create two nodes\n    zyre_t *node1 = zyre_new (\"node1\");\n    assert (node1);\n    zyre_set_header (node1, \"X-HELLO\", \"World\");\n    int rc = zyre_set_endpoint (node1, \"inproc://zyre-node1\");\n    assert (rc == 0);\n    // use gossiping instead of beaconing, suits Travis better\n    zyre_gossip_bind (node1, \"inproc://gossip-hub\");\n    if (verbose)\n        zyre_set_verbose (node1);\n    if (zyre_start (node1)) {\n        zyre_destroy (\u0026node1);\n        printf (\"OK (skipping test, no UDP discovery)\\n\");\n        return;\n    }\n    zyre_join (node1, \"GLOBAL\");\n    \n    zyre_t *node2 = zyre_new (\"node2\");\n    assert (node2);\n    if (verbose)\n        zyre_set_verbose (node2);\n    rc = zyre_set_endpoint (node2, \"inproc://zyre-node2\");\n    assert (rc == 0);\n    // use gossiping instead of beaconing, suits Travis better\n    zyre_gossip_connect (node2, \"inproc://gossip-hub\");\n    rc = zyre_start (node2);\n    assert (rc == 0);\n    zyre_join (node2, \"GLOBAL\");\n    \n    //  Give time for them to interconnect\n    zclock_sleep (250);\n    \n    //  One node shouts to GLOBAL\n    zmsg_t *msg = zmsg_new ();\n    zmsg_addstr (msg, \"Hello, World\");\n    zyre_shout (node1, \"GLOBAL\", \u0026msg);\n    zclock_sleep (100);\n    \n    //  Parse ENTER\n    zyre_event_t *event = zyre_event_new (node2);\n    assert (streq (zyre_event_type (event), \"ENTER\"));\n    const char *sender = zyre_event_peer_uuid (event);\n    assert (sender);\n    const char *name = zyre_event_peer_name (event);\n    assert (name);\n    assert (streq (name, \"node1\"));\n    const char *address = zyre_event_peer_addr (event);\n    assert (address);\n    const char *header = zyre_event_header (event, \"X-HELLO\");\n    assert (header);\n    zyre_event_destroy (\u0026event);\n    \n    //  Parse JOIN\n    //  We tolerate other events, which we can get if there are instances\n    //  of Zyre running somewhere on the network.\n    event = zyre_event_new (node2);\n    if (streq (zyre_event_type (event), \"JOIN\")) {\n        //  Parse SHOUT\n        zyre_event_destroy (\u0026event);\n        event = zyre_event_new (node2);\n        if (streq (zyre_event_type (event), \"SHOUT\")) {\n            assert (streq (zyre_event_group (event), \"GLOBAL\"));\n            zmsg_t *msg = zyre_event_get_msg (event);\n            char *string = zmsg_popstr (msg);\n            zmsg_destroy (\u0026msg);\n            assert (streq (string, \"Hello, World\"));\n            free (string);\n        }\n        zyre_event_destroy (\u0026event);\n    }\n    zyre_destroy (\u0026node1);\n    zyre_destroy (\u0026node2);\n```\n\n\n### Hints to Contributors\n\nZyre is a nice, neat library, and you may not immediately appreciate why. Read the CLASS style guide please, and write your code to make it indistinguishable from the rest of the code in the library. That is the only real criteria for good style: it's invisible.\n\nDon't include system headers in source files. The right place for these is CZMQ.\n\nDo read your code after you write it and ask, \"Can I make this simpler?\" We do use a nice minimalist and yet readable style. Learn it, adopt it, use it.\n\nBefore opening a pull request read our [contribution guidelines](https://github.com/zeromq/zyre/blob/master/CONTRIBUTING.md). Thanks!\n\n### This Document\n\n_This documentation was generated from zyre/README.txt using [Gitdown](https://github.com/zeromq/gitdown)_\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzeromq%2Fzyre","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzeromq%2Fzyre","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzeromq%2Fzyre/lists"}