<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Diogen Vagrant</title>
    <description>The latest articles on DEV Community by Diogen Vagrant (@ortexx).</description>
    <link>https://dev.to/ortexx</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F379855%2F6a5225e3-b1be-4a8b-a719-d4e34b99d719.jpg</url>
      <title>DEV Community: Diogen Vagrant</title>
      <link>https://dev.to/ortexx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ortexx"/>
    <language>en</language>
    <item>
      <title>Metastocle - a decentralized data storage</title>
      <dc:creator>Diogen Vagrant</dc:creator>
      <pubDate>Sat, 13 Jun 2020 12:10:14 +0000</pubDate>
      <link>https://dev.to/ortexx/metastocle-a-decentralized-data-storage-1i17</link>
      <guid>https://dev.to/ortexx/metastocle-a-decentralized-data-storage-1i17</guid>
      <description>&lt;p&gt;What if you need to store a variety of data decentralized? Objects, arrays, dates, numbers, strings, yes anything. Is it necessary to develop a powerful DBMS for this? Indeed, often we just need to store and receive data in a distributed, openly, but as simple as possible and without any special claims.&lt;/p&gt;

&lt;p&gt;In this article, I would like to reveal a little bit about the &lt;a href="https://github.com/ortexx/metastocle" rel="noopener noreferrer"&gt;metastocle&lt;/a&gt; library, which can be used to solve the above problem easily, but with some limitations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A bit of background&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;About a year ago, there was a desire and need to create a music storage. See &lt;a href="https://dev.to/ortexx/museria-a-decentralized-music-storage-cg0"&gt;this article&lt;/a&gt; for more details. From the very beginning, it was clear that you need to write everything so that you can do the same with other entities in the future: books, videos, etc. it was decided to divide everything into layers that can be used independently.&lt;/p&gt;

&lt;p&gt;Metastocle is one of the layers that allows you to store and retrieve many types of data (but not files), as opposed to &lt;a href="https://dev.to/ortexx/storacle-a-decentralized-file-storage-46ok"&gt;the storacle&lt;/a&gt; layer, which implements working with files.&lt;/p&gt;

&lt;p&gt;When we save files, we need to write the hashes somewhere so that we can access them later. This is exactly why we need metastocle. It's where we keep everything we need: the names of the songs, links to files etc.&lt;/p&gt;

&lt;p&gt;As a result, all this was brought to a certain universal form, and the system consists of three main entities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Collections - an entity for defining the data structure, various options, and so on.&lt;/li&gt;
&lt;li&gt;  Documents - data itself, as objects.&lt;/li&gt;
&lt;li&gt;  Actions(Instructions) - a set of rules for processing the required data: filtering, sorting, limiting, and so on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at a couple of examples:&lt;/p&gt;

&lt;p&gt;Server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;metastocle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="c1"&gt;// Creating a collection&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;metastocle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost:4000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Adding a document&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Updating this document&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bye&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Adding another document&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Getting the second document&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Getting it differently&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDocumentById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; 

    &lt;span class="c1"&gt;// Adding more documents&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Getting the documents that meet all the conditions&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$gt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;desc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Deleting documents with id &amp;gt; 15&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$gt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Customers can not create a collection. The network sets the structure itself, and users only work with documents. Collections can also be described declaratively, via node options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Main collection parameters:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;pk&lt;/strong&gt; - a primary key field. You can omit this if it is not required. If this field is specified, a uuid hash is created by default. But you can pass any integer or string.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;limit&lt;/strong&gt; - maximum number of documents per node&lt;/li&gt;
&lt;li&gt;  queue - queue mode: if enabled, when the limit is reached, certain documents are deleted to record new ones&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;limitationOrder&lt;/strong&gt; - if the limit and queue are enabled, then you can specify sorting rules to determine which documents to delete. By default, those that have not been used for a long time are deleted.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;schema&lt;/strong&gt; - document field structure&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;defaults&lt;/strong&gt; - default values for document fields&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;hooks&lt;/strong&gt; - document field hooks&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;preferredDuplicates&lt;/strong&gt; - you can specify the preferred number of duplicate documents in the network&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The structure of the collection fields (schema) can be described as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;goods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;isAble&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;boolean’
        }
      }
    }
  }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All rules can be found in the function &lt;strong&gt;utils.validateSchema()&lt;/strong&gt; in &lt;a href="https://github.com/ortexx/spreadable/blob/master/src/utils.js" rel="noopener noreferrer"&gt;https://github.com/ortexx/spreadable/blob/master/src/utils.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Default values and hooks can be like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;
    &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nested.prop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prevDoc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;prevDoc&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;prevDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;priority&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Main features of the library:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Working on the CRUD principle&lt;/li&gt;
