{"id":21913190,"url":"https://github.com/mdamiani/fuurin","last_synced_at":"2025-11-10T10:04:26.608Z","repository":{"id":49333670,"uuid":"96820692","full_name":"mdamiani/fuurin","owner":"mdamiani","description":"Simple and fast ZeroMQ-based communication library.","archived":false,"fork":false,"pushed_at":"2024-04-02T16:35:18.000Z","size":8840,"stargazers_count":12,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-29T05:11:19.849Z","etag":null,"topics":["availability","broker","communication-library","connected-workers","delivery","distributed-systems","grpc","microservice","pubsub","redundancy","replica","snapshot","synchronization","uuid","zeromq","zmq"],"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/mdamiani.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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}},"created_at":"2017-07-10T20:57:48.000Z","updated_at":"2024-07-26T08:15:07.000Z","dependencies_parsed_at":"2024-04-02T17:47:05.696Z","dependency_job_id":"55c6210d-ee22-4144-952c-d7833ac18658","html_url":"https://github.com/mdamiani/fuurin","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdamiani%2Ffuurin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdamiani%2Ffuurin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdamiani%2Ffuurin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdamiani%2Ffuurin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mdamiani","download_url":"https://codeload.github.com/mdamiani/fuurin/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249207245,"owners_count":21230029,"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":["availability","broker","communication-library","connected-workers","delivery","distributed-systems","grpc","microservice","pubsub","redundancy","replica","snapshot","synchronization","uuid","zeromq","zmq"],"created_at":"2024-11-28T18:14:53.390Z","updated_at":"2025-11-10T10:04:21.569Z","avatar_url":"https://github.com/mdamiani.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fuurin\n\n[![GitHub license](https://img.shields.io/github/license/mdamiani/fuurin)](https://github.com/mdamiani/fuurin/blob/master/LICENSE)\n[![GitHub build](https://github.com/mdamiani/fuurin/workflows/build/badge.svg)](https://github.com/mdamiani/fuurin/actions?query=workflow%3ACMake)\n[![codecov](https://codecov.io/gh/mdamiani/fuurin/branch/master/graph/badge.svg?token=hQ0rGhtCcr)](https://codecov.io/gh/mdamiani/fuurin)\n\n\nSimple and fast communication library based on [ZeroMQ](http://zeromq.org).\n\nIt provides few easy to use C++ classes in order to connect different parties together.\nThere are also C++ bindings to some ZMQ sockets and features, which are used by the library\ncomponents themselves. Finally there some system-level software components which\ncan be accessed by [gRPC](https://grpc.io) calls, in order to hide ZMQ details and\nto be able to develop microservices in any language supported by gRPC.\n\n## Table of Contents\n1. [Features](#Features)\n1. [Architecture](#Architecture)\n1. [gRPC](#gRPC)\n1. [Licenses](#Licenses)\n1. [Guidelines](#Guidelines)\n\n## Features\n\n * ZMQ Sockets:\n    - ZMQ C++ bindings with RAII design and exception safe code.\n    - Leverage of ZMQ thread safe sockets, like RADIO/DISH, CLIENT/SERVER.\n    - Multipart messages can be used with ZMQ thread safe sockets.\n    - Polling of ZMQ sockets made easy: can integrate with C++ range based loops, easy open/close sockets, etc..\n    - Multicore friendly, due to the usage of ZMQ and Boost::ASIO.\n    - Logging aware library: levels, customization and performance.\n    - Time elapsed measurement for timeouts.\n    - Endianess management for integer types.\n    - Timers can be polled like they were sockets.\n\n * Network Classes:\n    - Microservices mini framework, based on workers and brokers.\n    - Connection management between [worker](include/fuurin/worker.h) and [broker](include/fuurin/broker.h).\n    - Redesigned and extended [ZMQ Clone Pattern](https://zguide.zeromq.org/docs/chapter5/#Reliable-Pub-Sub-Clone-Pattern).\n    - Late joining management in publish/subscribe patterns.\n    - Retry and keep alive to check for liveness.\n    - Eventually consistent and coherent distributed system, for high availability.\n    - Universally unique identifier support (UUID).\n    - Reliability by means of supported redundant connections.\n\n * gRPC service\n    - [fuurin_broker](grpc/fuurin_broker.cpp) is a software component which implements a fuurin broker.\n    - [fuurin_worker](grpc/fuurin_worker.cpp) is a software component which is both a gRPC server and fuurin worker.\n    - Clients may be written following the service [worker.proto](grpc/worker.proto) specification.\n\n(_TODO: add references to classes where possibile._)\n\n\n## Architecture\n\n### Components\n\nThis library provides two main components which are *worker* and *broker*. The communication\nbetween them is based on ZMQ thread-safe sockets [Radio/Dish](https://rfc.zeromq.org/spec/48/) and\n[Client/Server](https://rfc.zeromq.org/spec/41/), as shown in the following diagram:\n\n![Worker Broker Connections](http://www.plantuml.com/plantuml/png/VP11QyCm38Nl_XMYE_iVZ5BQ74S9ow6KTGUJYCRKaOpZh9In_xuejKctGcuMrlVqtjlqqOGuT0vgxZmJKbJAc_fYpWZRm1SCyF9cpstStGp1jmA0UHLM1JhxXU5s8lXuDutbpnMO7hR5yG7x3rLaVDzo5AZ2CFA9glOBL65xRsBT2ZM-stofV6H-PlS73hFx8ph7rsV_IGfEb9DCgeVaFuFFsCxPyILWJdC7g_qYc5eIBkT91yi_d0IH4dN3Lz9hCQH4MmzUhwMeQNgzVq-pAEWW2h9Gb4fja9gXSxy0)\n\nBasic operations:\n  - **Dispatch**: A worker *dispatches* a message, i.e. a *topic*, to any connected broker.\n    Every message is marked with a *sequence number*, at worker's side.\n    Depending on the underlying ZMQ socket transport, a topic might be dispatched to more than\n    a single broker.\n\n    ZMQ sockets: [Radio/Dish](https://rfc.zeromq.org/spec/48/).\n\n  - **Delivery**: A broker *delivers* data to any connected workers, whenever it receives a *topic*.\n    A broker filters out every message marked with a sequence number that is less or equal to the\n    last seen sequence number, for every single worker. Every worker does such filtering as well.\n    Every received topic is locally stored, at broker side.\n\n    ZMQ sockets: [Radio/Dish](https://rfc.zeromq.org/spec/48/).\n\n  - **Synchronize**: A worker *synchronizes* with lastest broker's *snapshot*.\n    A broker replies with its snapshot, before serving any other dispatched topics.\n    Returned topics are the only ones marked as *state*, instead *event* ones are just delivered.\n\n    ZMQ sockets: [Client/Server](https://rfc.zeromq.org/spec/41/).\n\n\n### Usage\n\nUpon instantiation of worker and broker classes, an unique UUID is assigned in order to\ndistinguish them. Every components will connect/bind to the speicified ZMQ endopoints.\nAfter starting both components, an asynchronous connection is set up. Data will begin to\nflow as soon as the connection gets ready. Auto reconnection to the broker is automatically\nperformed by the worker, in case the former disappears. At any time, a worker can synchronize\nwith its connected broker.\n\n![Usage_Diagram](http://www.plantuml.com/plantuml/png/ZLDDJm8n4BttLpJnB8aSITOWS3J1eCPpqnrWmZAjRPTa_VMsx62eZ8ctTZxUctbzdSTaGkgFdRRkw1q19QMQHh-Mi6uQfOnDBkXbXoNbSnGjUaD9VxXmWF1GnHR1vPY-UyRTFYq7GqgDdVh-yTAWPoEwudj9SH5de3tJuiaak7HTLpFBJ3yHkPuiA8vK92z8Ev5ZJHqIg1P-SuoR3sJtmH5-cOIEYWRo3hbEXD_0PmynBo6Cp6q_st5Sd7zz4E4Ni4FYXWfv0xuRHAJ9HByvZpRcqqG4hIiaZ6MsHmUfaes7bn-ojnPYF4lwxjjS7ekKWeEcjt9mebflXT6RPFVYT2ley0HXa4OPDvFUa3Dy_n_Rz8hiHWh-Eix_xPgSb4svtHPNohwlYXd5GojQU0xKvl-ilW40)\n\nPlease see the [examples](examples) folder for some actual code.\n\n\n### Configurations\n\nSome examples of possible configurations, by putting pieces together.\n\n#### Sharing of State among Services\nEvery service can export and distribute its data to others and a central broker can manage the\naccesses to shared state.\n\n![Conf_Services](http://www.plantuml.com/plantuml/png/XT2noeCm4C3n_PuY-CqEwluXRKU7GWTnFEBLIZMHUBQKqdVlaLYqfT1C_y2Fov5yP7GyzHqCgC_Oa8eEZ4oH-YlQviJR6nfr1oMdDKpkYDeJwuJWg3Qx_Kf-ki9YFRDgpHu0slQ3DMHOff6xj9eIByjaXXLrdRr-SMbmwI-N1PUzEv07ucc8__tg86FYq23obI3x-akPB9akcK5EffPVUm80)\n\n#### Master and Replica\nWhenever a service publishes its data, it can dispatch it to both a master and a replica.\nSwitchover between master and replica, or activation of services, are beyond the scope of\nthis library.\n\n![Conf_Replica](http://www.plantuml.com/plantuml/png/bP3Dgi8m48NtynH3xxgBsmUGeYuhY2vAbqCwrc2QX2IrYFZkfZ-WTgNPdV1nFixaFf0BNQl0ahXGmvZio0Ts2VuLiZc7pOqqtW7Zaph-dqX4vXYCumJ9utgx_tz3bs1Xg9wvweDxmCjuOAkae1-KsPVARA44OLfhDEiG2zbTfPWgovchM2dJ0vIOTZPTZUk6K9jUJp436AUaCPQGiww7upq1)\n\n#### Redundancy for Cabling\nIt's possible to configure multiple paths from worker to broker, using more than just one network.\nMessages will be filtered out by both worker and broker, using sequence numbers.\n\n![Conf_Cables](http://www.plantuml.com/plantuml/png/NT31QeGm40RW-pn5i6SFvW6Aj8LU91Hw48z3d5en9XB75LdstJVHB8Wv3JzVXfyfPqRFosW09jG3TYIoNqQcJBnLVVVFdnjQSGSHNc-P_1_gdJWV2CxYu-ld9A-kSjWcrfpP0q2xSNAMB8Tjv6-zFlRLYJLaZ5j16xUq8bF4g_D3iHDL9FFjSRi8UGXv5b2BF7yFtq0LSOgTNva49UCK8mWbBx9EMP8fWv9i6s_s1000)\n\n### Connection Management\nCommunication between workers and broker relys on a continuous flow of probes, at configurable\nspecific time intervals. Every worker actively checks whether its delivery and dispatch sockets\nare connected to the broker. Conversely, snapshot socket(s) is checked on demand, that is when\na synchronization action is performed.\n\n#### Dispatch \u0026 Delivery\nThe following state machine represents how a worker checks for liveness of the dispatch and delivery\nround trip path. A worker shall send a probe to the dispatch socket, which shall be received later\nthrough the delivery socket. This dispatch and delivery path *might* potentially pass through\nmultiple brokers, despites it requires a peer-to-peer communication among brokers.\n\nThe state machine has three states:\n  - *Halted*\n      - Sockets are disconnected and connection is closed.\n\n  - *Trying*\n      - Announcements are continuously dispatched, at every retry interval.\n      - Sockets are reconnected every timeout interval, in case of timeout.\n      - A transition to *Stable* happens if a reply is delivered.\n\n  - *Stable*\n      - Keepalives are dispatched, at every delivered reply.\n      - A transition back to *Trying* happens in case of timeout.\n\n\n![Conn_State](http://www.plantuml.com/plantuml/png/VO_1IWCn48RlynHpL67t0VOW5Jq8tjhUn4FSZZMGdIpfH2ZYkvjqqeI0UChmpvUF-JSdCK7YuW1UxzvmSFGXmpq-6oTq0D1tmYTxcZqppPTq7ywMZnC-QfJcSHm1TcBU7TMuaJXKvOIUT-BNczk2_xyBzlWf2L1F1lPs8HybCUKu7Bfz-XdoLfDcK6CcjhIwS_wVgWjX0R-rVoAtLAf2dIxv0xEFF1EgH4AMNCEE-0DeNPg_h_DpFQXqRmUz4At6sI-2ElKvrbhgsH0Vuk9-0G00)\n\n**TBD**: Matching between dispached vs delivered probe to be implemented.\n\n#### Synchronization\nThe operation of snapshot download involves some steps. In case just one endpoint is used to connect\nto a broker, then synchronization retries to until it completely succeeds. When multiple endpoints\nare configured, then for every download failure, then next endpoint is selected, until it finally\nsucceeds or the maximum number of retry count is reached. Such endpoints *might* potentially belong\nto different brokers. A detailed state machine chart is shown below:\n\n![Sync_State](http://www.plantuml.com/plantuml/png/RP59Rzim48Nl_1LpJ0g4w78psY10WgAvIhaLFH3as1BAHI17hesY_xrSdB49-qHgtZSpZqzFYLIarLdnyyClU7XuX1_A4XeXshc1JnAURKW8AUZVI9A5pn86J4ZWyK0d5IZ0Texf0lloZgMZr_3wKf2FKho4Fzu6bO41ASwud_qEabVB57AtbEAxcctfxvUFUKYfZglMc98KF8ZD5pduaS9oTz-cP2tEkubk0MLW0TRbbYfoF8J0E_uAW5OgWyCU8sGlQ55tCKZ61jJ1-o9l-7vD5HDGetxrEg93pt5TGJds4RrfkWxEAM_Eq5jKFcqnjdKuxx66cgRGcQ9upCHvcLC7YBMgm-epAe1VM8DbxrdWUrMABG7rAD_iG01VkpgKU0TSxF7klcCiRYbppLo1tcQ7OQKEuvLeAlCSt6AHq5HAmEaNzfuBM7eoqvxL07vXfxVXlAVJ13HxNJTiMcZmzHlG535dRSqLYOQvnmlidtX2RrPd_mC0)\n\n**TDB**: Handling of multiple snapshot endpoints to be implemented.\n\n\n## gRPC\n\nThe [gRPC](https://grpc.io) interface in defined is the [worker.proto](grpc/worker.proto)\nspecification file, which requires [protobuf](https://developers.google.com/protocol-buffers)\nat least version\n[3.15.8](https://github.com/protocolbuffers/protobuf/releases/tag/v3.15.8)\nand gRPC libraries at least version\n[1.37.1](https://github.com/grpc/grpc/releases/tag/v1.37.1).\nSuch libraries are not provided by this project, but they are dynamically linked at system level.\n\nThe gRPC service mainly provides the following calls, which all are thread-safe:\n\n  - *SetEndpoints*: Changes the fuurin worker endpoints.\n  - *SetSubscriptions*: Registers the topic names to sync with fuurin broker.\n  - *WaitForEvent*: Waits for any incoming events. In case of multiple clients, events are multiplexed,\n    that is they are all received by every client.\n  - *Start*: Starts the connection with broker. Following subsequent events are expected:\n    - *Started*: upon worker start.\n    - *Online*: upon connection with broker.\n  - *Stop*: Stops the connection with broker. Following subsequent events are expected:\n    - *Offline*: upon disconnection with broker.\n    - *Stopped*: upon worker stop.\n  - *Dispatch*: Sends a topic to the broker. Topic data is always treated as generic bytes. Following subsequent events are expected:\n    - *Delivery*: for every subscribed topic.\n  - *Sync*: Syncs with the broker. Following subsequent events are expected:\n    - *SyncDownloadOn*: upon sync start.\n    - *SyncRequest*: when sync request was sent to the broker.\n    - *SyncBegin*: when sync reply from broker was received.\n    - *SyncElement*: for every subscribed topic.\n    - *SyncElement*\n    - ....\n    - *SyncSuccess*: in case of sync success.\n    - *SyncError*: in case of sync error.\n    - *SyncDownloadOff*: upon sync stop.\n\n\n\n\n## Licenses\n\n### MPL 2.0\n\nThe *fuurin* library is released under the terms of\n[Mozilla Public License 2.0](https://www.mozilla.org/en-US/MPL/2.0/).\nPlease see the attached LICENSE file for further details.\n\n### LGPL v3 plus a static linking exception\n\nThe [ZeroMQ](http://zeromq.org) library is released under\n[GNU Lesser General Public License v3](http://www.gnu.org/licenses/lgpl.html),\nplus a static linking exception.\nFurther details can be found at ZeroMQ [licensing](http://zeromq.org/area:licensing) page.\n\n### Boost Software License 1.0\n\nThe [Boost Test Framework](https://github.com/boostorg/test), which is part of the\n[Boost Library](http://www.boost.org), is distributed under the\n[Boost Software License 1.0](http://www.boost.org/LICENSE_1_0.txt).\nSee the accompanying file [LICENSE_1_0.txt](vendor/boost/LICENSE_1_0.txt).\n\n### Apache License 2.0\n\nThe [Google Benchmark](https://github.com/google/benchmark) library is distributed under\n[Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0).\nSee the accompanying file [LICENSE](vendor/google/benchmark/LICENSE).\n\n\n## How to build the library\n\n[CMake](ttps://cmake.org) is used as project management tool to\nboth build and test this library.\n\n```\n$\u003e mkdir build\n$\u003e cd build\n$\u003e cmake -D BUILD_SHARED=1 -D BUILD_STATIC=1 /path/to/fuurin/folder\n$\u003e make\n$\u003e make install\n```\n\nLibrary is installed on Unix systems to the default path `/usr/local`,\nthough a different PREFIX can be specified.\nFor example library can be installed to the path `/home/user/usr/local`\nby setting the `DESTDIR` variable:\n\n```\n$\u003e make DESTDIR=/home/user install\n```\n\n### How to make a debian package\n\nDebian packages are generated through the following command:\n\n```\n$\u003e cmake -D BUILD_DEB_PACKAGE=ON /path/to/fuurin/folder\n$\u003e make\n$\u003e (umask 022 \u0026\u0026 fakeroot make package)\n```\n\nTwo packages are generated:\n\n * runtime: `libfuurin`\n * development: `libfuurin-dev`\n\nDevelopment package depends on runtime, so the latter one must be installed first\nin order install the library headers.\n\n\n### How to run tests\n\nIn order to run tests, they must be enabled first:\n\n\n```\n$\u003e cmake -D BUILD_TESTS=1 /path/to/fuurin/folder\n$\u003e make\n$\u003e ctest -v\n```\n\n\n### How to build examples\n\nIn order to run examples, they must be enabled first:\n\n\n```\n$\u003e cmake -D BUILD_EXAMPLES=1 /path/to/fuurin/folder\n$\u003e make\n```\n\n\n### How to enable sanitizers\n\nSanitizers can enabled with some cmake options:\n\n  - Address Sanitizer: `cmake -D ENABLE_ASAN=ON`\n  - Thread Sanitezer: `cmake -D ENABLE_TSAN=ON`\n  - Memory Sanitezer: `cmake -D ENABLE_MSAN=ON`\n  - Undefined Behavior Sanitizer: `cmake -D ENABLE_UBSAN=ON`\n\n\n### How to check coverage\nCoverage can be obtained by specifying CMake option `ENABLE_COVERAGE`.\n\n```\n$\u003e cmake -D BUILD_TESTS=1 -D ENABLE_COVERAGE=1 /path/to/fuurin/folder\n$\u003e make\n$\u003e ctest -v\n$\u003e lcov --directory . --capture --output-file coverage.info\n$\u003e genhtml coverage.info --output-directory coverage\n```\n\n\n### How to build Doxygen documentation\n\n```\n$\u003e cmake -D ENABLE_DOXYGEN=1 /path/to/fuurin/folder\n$\u003e make doxygen\n```\n\n\n### How to build gRPC service\n\n```\n$\u003e cmake -D ENABLE_SERVICE_GRPC=1 /path/to/fuurin/folder\n$\u003e make\n```\n\n\n### How to compile with clang\n\n```\n$\u003e cmake -D CMAKE_C_COMPILER=/usr/bin/clang -D CMAKE_CXX_COMPILER=/usr/bin/clang++ /path/to/fuurin/folder\n$\u003e make\n```\n\n\n### Vendoring\n\nExternal libraries used by *fuurin* are integrated through a git subtree approach.\nFor example, Boost unit test framework may be added in this manual way:\n\n - Create an orphan branch where to store the external library\n\n```\n$\u003e cd /path/to/fuurin/folder\n$\u003e git checkout --orphan vendor/boost\n$\u003e git reset\n$\u003e git ls-files -o | xargs rm\n```\n\n - Extract a subset of Boost\n\n```\n$\u003e cd /tmp\n$\u003e tar xf boost_1_65_1.tar.bz2\n$\u003e cd boost_1_65_1\n$\u003e ./bootstrap.sh\n$\u003e ./b2 tools/bcp\n$\u003e ./bin.v2/tools/bcp/.../bcp \\\n    LICENSE_1_0.txt \\\n    boost/test/unit_test.hpp \\\n    boost/test/included/unit_test.hpp \\\n    boost/test/data/test_case.hpp \\\n    libs/test/test/test-organization-ts/datasets-test/* \\\n    libs/test/example/* \\\n    boost/mpl/list.hpp \\\n    boost/scope_exit.hpp \\\n    boost/typeof/incr_registration_group.hpp \\\n    boost/uuid/uuid.hpp \\\n    boost/uuid/uuid_generators.hpp \\\n    boost/uuid/uuid_io.hpp \\\n    boost/uuid/uuid_hash.hpp \\\n    boost/asio.hpp \\\n    /path/to/fuurin/folder\n```\n\n - Add Boost files to the orphan branch\n\n```\n$\u003e cd /path/to/fuurin/folder\n$\u003e git add boost/ libs/\n$\u003e git ci -m\"boost: import test framework version 1.65.1\"\n```\n\n - Checkout the Boost vendor branch into a subfolder of master branch\n\n```\n$\u003e git checkout master\n$\u003e git merge --allow-unrelated-histories -s ours --no-commit vendor/boost\n$\u003e git read-tree -u --prefix=vendor/boost vendor/boost\n$\u003e git commit\n```\n\n - Update the Boost library and merge back into the master branch\n\n```\n$\u003e ## folder must be free of untracked files\n$\u003e git checkout vendor/boost --\n$\u003e ## optional clear all tracked files\n$\u003e git ls-files | xargs git rm\n$\u003e ## update library by adding new untracked files\n$\u003e git ls-files -o | xargs git add\n$\u003e git ci -m\"boost: library updated\"\n$\u003e git checkout master\n$\u003e git merge -s subtree -X subtree=vendor/boost vendor/boost\n```\n\n\n## Guidelines\n\n* Contribution: *TBD*\n* Writing tests: *TBD*\n* Code review: *TDB*\n* Pull request: *TDB*\n* Coding standard: *TDB*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdamiani%2Ffuurin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmdamiani%2Ffuurin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdamiani%2Ffuurin/lists"}