&lt;li&gt;  Storing all Javascript data types that can be serialized, including nested ones.&lt;/li&gt;
&lt;li&gt;  Data can be added to storage through any node.&lt;/li&gt;
&lt;li&gt;  Data can be duplicated for greater reliability.&lt;/li&gt;
&lt;li&gt;  Queries may contain nested filters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Isomorphism&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The client is written in javascript and is isomorphic, it can be used directly from your browser.&lt;br&gt;
You can upload a file &lt;a href="https://github.com/ortexx/metastocle/blob/master/dist/metastocle.client.js" rel="noopener noreferrer"&gt;https://github.com/ortexx/metastocle/blob/master/dist/metastocle.client.js&lt;/a&gt; as a script and get access to window.ClientMetastocle or import via the build system etc&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client Api&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  async &lt;strong&gt;Client.prototype.addDocument()&lt;/strong&gt; - adding a document to the collection&lt;/li&gt;
&lt;li&gt;  async &lt;strong&gt;Client.prototype.getDocuments()&lt;/strong&gt; - getting documents from the collection according some instructions&lt;/li&gt;
&lt;li&gt;  async &lt;strong&gt;Client.prototype.getDocumentsСount()&lt;/strong&gt; - getting the number of documents in the collection&lt;/li&gt;
&lt;li&gt;  async &lt;strong&gt;Client.prototype.getDocumentByPk()&lt;/strong&gt; - getting a document from a collection using the primary key&lt;/li&gt;
&lt;li&gt;  async &lt;strong&gt;Client.prototype.updateDocuments()&lt;/strong&gt; - updating documents in the collection according to some instructions&lt;/li&gt;
&lt;li&gt;  async &lt;strong&gt;Client.prototype.deleteDocuments()&lt;/strong&gt; - deleting documents from the collection according to some instructions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Basic actions (instructions)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;.filter - data filtering, example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$lt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$gt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b.c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;.sort&lt;/strong&gt; - data sorting, example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;asc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;y.z&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;desc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;.limit&lt;/strong&gt; - amount of data&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;.offset&lt;/strong&gt; - starting position for data selection&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;.fields&lt;/strong&gt; - the required fields&lt;/p&gt;

&lt;p&gt;All instructions and possible values are described in more detail in the &lt;a href="https://github.com/ortexx/metastocle#actions" rel="noopener noreferrer"&gt;readme&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using the command line&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The library can be used via the command line. To do this you need to install it globally: &lt;strong&gt;npm i -g metastocle --unsafe-perm=true --allow-root&lt;/strong&gt;. After that, you can run the necessary actions from the project directory.&lt;/p&gt;

&lt;p&gt;For example, &lt;strong&gt;metastocle -a getDocumentByPk -o test -p 1 -c ./config.js&lt;/strong&gt;, to get a document with the primary key 1 from the collection "test". All actions can be found in &lt;a href="https://github.com/ortexx/storacle/blob/master/bin/actions.js" rel="noopener noreferrer"&gt;https://github.com/ortexx/metastocle/blob/master/bin/actions.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  All data is first stored in memory, and later written to a file, at certain intervals, and when exiting the process. Therefore, first, you need to have enough RAM, and second, keep in mind that you will not be able to run multiple processes to work with the same database.&lt;/li&gt;
&lt;li&gt;  Sharding at the level of the entire network has not been implemented very effectively yet. Priority is given to duplication, because the size of the network is unstable: nodes can be disconnected, connected, and so on at any time. So if you want to get a large amount of data from the network, keep in mind that all this will be collected via the HTTP protocol, without much optimization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I came to the choice of the stack and these restrictions deliberately, because there was no goal and possibility to create a full-fledged DBMS.&lt;/p&gt;

&lt;p&gt;Although the library is still a bit crude in terms of optimizing data requests, but if you follow certain rules, everything is fine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  You need to narrow the selection of data as much as possible, and try to organize everything so that you get documents by keys, or by some other fields, but filtered to the optimal size.&lt;/li&gt;
&lt;li&gt;  If you still need to pull a lot of data, you will have to limit each server, based on their optimal size, to transfer them over the network. For example, if 10,000 documents in a collection weigh 100 KB in compressed form, then by limiting the collection at each node to this value, we will get everything at an acceptable speed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For any questions, please contact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://t.me/ortex" rel="noopener noreferrer"&gt;@ortex&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="mailto:mywebstreet@gmail.com"&gt;mywebstreet@gmail.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>showdev</category>
      <category>javascript</category>
      <category>node</category>
      <category>database</category>
    </item>
    <item>
      <title>Storacle - a decentralized file storage</title>
      <dc:creator>Diogen Vagrant</dc:creator>
      <pubDate>Sat, 09 May 2020 11:05:30 +0000</pubDate>
      <link>https://dev.to/ortexx/storacle-a-decentralized-file-storage-46ok</link>
      <guid>https://dev.to/ortexx/storacle-a-decentralized-file-storage-46ok</guid>
      <description>&lt;p&gt;Before the start, I'd like to leave &lt;a href="https://dev.to/ortexx/museria-a-decentralized-music-storage-cg0"&gt;a link to the previous article,&lt;/a&gt; to clarify what exactly we are talking about.&lt;/p&gt;

&lt;p&gt;In this article, I want to introduce the layer that is responsible for storing files and how it can be used by anyone. &lt;a href="https://github.com/ortexx/storacle/" rel="noopener noreferrer"&gt;Storace is an independent&lt;/a&gt; library. You can organize storage of any files.&lt;/p&gt;

&lt;p&gt;In my previous article I was too hard on ipfs, but it is due to the context of my task. &lt;/p&gt;

&lt;p&gt;In fact, I think that project is really cool. I just prefer the ability to create different networks for different tasks. This allows you to better organize the structure and reduce the load on each node and the network as a whole. If necessary, you can even split the network into pieces within a single project based on certain criteria, reducing the overall load.&lt;/p&gt;

&lt;p&gt;So, &lt;strong&gt;storacle&lt;/strong&gt; uses the &lt;a href="https://github.com/ortexx/spreadable" rel="noopener noreferrer"&gt;spreadable&lt;/a&gt; mechanism to organize the network. Main features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Files can be added to the storage through any node.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Files are saved as a whole, not in blocks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each file has its own unique content hash for further work with it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Files can be duplicated for greater reliability&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The number of files on a single node is limited only by a file system (there is an exception, which will be discussed later)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The number of files in the network is limited by the capabilities of spreadable by the number of allowed nodes in the network, which in the second version can allow you to work with an infinite number of nodes (more on this in another article)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple example of how it works from the program:&lt;/p&gt;

&lt;p&gt;The server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;Node&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;storacle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;node&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;new&lt;/span&gt;  &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;Client&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;storacle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;client&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;new&lt;/span&gt;  &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost:4000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;hash&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;storeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./my-file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;link&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFileLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Inside peek&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There's nothing supernatural under the hood. Information about the number of files, their total size and other points are stored in the in-memory database and updated when deleting and adding files, so there is no need to frequently access the file system. An exception is the inclusion of the garbage collector when file circulation is needed, rather than limiting their number. In this case, you have to go through the storage from time to time. And working with a large number of files (let's say more than one million files) can lead to significant loads. It is better to store less files and run more nodes. If the "cleaner" is disabled, there is no such problem.&lt;/p&gt;

&lt;p&gt;The file storage consists of 256 folders and 2 levels of nesting. Files are stored in second-level folders. So, if we have 1 million files in each folder, there are about 62500 pieces (1000000 / sqrt (256)).&lt;/p&gt;

&lt;p&gt;Folder names are formed from the file hash to provide you quick access if necessary.&lt;/p&gt;

&lt;p&gt;This structure was chosen based on a large number of different storage requirements: support for weak file systems, where it is not desirable to have many files in a single folder, fast crawl of the folders if necessary, and so on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When files are added or received, links to files are written to the cache. This often means that you don't need to search the entire network for a file. This speeds up getting links and reduces the load on the network. Caching also occurs via http headers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isomorphism&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The client is written in javascript and is isomorphic, it can be used directly from your browser.&lt;/p&gt;

&lt;p&gt;You can upload a file &lt;a href="https://github.com/ortexx/storacle/blob/master/dist/storacle.client.js" rel="noopener noreferrer"&gt;https://github.com/ortexx/storacle/blob/master/dist/storacle.client.js&lt;/a&gt; as a script and get access to &lt;strong&gt;window.ClientStoracle&lt;/strong&gt; or import via the build system, etc&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deferred links&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An interesting feature is also the "deferred link". This is a link to the file that can be obtained synchronously, here and now, and the file will be pulled up when it is found in the storage. This is very convenient, for example, when you need to show some images on the site. Just put a deferred link in the src and that's it. You can come up with a lot of cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Api of the client&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;async &lt;strong&gt;Client.prototype.storeFile()&lt;/strong&gt; - file storing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;async &lt;strong&gt;Client.prototype.getFileLink()&lt;/strong&gt; - getting a direct link to a file&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;async &lt;strong&gt;Client.prototype.getFileLinks()&lt;/strong&gt; - getting a list of direct links to a file from all nodes where it exists&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;async &lt;strong&gt;Client.prototype.getFileToBuffer()&lt;/strong&gt; - getting a file as a buffer&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;async &lt;strong&gt;Client.prototype.getFileToPath()&lt;/strong&gt; - getting a file to the file system&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;async &lt;strong&gt;Client.prototype.getFileToBlob()&lt;/strong&gt; - getting a file as a blob (for the browser version)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;async &lt;strong&gt;Client.prototype.removeFile()&lt;/strong&gt; - file deletion&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Client.prototype.createRequestedFileLink()&lt;/strong&gt; - creating a deferred link&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Export files to another server&lt;/p&gt;

&lt;p&gt;To transfer files to another node, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Just copy the entire storage folder along with the settings. (this may not work in the future).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy only the file folder. But in this case, you have to run the &lt;strong&gt;node.normalizeFilesInfo()&lt;/strong&gt; function once to recalculate all the data and put it in the database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the &lt;strong&gt;node.exportFiles()&lt;/strong&gt; function, which starts copying files.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main node settings &lt;/p&gt;

&lt;p&gt;When running the storage node, you can specify all the necessary settings. Only the most basic ones are listed below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;storage.dataSize&lt;/strong&gt; - size of the file folder&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;storage.tempSize&lt;/strong&gt; - size of the temporary folder&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;storage.autoCleanSize&lt;/strong&gt; - minimum size of storage that you want to keep. If you specify this parameter, the most underused files will be deleted as soon as there is not enough space.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;file.maxSize&lt;/strong&gt; - maximum file size&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;file.minSize&lt;/strong&gt; - minimum file size&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;file.preferredDuplicates&lt;/strong&gt; - preferred number of duplicate files in the network&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;file.mimeWhitelist&lt;/strong&gt; - acceptable file types&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;file.mimeBlacklist&lt;/strong&gt; - unacceptable file types&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;file.extWhitelist&lt;/strong&gt; - acceptable file extensions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;file.extBlacklist&lt;/strong&gt; - unacceptable file extensions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;file.linkCache&lt;/strong&gt; - link caching settings&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Almost all parameters related to sizes can be set in both absolute and relative values.&lt;/p&gt;

&lt;p&gt;Using the command line&lt;/p&gt;

&lt;p&gt;The library can be used via the command line. You need to install it globally: &lt;strong&gt;npm i -g storacle&lt;/strong&gt;. After that, you can run the necessary actions from the project directory where the node is located.&lt;br&gt;&lt;br&gt;
For example, &lt;strong&gt;storacle -a storeFile -f ./file.txt -c ./config.js&lt;/strong&gt; to add a file. All actions can be found in &lt;a href="https://github.com/ortexx/storacle/blob/master/bin/actions.js" rel="noopener noreferrer"&gt;https://github.com/ortexx/storacle/blob/master/bin/actions.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why would you want to use that&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If you want to create a decentralized project where you are going to store and work with files using convenient methods. For example, the music project, that is described in the link at the beginning of the article, uses storacle.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you are working on any other project where you need to store files distributed. You can easily build your own closed network, flexibly configure nodes and add new ones when you need it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you just need to store the files of your site somewhere and you have to write everything yourself. Perhaps this library is better than others, in your case.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you have a project in which you work with files, but want to perform all manipulations from the browser. You can avoid writing server-side code.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My contacts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://t.me/ortex" rel="noopener noreferrer"&gt;@ortex&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="mailto:mywebstreet@gmail.com"&gt;mywebstreet@gmail.com&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>showdev</category>
      <category>javascript</category>
      <category>node</category>
      <category>database</category>
    </item>
    <item>
      <title>Museria - a decentralized music storage</title>
      <dc:creator>Diogen Vagrant</dc:creator>
      <pubDate>Tue, 05 May 2020 07:00:21 +0000</pubDate>
      <link>https://dev.to/ortexx/museria-a-decentralized-music-storage-cg0</link>
      <guid>https://dev.to/ortexx/museria-a-decentralized-music-storage-cg0</guid>
      <description>&lt;p&gt;One day I decided to write an application for selecting music. I wanted to organize a stream of music to select something and listen to it at any time. Nothing complicated, right? The architecture was successfully designed, the preparations were completed. But a "little problem" arose. Where to get the song files? At that moment I realized that the whole world of music was plunged into commerce. It's not bad when people want to earn money on their work, but at what cost? When in the end no one but large companies can create a full-fledged music application without resorting to all sorcery.&lt;/p&gt;

&lt;p&gt;Then two tasks arose before me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Organizing the free distribution of music, using methods that are convenient for most people, including programmable ones.&lt;/li&gt;
&lt;li&gt;  Offer earning alternatives to music creators.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Global decentralized music storage.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Initially, I tried to find existing solutions and create everything based on this. After some time searching, &lt;a href="https://github.com/ipfs/ipfs" rel="noopener noreferrer"&gt;ipfs&lt;/a&gt; was the first one I liked. I started implementing my idea, but after a while I found several critical problems in this solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Ipfs is the repository for everything. There are texts, images, music, videos, anything. By and large, this is such a big planetary "garbage". When you start your node, you immediately get a huge load.&lt;/li&gt;
&lt;li&gt;  Some kind of unfinished "garbage collection" mechanism. I don't know how it is now, but at that moment, if you specified in the config that you want to limit the storage to ten gigabytes of data, it didn't mean anything. The repository was growing, ignoring many configuration options. In the end, you had to have a huge supply of hard drive, while ipfs figured out how to reset the unnecessary.&lt;/li&gt;
&lt;li&gt;  At the time of using the library (I don't know about now), the client did not have timeouts. You send a request to get a file, and if it is not there, then you just hang. Of course, people came up with all kinds of workarounds that partly solved the problem, but it was crutches. Such things should be out of the box.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There were still many minor problems that I don't remember now, but the impression was unambiguous: this cannot be used for my project. I continued to search for a solution, studied various options, but did not find anything suitable.&lt;/p&gt;

&lt;p&gt;In the end, I decided that I should try writing a decentralized storage myself. Let it not pretend to be interplanetary, but it will solve my specific task.&lt;/p&gt;

&lt;p&gt;So they appeared: &lt;a href="https://github.com/ortexx/spreadable" rel="noopener noreferrer"&gt;spreadable&lt;/a&gt;, &lt;a href="https://github.com/ortexx/storacle/" rel="noopener noreferrer"&gt;storacle&lt;/a&gt;, &lt;a href="https://github.com/ortexx/metastocle" rel="noopener noreferrer"&gt;metastocle&lt;/a&gt;, &lt;a href="https://github.com/ortexx/museria" rel="noopener noreferrer"&gt;museria&lt;/a&gt;, &lt;a href="https://github.com/ortexx/museria-global" rel="noopener noreferrer"&gt;museria-global&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;spreadable&lt;/strong&gt; - the main, lowest layer that allows you to combine nodes into a network. It contains an algorithm that I partially implemented based on the calculation of about 10,000 servers. The full version of the algorithm is much more difficult to implement and would require several additional months (maybe more).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  It uses http/https.&lt;/li&gt;
&lt;li&gt;  You can create a separate network for a specific task, which will significantly reduce the load on each individual project than if they were all on the same network.&lt;/li&gt;
&lt;li&gt;  The timeout mechanism and other important details were thought out from the very beginning for all methods both in the client and node. You can flexibly manage parameters from your app.&lt;/li&gt;
&lt;li&gt;  The library is written with nodejs. Performance problems are compensated by the decentralized nature. The load can be "smeared" by increasing the number of nodes. Instead, there are many advantages: a huge community, simplicity and convenience, an isomorphic client, and so on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;storacle&lt;/strong&gt; - a layer inherited from &lt;strong&gt;spreadable&lt;/strong&gt; that allows you to store files on the network. Each file has its own content hash, which can be used to get it later. Files are not divided into blocks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;metastocle&lt;/strong&gt; - a layer inherited from spreadable that allows you to store data on the network, but not files. The interface is similar to a nosql database. For example, you can add a file to storage, get its hash, and write it to metastocle by linking it to something.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;museria&lt;/strong&gt; - inherited from storace and metastocle. This layer is directly responsible for storing music. The storage only works with mp3 files and id3 tags.&lt;/p&gt;

&lt;p&gt;As the "key" to the song, its full name is used in the form of &lt;strong&gt;Artist (TPE1) - Title (TIT2)&lt;/strong&gt;. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Brimstone - The Burden&lt;/li&gt;
&lt;li&gt;  Hi-rez - Lost My Way (feat. Emilio Rojas, Dani Devinci)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can learn as much as possible about how song titles are formed here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ortexx/museria/blob/master/src/utils.js#L60" rel="noopener noreferrer"&gt;https://github.com/ortexx/museria/blob/master/src/utils.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Look at &lt;strong&gt;utils.beautifySongTitle()&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A key match is the percentage specified in the node settings. For example, a value of 0.85 means that if the key comparison function (song names) found a similarity of more than 85%, then this is the same song.&lt;br&gt;
Algorithm for determining similarity in the same place, in the function &lt;strong&gt;utils.getSongSimilarity()&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An example of working with the storage you can find in &lt;a href="https://github.com/ortexx/museria" rel="noopener noreferrer"&gt;the readme&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All of the above layers are self-sufficient and can be used separately as lower layers for other projects. For example, now there is an idea to make a layer for storing books.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;museria-global&lt;/strong&gt; - a configured git repository to run your own node in the global music storage network. Clone it and then: &lt;strong&gt;npm i &amp;amp;&amp;amp; npm start&lt;/strong&gt;. That's it. You can configure in more detail, run in docker, etc. Detailed information is available &lt;a href="https://github.com/ortexx/museria-global#museria-global-storage-alpha-" rel="noopener noreferrer"&gt;on github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When the repository is updated, you should update your node as well. If the major or minor version number changes, then this action is required, otherwise your node will be ignored by the network.&lt;/p&gt;

&lt;p&gt;You can work with songs manually or programmatically. Each node starts the server for different tasks. In particular, when you visit the default endpoint, you will get an interface for working with music. For example, you can go to &lt;a href="http://storage.museria.com/" rel="noopener noreferrer"&gt;the root node&lt;/a&gt; (the link may not be up to date later, you can get input nodes in &lt;a href="https://t.me/museria" rel="noopener noreferrer"&gt;telegram&lt;/a&gt;, or view updates on github).&lt;/p&gt;

&lt;p&gt;There you can search and upload songs. Songs can be uploaded in two modes: normal and moderated. The second mode means that the work is done by a person, not a program. And if you check this box when adding it, you will need to solve the captcha. Songs can be added with the priorities of -1, 0 or 1. Priority 1 can only be set in moderated mode. Priorities are needed to let the storage more effectively decide what to do when you try to replace an existing song with a new one. The higher the priority, the more likely you will overwrite an existing file. This helps fight spam and increases the quality of downloaded songs.&lt;/p&gt;

&lt;p&gt;How files are technically added, in a nutshell:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The client gets the address of the free node, which will become the coordinator for a while.&lt;/li&gt;
&lt;li&gt;  The function of adding a song (by a person or code) is triggered, and a request is made to the coordinator's endpoint to add a file.&lt;/li&gt;
&lt;li&gt;  The coordinator calculates how many duplicates to make (configurable param).&lt;/li&gt;
&lt;li&gt;  Search for the most suitable nodes to save.&lt;/li&gt;
&lt;li&gt;  The file goes directly to these nodes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How files are received technically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The client gets the address of the free node, which will become the coordinator for a while.&lt;/li&gt;
&lt;li&gt;  The function of getting a song (by a person or code) is triggered, and a request is made to the coordinator's endpoint to get a file.&lt;/li&gt;
&lt;li&gt;  The coordinator checks for the link in the cache. If there is one and it is working, it is immediately returned to the client, otherwise the nodes are polled for availability.&lt;/li&gt;
&lt;li&gt;  The file is received by the link, if there is one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Alternatives for music creators&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have always been interested in the question, how can we objectively estimate the cost of creative works? Why does anybody sell their music album for $10, $20 or $100. Where is the algorithm? When we are talking about a physical product, or even many types of services, we have at least the prime cost.&lt;/p&gt;

&lt;p&gt;Okay, let's take $10. Is it effective? Let's say I listened to an album somewhere or a song from there and decided to thank you. But according to my feelings and material possibilities, $3 is my top. What should I do? I probably just won't do anything, like most people. By setting a fixed price for creative work, you only limit yourself, do not allow a large number of people to send you less money, which in total can be more impressive than those who will buy at the price you set. It seems to me that creativity is exactly the sphere where donations should rule in the first place. To do this, we need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Teach people to thank in this way. The creators themselves must clearly show that they would like to receive donations, add links to different payment methods everywhere, and so on.&lt;/li&gt;
&lt;li&gt;  More mechanisms are needed to simplify and strengthen these processes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, someone can create a global website where you can donate through the special author's links.&lt;/p&gt;

&lt;p&gt;Something like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://someartistsdonationsite.com/{category}/{artist}?{external-info}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If to narrow it down to musicians,then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://someartistsdonationsite.com/music/eminem?song=blabla
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Artists need to verify their nickname and link themselves to it.&lt;/p&gt;

&lt;p&gt;In the museria client, we add a function for generating such a link, and all projects using the storage can place buttons for donations with these links next to the songs on their sites/apps. Users can quickly and easily make a donation. Naturally, this approach can be used in any project and creative category, not just through the storage and music.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why you need the music storage, and how you can participate in it.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  If you are working on a project related to music, or are planning to create one, then this is what it was all about. You can use museria to store and receive songs, increasing the flow of songs on the network. If, at the same time, you have the ability to run and hold at least one own node, it will be the best contribution to the development of the network.&lt;/li&gt;
&lt;li&gt;  Perhaps you are ready to take on some other role: help with the code, or fill in and moderate the database, distribute information about the project to your friends, and so on.&lt;/li&gt;
&lt;li&gt;  Maybe you like the idea and are ready to help financially, so that it all lives and develops. The more nodes, the more songs.&lt;/li&gt;
&lt;li&gt;  Or you may just need to find and download a song at some point. You can do this very simply, for example, through the &lt;a href="https://t.me/MuseriaBot" rel="noopener noreferrer"&gt;telegram bot&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project is now at the very beginning stage. The test network is running, nodes can frequently restart, require updates, and so on. if there are no critical problems during the estimation period, this same network is transformed into the main one.&lt;/p&gt;

&lt;p&gt;To view information about the node from the outside: the number of songs, free space, and so on, follow the link below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://node-address/status or http://node-address/status?pretty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My contacts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://t.me/ortex" rel="noopener noreferrer"&gt;@ortex&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="mailto:mywebstreet@gmail.com"&gt;mywebstreet@gmail.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://t.me/museria" rel="noopener noreferrer"&gt;Telegram group&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://museria.com/" rel="noopener noreferrer"&gt;Well, the site that gave birth to this idea.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>javascript</category>
      <category>node</category>
      <category>database</category>
    </item>
  </channel>
</rss>
