<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Josiah Witt</title>
    <description>Software Engineer</description>
    <link>https://josiahwitt.com/words</link>
    <atom:link href="https://josiahwitt.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sat, 16 May 2026 01:33:33 +0000</pubDate>
    <lastBuildDate>Sat, 16 May 2026 01:33:33 +0000</lastBuildDate>
    <generator>Jekyll v3.4.0</generator>
    
      <item>
        <title>How To Build a Github Bot for Discord using Val.town</title>
        <description>&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;On our team at &lt;a href=&quot;https://dxos.org&quot;&gt;DXOS&lt;/a&gt; we use &lt;a href=&quot;https://discord.com&quot;&gt;Discord&lt;/a&gt; for both community and team chat and we also use &lt;a href=&quot;https://github.com&quot;&gt;Github&lt;/a&gt; for code collaboration. Both of these services work well for us, but unfortunately they don’t work very well together. Unfortunately the options for notifications from Github are not the best. The email notifications aren’t bad but if you’re not on top of your inbox they can quickly get lost and buried. Additionally, perhaps I don’t understand how to use them but, the web notifications are just not useful in my experience. Other than this, the only notifications integration for Github that I’m aware of is their &lt;a href=&quot;https://github.com/integrations/slack&quot;&gt;Slack integration&lt;/a&gt;. I haven’t used it in a number of years but I remember it being quite useful at a previous job. This is the experience we were hoping to emulate inside Discord.&lt;/p&gt;

&lt;p&gt;Unfortunately, the Github API doesn’t make this easy. In order to get a coherent stream of relevant information you’d need to make several api calls and figure out how to join all the relevant data. At that point you probably want some caching so you’re not making tons of repeat api calls and now you’ve got a full blown service on your hands. Not to mention the cost of running something like that, my general feeling about “dev ops” is that I don’t want to do it, so this didn’t seem like a path we wanted to go down. It was at this point that we had an idea: the notifications that we want are available from Github via email without putting any data together ourselves, but we want to just be able to send emails to Discord. Enter &lt;a href=&quot;https://val.town&quot;&gt;Val.town&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The heading on the Val.town landing page is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If GitHub Gists could run and AWS Lambda were fun&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not only that, but one of the types of “vals” is &lt;a href=&quot;https://docs.val.town/types/email/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Email&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Email handler vals get their own email address that you can send email to, and when Val Town receives that email, it triggers the val with the email as its first argument. Ever wanted a robot that you could forward emails to, or to be able to control something via an email trigger? With email handler vals it’s simple: they get their own, unique address.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we’re cooking!&lt;/p&gt;

&lt;p&gt;So to summarize the solution we’re going to build:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;We need a Discord bot to be able to send DMs on Discord.&lt;/li&gt;
  &lt;li&gt;We need an email Val in order to send Github emails to and forward to Discord.&lt;/li&gt;
  &lt;li&gt;We need to configure our Github account to send notifications to the Val instead of our regular email address.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;setup-a-discord-bot&quot;&gt;Setup a Discord Bot&lt;/h2&gt;

&lt;p&gt;The first step here is to create an “application” on Discord, you can do that via &lt;a href=&quot;https://discord.com/developers/applications?new_application=true&quot;&gt;this link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once there you can optionally name your bot, give it an avatar, and hide it from public lists.&lt;/p&gt;

&lt;p&gt;One important setting to remember to turn is the &lt;code class=&quot;highlighter-rouge&quot;&gt;SERVER MEMBERS INTENT&lt;/code&gt; on the &lt;code class=&quot;highlighter-rouge&quot;&gt;Bot&lt;/code&gt; settings page. This will allow it to look at the members of the servers it is in to be able to send them DMs. While you’re on this page, grab the token for your bot for later.&lt;/p&gt;

&lt;p&gt;After that got the &lt;code class=&quot;highlighter-rouge&quot;&gt;OAuth2 &amp;gt; URL Generator&lt;/code&gt; page, check off the &lt;code class=&quot;highlighter-rouge&quot;&gt;Bot&lt;/code&gt; permission and grab the url it generates for you. Paste the url into a web browser and choose the server you want the bot to live on.&lt;/p&gt;

&lt;p&gt;The last thing you need to do with Discord is get your user id so that your bot knows who the forward the messages to. Discord has a &lt;a href=&quot;https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID&quot;&gt;helpful guide&lt;/a&gt; for how to do that if you don’t already have it.&lt;/p&gt;

&lt;h2 id=&quot;setup-an-email-val&quot;&gt;Setup an Email Val&lt;/h2&gt;

&lt;p&gt;I’ve already written some Vals which accomplish this, all you need to do is create a new email Val and then paste in the following code:&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://esm.town/v/std/email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;noteToSelf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://esm.town/v/wittjosiah/discord_note_to_self&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://esm.town/v/wittjosiah/parse_github_email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;notifications@github.com&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;bot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;embeds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;footer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dmId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;noteToSelf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;dmId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Deno&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DISCORD_DM_ID&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;userId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Deno&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DISCORD_USER_ID&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Deno&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DISCORD_GH_BOT_TOKEN&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dmId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;Deno&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DISCORD_DM_ID&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dmId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Now take your Discord user id and bot token from before and add them as &lt;a href=&quot;https://docs.val.town/reference/environment-variables/&quot;&gt;env vars&lt;/a&gt; in Val.town.&lt;/p&gt;

&lt;h2 id=&quot;connect-github-to-valtown&quot;&gt;Connect Github to Val.town&lt;/h2&gt;

&lt;p&gt;The last step is to get Github to send emails to your val. Before you do this, I’d recommend temporarily adding &lt;code class=&quot;highlighter-rouge&quot;&gt;console.log(message.text)&lt;/code&gt; to the beginning of your Val to be able to more easily grab the verification link.&lt;/p&gt;

&lt;p&gt;Now, grab the email of your val, go to your Github &lt;a href=&quot;https://github.com/settings/emails&quot;&gt;email settings&lt;/a&gt; and add it to your account. Once you’ve verified the email, you’ll then be able to set it up to route notifications to it based on the organization it originates from in the &lt;a href=&quot;https://github.com/settings/notifications/custom_routing&quot;&gt;custom routing settings&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That’s it! You’ve now got a custom Discord Github bot which will forward you all your email notifications.&lt;/p&gt;

&lt;p&gt;One last thing that might help this be more useful depending on your needs is to tune the &lt;a href=&quot;https://github.com/settings/notifications&quot;&gt;notification settings&lt;/a&gt; themselves. The setting called &lt;code class=&quot;highlighter-rouge&quot;&gt;Customize email updates&lt;/code&gt; is the key one here and I find it most useful to have only notifications for &lt;code class=&quot;highlighter-rouge&quot;&gt;Reviews&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Comments&lt;/code&gt; turned on.&lt;/p&gt;
</description>
        <pubDate>Thu, 09 Feb 2023 23:00:00 +0000</pubDate>
        <link>https://josiahwitt.com/2023/02/09/discord-github-valtown.html</link>
        <guid isPermaLink="true">https://josiahwitt.com/2023/02/09/discord-github-valtown.html</guid>
        
        <category>discord</category>
        
        <category>github</category>
        
        <category>valtown</category>
        
        <category>software</category>
        
        
      </item>
    
      <item>
        <title>An Introduction to Scuttlebutt Development: Flume &amp; Plugins</title>
        <description>&lt;p&gt;This will assume you have read and followed the previous post: &lt;a href=&quot;/2018/07/08/scuttlebutt-intro-test-playground.html&quot;&gt;Your Test Playground&lt;/a&gt;. If you have not you should check that out before continuing.&lt;/p&gt;

&lt;h2 id=&quot;sbot-plugins&quot;&gt;sbot Plugins&lt;/h2&gt;

&lt;p&gt;Now we’ll take a look at one of these plugins for &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt;. We’ll use a fairly simple one, &lt;a href=&quot;https://github.com/ssbc/ssb-about&quot;&gt;ssb-about&lt;/a&gt;, to demonstrate some things you can do in plugins. Plugins can add different types of functionality to sbot; what ssb-about adds, and what we will focus on here, is a database index, but plugins can also be used to handle functions such as replication logic and private messages.&lt;/p&gt;

&lt;p&gt;You will see, if you look at the code for ssb-about that it consists of all of &lt;a href=&quot;https://github.com/ssbc/ssb-about/blob/e7f9b0b243ec462fc2628c0eb7b9be29b06c10f1/index.js&quot;&gt;52 lines of code&lt;/a&gt;. Needless to say its not the most complex piece of code, but you will find that it is quite useful!&lt;/p&gt;

&lt;h2 id=&quot;database-indexes&quot;&gt;Database Indexes&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; uses a database called &lt;a href=&quot;https://github.com/flumedb/flumedb&quot;&gt;Flume&lt;/a&gt;, which consists of an append-only log and modular views of that log. The log is where all the data is stored and messages are appended. Views are essentially indexes of the log and can be anything as long as they are generated strictly from the data, such that the same data will give you the same view if rebuild.&lt;/p&gt;

&lt;p&gt;The type of view that ssb-about and many other ssb modules use is called &lt;code class=&quot;highlighter-rouge&quot;&gt;flumeview-reduce&lt;/code&gt;, which uses a map-reduce function pair to create a view. The idea is simple enough, to create this view you define a map function and a reduce function. When paired with a log the view will be created by mapping over every message in the log and then reducing into a single object, which is returned as the view. If you’ve never encountered the map-reduce programming pattern I recommend you &lt;a href=&quot;https://en.wikipedia.org/wiki/MapReduce&quot;&gt;read a bit about it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each individual message is not important here, what is important is the final reduced state, the view. After reducing all the existing messages in the log the view is stored so that it does not need to be recomputed each time a new message is added to the log. Instead the new message is just reduced into the stored view to create a new view, this makes it very efficient at handling incoming data.&lt;/p&gt;

&lt;h2 id=&quot;ssb-about&quot;&gt;ssb-about&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ssb-about&lt;/code&gt; is an &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; plugin which keeps track of &lt;code class=&quot;highlighter-rouge&quot;&gt;about&lt;/code&gt; messages. &lt;code class=&quot;highlighter-rouge&quot;&gt;about&lt;/code&gt; messages are the messages primarily used to name things. For example, when you startup Patchwork for the first time and it asks you to input a name for yourself. That is publishing an &lt;code class=&quot;highlighter-rouge&quot;&gt;about&lt;/code&gt; message about yourself. It would look something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;%tceR4cb3J/aIOd1vwm96OAlj4y62LqbtLsVPiS1CfLs=.sha256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;previous&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;%PYHOPseaFKTFfkSXbtMjuQC7fC5BvePflQamh1812K0=.sha256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;sequence&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;@MKEfPpaGFMInD9P0ZIUykojfwaXmo6CtWOzB1EBC4qg=.ed25519&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1521080232427&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;hash&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sha256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;about&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;about&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;%scqes7231lx8Gak1DTrXjsFSLwnhVqXxBSGkkj5UCPs=.sha256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Eve&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;signature&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;T44+YzUoCfATdl0XXNK4jw0btqkOeQUHopZR6nq+/0eBZlx8sGRpCXa6GrifEmXtJ7j1i5IK3Ymz1AvspnRdCw==.sig.ed25519&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1521080232428&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ssb-about&lt;/code&gt; keeps an index of these &lt;code class=&quot;highlighter-rouge&quot;&gt;about&lt;/code&gt; messages so that clients can easily lookup things such as nicknames, descriptions, and display images, among other things. We’ll go through &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb-about&lt;/code&gt; piece by piece so that you understand how these flume views work. The first thing in ssb-about are the dependencies:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FlumeReduce&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'flumeview-reduce'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'ssb-ref'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;flumeview-reduce&lt;/code&gt;, as mentioned earlier is the library that is used create the flume views here. &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb-ref&lt;/code&gt; is a helper library which provides the ability to check whether strings are valid ssb references or not. Next, some module information is provided:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'about'&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'./package.json'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;version&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;manifest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'source'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'async'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;name&lt;/code&gt; is just the name you give the plugin, it should be the name of the library minus the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb-&lt;/code&gt; prefix (I’ve had trouble with &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; not finding my plugins when this is not the case). &lt;code class=&quot;highlighter-rouge&quot;&gt;version&lt;/code&gt; is just the version number of the package. &lt;code class=&quot;highlighter-rouge&quot;&gt;manifest&lt;/code&gt; gives information on what methods are available from the about view and what types of functions they are. &lt;code class=&quot;highlighter-rouge&quot;&gt;source&lt;/code&gt; is a live stream of messages, &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; is unsurprisingly an asynchronous function with a callback, and there is also a &lt;code class=&quot;highlighter-rouge&quot;&gt;sync&lt;/code&gt; option which is for synchronous functions.&lt;/p&gt;

&lt;p&gt;Following the module information, the flume view is declared:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ssb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ssb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_flumeUse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'about'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FlumeReduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This function is taking in an &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; which, here, is &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb&lt;/code&gt; and some optional configuration which currently is not utilized. Your &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; already has an existing log with messages in it, so all that needs to be done is add a view to it, this is what &lt;code class=&quot;highlighter-rouge&quot;&gt;_flumeUse()&lt;/code&gt; does. The first argument is the name of the view and how you would access the module through an &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; client. For example &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot.about.stream()&lt;/code&gt; would return the stream of messages flowing through this view. The second argument is the view itself, which here is a &lt;code class=&quot;highlighter-rouge&quot;&gt;FlumeReduce&lt;/code&gt; view.&lt;/p&gt;

&lt;p&gt;FlumeReduce itself takes several arguments, the first being the version of the view. The version can be anything, but a number is recommended. Every time the version changes the view will be rebuild from scratch to account for any changes that have been made. Its useful to know that the version number does not always need to increment, so when developing and testing a view you can flip between just two versions and succesfully rebuild the view each time.&lt;/p&gt;

&lt;p&gt;The second, and last required, argument is a reduce function. The reduce function takes in a message and indexes it in the view. The third, and first optional, argument is the map function. The map function is called prior to the reduce function on a message and performs a transformation on the message before it is reduced.&lt;/p&gt;

&lt;p&gt;The other two optional arguments, unused here, are the codec and an initial state. The codec can be specified and used in the event your log uses the filesystem and the initial state provides an initial reduce state in the event that no messages have been reduced yet.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;_flumeUse&lt;/code&gt; function returns an object with a &lt;code class=&quot;highlighter-rouge&quot;&gt;stream&lt;/code&gt; function and a &lt;code class=&quot;highlighter-rouge&quot;&gt;get&lt;/code&gt; function in it; this aligns with what was specified in the &lt;code class=&quot;highlighter-rouge&quot;&gt;manifest&lt;/code&gt; above. The &lt;code class=&quot;highlighter-rouge&quot;&gt;stream&lt;/code&gt; function gives a &lt;a href=&quot;https://github.com/pull-stream/pull-stream&quot;&gt;pull-stream&lt;/a&gt; whose first value is the current state of the view and following values new messages coming into the view. The &lt;code class=&quot;highlighter-rouge&quot;&gt;get&lt;/code&gt; function simply gives the current state of the view.&lt;/p&gt;

&lt;p&gt;Finally, we will look at how the view is actually built. Let’s skip over the &lt;code class=&quot;highlighter-rouge&quot;&gt;reduce&lt;/code&gt; function for now and jumpy straight to the &lt;code class=&quot;highlighter-rouge&quot;&gt;map&lt;/code&gt; function:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'about'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isLink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;about&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;about&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'about'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'type'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;values&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The outer if statement is filtering out any messages that are not &lt;code class=&quot;highlighter-rouge&quot;&gt;about&lt;/code&gt; messages, which is what this view is for. After that it pulls some helpful variables out of the message structure: namely the author reference and the reference to the target that is being described. Following this the values dictionary is initialized and populated based on the message. Any key values pairs in the message content other than &lt;code class=&quot;highlighter-rouge&quot;&gt;type&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;about&lt;/code&gt; will be taken to be describing the target. The index keeps track and what is being described (&lt;code class=&quot;highlighter-rouge&quot;&gt;key&lt;/code&gt;), the description (&lt;code class=&quot;highlighter-rouge&quot;&gt;content[key]&lt;/code&gt;), who described it (&lt;code class=&quot;highlighter-rouge&quot;&gt;author&lt;/code&gt;), and when they described it (&lt;code class=&quot;highlighter-rouge&quot;&gt;timestamp&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The sample &lt;code class=&quot;highlighter-rouge&quot;&gt;about&lt;/code&gt; message from above would be mapped to:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;@MKEfPpaGFMInD9P0ZIUykojfwaXmo6CtWOzB1EBC4qg=.ed25519&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;@MKEfPpaGFMInD9P0ZIUykojfwaXmo6CtWOzB1EBC4qg=.ed25519&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Eve&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1521080232427&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Here, the user &lt;code class=&quot;highlighter-rouge&quot;&gt;@MKEfPpaGFMInD9P0ZIUykojfwaXmo6CtWOzB1EBC4qg=.ed25519&lt;/code&gt; is naming themselves &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;Eve&quot;&lt;/code&gt; and this happened at 1521080232427.&lt;/p&gt;

&lt;p&gt;Now digging into the &lt;code class=&quot;highlighter-rouge&quot;&gt;reduce&lt;/code&gt; function:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reduce&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;valuesForId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;valuesForKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;valuesForId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;valuesForId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;valuesForKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;valuesForKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;valuesForKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This function is a little dense, but if you go through it piece my piece it becomes clearer. &lt;code class=&quot;highlighter-rouge&quot;&gt;result&lt;/code&gt; is the currently reduced state (if there is none it will be set to the &lt;code class=&quot;highlighter-rouge&quot;&gt;initialState&lt;/code&gt; passed to &lt;code class=&quot;highlighter-rouge&quot;&gt;FlumeReduce&lt;/code&gt;, which in this case is &lt;code class=&quot;highlighter-rouge&quot;&gt;undefined&lt;/code&gt;) and &lt;code class=&quot;highlighter-rouge&quot;&gt;item&lt;/code&gt; is a message from the log which has been mapped by the &lt;code class=&quot;highlighter-rouge&quot;&gt;map&lt;/code&gt; function. Since no &lt;code class=&quot;highlighter-rouge&quot;&gt;initialState&lt;/code&gt; is specified, there is a check to ensure that &lt;code class=&quot;highlighter-rouge&quot;&gt;result&lt;/code&gt; is initialized, as well as a check to make sure &lt;code class=&quot;highlighter-rouge&quot;&gt;item&lt;/code&gt; exists.&lt;/p&gt;

&lt;p&gt;The rest of these nested &lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt; loops basically amount to merging &lt;code class=&quot;highlighter-rouge&quot;&gt;item&lt;/code&gt; into &lt;code class=&quot;highlighter-rouge&quot;&gt;result&lt;/code&gt; while making sure everything is initialized. The if statement at the inner &lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt; loop, is checking whether the incoming value is newer than the existing value based on the timestamps; the incoming value is only accepted if it is newer, otherwise the existing value remains.&lt;/p&gt;

&lt;p&gt;And that’s it, that’s how the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb-about&lt;/code&gt; plugin works to build the &lt;code class=&quot;highlighter-rouge&quot;&gt;about&lt;/code&gt; message index!&lt;/p&gt;

&lt;h2 id=&quot;hello-world&quot;&gt;Hello World&lt;/h2&gt;

&lt;p&gt;Now, it’s time to make your own little &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; plugin. We’re going to make a simple little view of Hello World messages. If you have gone through the previous post you will already have one of these messages in your testnet log, if not, you can add one by simply running:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;sbot publish --type post --text &lt;span class=&quot;s2&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The view we’re going to make will look something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So we’ll start with making our new project:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;mkdir ssb-helloworld
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;ssb-helloworld
npm init
npm install --save flumeview-reduce ssb-ref&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next we’ll fill out the boilerplate of our plugin, similar to &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb-about&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FlumeReduce&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'flumeview-reduce'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'ssb-ref'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'helloworld'&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'./package.json'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;version&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;manifest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'source'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'async'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ssb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ssb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_flumeUse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'helloworld'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FlumeReduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reduce&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I think we’ll try using the &lt;code class=&quot;highlighter-rouge&quot;&gt;initialState&lt;/code&gt; parameter this time around, so we’ll change it up as such:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;initialState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ssb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ssb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_flumeUse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'helloworld'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FlumeReduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;initialState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now taking a look at the map function. We only want to index &lt;code class=&quot;highlighter-rouge&quot;&gt;post&lt;/code&gt; messages with the text &lt;code class=&quot;highlighter-rouge&quot;&gt;Hello World!&lt;/code&gt;. From that, we want to keep track of all the message ids and who published those messages. From that we end up with the following &lt;code class=&quot;highlighter-rouge&quot;&gt;map&lt;/code&gt; function:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'post'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Hello World!'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timestamp&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This will provide us with a stream of author to message id pairs into our &lt;code class=&quot;highlighter-rouge&quot;&gt;reduce&lt;/code&gt; function. In our reduce function, after making sure the &lt;code class=&quot;highlighter-rouge&quot;&gt;item&lt;/code&gt; exists we want to loop through all the &lt;code class=&quot;highlighter-rouge&quot;&gt;author&lt;/code&gt;s in the &lt;code class=&quot;highlighter-rouge&quot;&gt;item&lt;/code&gt; in order to add the message ids to the index. If an &lt;code class=&quot;highlighter-rouge&quot;&gt;author&lt;/code&gt; has no previous messages in the index, their index needs to be initialized; then the message ids from the &lt;code class=&quot;highlighter-rouge&quot;&gt;item&lt;/code&gt; can be concatenated to the end. After the entire item has been processed the result is returned.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reduce&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;To install this into our &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; folder we need to &lt;code class=&quot;highlighter-rouge&quot;&gt;npm link&lt;/code&gt; it. First in the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb-helloworld&lt;/code&gt; directory run:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;npm link&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then navigating into your &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; directory (&lt;code class=&quot;highlighter-rouge&quot;&gt;ssb-test&lt;/code&gt; if you are following along), run:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;npm link ssb-helloworld&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now restart your &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; and check whether your view was created as intended. To check the current state of the view you can open up a &lt;code class=&quot;highlighter-rouge&quot;&gt;node&lt;/code&gt; REPL with &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb-client&lt;/code&gt; to query the view.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ssbClient&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'ssb-client'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;ssbClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sbot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;sbot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;helloworld&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;sbot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If all went well you should see something similar to the outlined structure above:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;@MKEfPpaGFMInD9P0ZIUykojfwaXmo6CtWOzB1EBC4qg=.ed25519&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;%yaQL8/anqArSnybN8Isv5CgQz/Nkrski4CpKBM0wNbc=.sha256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1524275394775&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Here, &lt;code class=&quot;highlighter-rouge&quot;&gt;%yaQL8/anqArSnybN8Isv5CgQz/Nkrski4CpKBM0wNbc=.sha256&lt;/code&gt; is the id of the &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;Hello World&quot;&lt;/code&gt; message published earlier.&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next&lt;/h2&gt;

&lt;p&gt;You now have a very basic custom &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; plugin installed in your local &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt;. Next, I will take a closer look at Patchbay and how you can build user interfaces for Scuttlebutt.&lt;/p&gt;
</description>
        <pubDate>Sun, 08 Jul 2018 15:00:00 +0000</pubDate>
        <link>https://josiahwitt.com/2018/07/08/scuttlebutt-intro-flume.html</link>
        <guid isPermaLink="true">https://josiahwitt.com/2018/07/08/scuttlebutt-intro-flume.html</guid>
        
        <category>scuttlebutt</category>
        
        <category>software</category>
        
        
      </item>
    
      <item>
        <title>An Introduction to Scuttlebutt Development: Your Test Playground</title>
        <description>&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;Since you’re here, I’m going to assume that you know what Scuttlebutt is and that you’re interested in developing using it. If this is not the case, today’s your lucky day! I recommend Andre Staltz’s &lt;a href=&quot;https://staltz.com/an-off-grid-social-network.html&quot;&gt;blog&lt;/a&gt; &lt;a href=&quot;https://staltz.com/a-plan-to-rescue-the-web-from-the-internet.html&quot;&gt;posts&lt;/a&gt; as a good place to wet your appetite.&lt;/p&gt;

&lt;p&gt;Today, we will walk through how to setup a Scuttlebutt test network in your local environment. A test network is a very useful tool for learning and experimenting with reckless abandon as you needn’t worry about breaking things. If anything goes wrong in your little playground you can just blow it all up and start over, no harm no foul. In contrast to this, if you are experimenting on the main network and and gossiping all those messages to your peers, if anything goes awry there’s a chance of it being sent to your peers as well; not to mention the fact that others may not be interested in seeing all of your test messages all the time. All this to say, if you’re going to be developing for Scuttlebutt I would recommend devising a way to not gossip around your test messages. The following post is how I have gone about doing that after some discussion with others on Scuttlebutt.&lt;/p&gt;

&lt;h2 id=&quot;dependencies&quot;&gt;Dependencies&lt;/h2&gt;

&lt;p&gt;We’re going to walk through the install of &lt;a href=&quot;https://github.com/ssbc/scuttlebot&quot;&gt;Scuttlebot&lt;/a&gt; (&lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt;), which is documented more concisely &lt;a href=&quot;https://ssbc.github.io/docs/scuttlebot/install.html&quot;&gt;here&lt;/a&gt;. You will need node v4 or greater (you might want to use &lt;a href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;) and node-gyp (&lt;code class=&quot;highlighter-rouge&quot;&gt;npm install -g node-gyp&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;If you’re on Linux, installation may fail when building the libsodium dependencies due to missing aclocal. On Ubuntu you can install them by running the following:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;sudo apt-get install autotools-dev automake m4 libtool eclipse-cdt-autotools&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;On MacOS you may need the following packages installed, which you can install via &lt;a href=&quot;https://brew.sh/&quot;&gt;Homebrew&lt;/a&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;brew install libtool automake autoconf&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now you can install &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;npm install -g scuttlebot&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;To test whether &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; installed correctly run start Scuttlebot as a server by running:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;sbot server&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Once that is running, open a second shell session and use the &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; CLI access the API. Try the following commands:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;sbot whoami
sbot publish --type post --text &lt;span class=&quot;s2&quot;&gt;&quot;Hello World&quot;&lt;/span&gt;
sbot createLogStream&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;setting-up-a-local-testnet&quot;&gt;Setting Up A Local Testnet&lt;/h2&gt;

&lt;p&gt;Whether you just installed &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; now or you already had it installed, you probably ran &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot server&lt;/code&gt; to see if it was installed correctly. When you run &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot server&lt;/code&gt; for the first time, &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; creates a folder in your home directory called &lt;code class=&quot;highlighter-rouge&quot;&gt;.ssb&lt;/code&gt;. This is where &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; stores all the messages, indexes, blobs and more.&lt;/p&gt;

&lt;h3 id=&quot;scuttlebot-configuration&quot;&gt;Scuttlebot Configuration&lt;/h3&gt;

&lt;p&gt;When you install &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; it comes with a bunch of pre-configured configured network settings. You can check out all the default settings &lt;a href=&quot;https://github.com/ssbc/ssb-config/blob/master/inject.js&quot;&gt;here&lt;/a&gt;. We’re just going to focus on a few right now.&lt;/p&gt;

&lt;p&gt;The first setting we will look at is &lt;code class=&quot;highlighter-rouge&quot;&gt;name&lt;/code&gt;. As you can see if the link above, the default network name is &lt;code class=&quot;highlighter-rouge&quot;&gt;'ssb'&lt;/code&gt;, as in your &lt;code class=&quot;highlighter-rouge&quot;&gt;.ssb&lt;/code&gt; folder. If this name were to be changed to &lt;code class=&quot;highlighter-rouge&quot;&gt;'mysuperawesomenetwork'&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; would look for all the Scuttlebutt files in &lt;code class=&quot;highlighter-rouge&quot;&gt;~/.mysuperawesomenetwork&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The second setting(s) we will look at are the &lt;code class=&quot;highlighter-rouge&quot;&gt;port&lt;/code&gt; settings, both &lt;code class=&quot;highlighter-rouge&quot;&gt;port&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;ws.port&lt;/code&gt;. These determine which ports &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; runs on, and if you change them it will use different ports (shocking I know).&lt;/p&gt;

&lt;p&gt;The last setting we will look at is &lt;code class=&quot;highlighter-rouge&quot;&gt;caps&lt;/code&gt;. &lt;code class=&quot;highlighter-rouge&quot;&gt;caps&lt;/code&gt; is probably the most important setting, since it holds the network keys. These keys determine which network your &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; is on. &lt;a href=&quot;https://viewer.scuttlebot.io/@f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519&quot;&gt;@cel&lt;/a&gt; explains this better and more succinctly than I could so I will borrow &lt;a href=&quot;https://viewer.scuttlebot.io/%259yC1XNJJtNiJVCMGchCO7iRKB6xMUrDaCfLUeFPTk3o%3D.sha256&quot;&gt;his words&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Setting caps.shs makes gossip connections not occur with peers that have a different shs key.&lt;/p&gt;

  &lt;p&gt;Setting caps.sign makes messages to be considered invalid that were created with a different sign key.&lt;/p&gt;

  &lt;p&gt;If you only set the shs key, messages could leak out of your network if someone in the network changes their shs key, or adds messages manually. Setting the sign key ensures that the messages will not be able to be published (or validated) on the main ssb network, only on a network where that same sign key is used.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;caps.shs&lt;/code&gt; key that &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; comes pre-configured with is the key of the “main network”.  It is the key that all the packaged Scuttlebutt clients use and unless you know what you’re doing you probably shouldn’t change. So naturally, that’s what we’re going to do next!&lt;/p&gt;

&lt;p&gt;When learning and developing with Scuttlebutt you probably don’t want to be publishing and gossiping all your test messages around the network. Mostly, it’s just a little embarassing having everyone see your messages live as you learn. Your testnet is your local development environment, your playground.&lt;/p&gt;

&lt;p&gt;To make our local testnet, we’re going to need to give our &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; a place store it, and this is where that &lt;code class=&quot;highlighter-rouge&quot;&gt;name&lt;/code&gt; field comes in. Create a new folder in your home directory called &lt;code class=&quot;highlighter-rouge&quot;&gt;.ssb-test&lt;/code&gt; (or &lt;code class=&quot;highlighter-rouge&quot;&gt;.anythingyourwantreally&lt;/code&gt;). Inside &lt;code class=&quot;highlighter-rouge&quot;&gt;.ssb-test&lt;/code&gt; we’re going to make a file called &lt;code class=&quot;highlighter-rouge&quot;&gt;config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Open up a &lt;code class=&quot;highlighter-rouge&quot;&gt;node&lt;/code&gt; &lt;a href=&quot;https://www.tutorialspoint.com/nodejs/nodejs_repl_terminal.htm&quot;&gt;REPL&lt;/a&gt; and generate new &lt;code class=&quot;highlighter-rouge&quot;&gt;shs&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;sign&lt;/code&gt; keys with &lt;code class=&quot;highlighter-rouge&quot;&gt;crypto.randomBytes(32).toString('base64')&lt;/code&gt;. We want a new &lt;code class=&quot;highlighter-rouge&quot;&gt;shs&lt;/code&gt; key so that our testnet messages &lt;em&gt;won’t be&lt;/em&gt; gossiped to the main network and we want a new &lt;code class=&quot;highlighter-rouge&quot;&gt;sign&lt;/code&gt; key so that our testnet messages &lt;em&gt;can’t be&lt;/em&gt; gossiped onto the main network.&lt;/p&gt;

&lt;p&gt;Copy this into the &lt;code class=&quot;highlighter-rouge&quot;&gt;config&lt;/code&gt; file you made, filling in your keys where specified.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;caps&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;shs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;your shs key here&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;sign&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;your sign key here&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;port&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8007&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;ws&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;port&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8988&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;ssb_appname&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ssb-test&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We specify ports 8007 and 8988 so that our testnet can run in parallel with another &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; on the main network and we specify &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb_appname&lt;/code&gt; to be the name you gave your network. You will notice that most of the config settings aren’t mentioned here and that’s because for the rest the defaults are used, we only need to specify settings we want changed.&lt;/p&gt;

&lt;p&gt;Once you save your &lt;code class=&quot;highlighter-rouge&quot;&gt;config&lt;/code&gt; you’re almost ready to go, you just need to tell &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; which network you want to run on. To do that, you need to set the environment variable &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb_appname&lt;/code&gt;, this is what &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; looks for. So run &lt;code class=&quot;highlighter-rouge&quot;&gt;export ssb_appname=&quot;ssb-test&quot;&lt;/code&gt; and we’re good to go. (Note: you may want to also add this command to your &lt;code class=&quot;highlighter-rouge&quot;&gt;.bash_profile&lt;/code&gt; if you don’t want to run it in every bash session you want to access your testnet in.)&lt;/p&gt;

&lt;h3 id=&quot;getting-your-testnet-up-and-running&quot;&gt;Getting Your Testnet Up and Running&lt;/h3&gt;

&lt;p&gt;You can now run sbot with &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot server&lt;/code&gt; and it will be running your testnet! The &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb_appname&lt;/code&gt; environment variable will tell &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; where to look for the network files and there it will find your config file. Now open up another bash shell (ensuring &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb_appname&lt;/code&gt;is set) and try out your testnet by running some of the &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; CLI commands again:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;sbot whoami
sbot publish --type post --text &lt;span class=&quot;s2&quot;&gt;&quot;Hello World&quot;&lt;/span&gt;
sbot createLogStream&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Congratulations, you’ve just shared your first message on your testnet! You will notice that when you ran &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot whoami&lt;/code&gt; you have a different identity from when you initially setup &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt;. This is because you are now on a different network (your testnet) and so &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; created an identity for this network. Naturally, none of the messages you shared in the other network will appear here either, so the post you published will be the only message to show up in the log.&lt;/p&gt;

&lt;h3 id=&quot;scuttlebot-plugins&quot;&gt;Scuttlebot Plugins&lt;/h3&gt;

&lt;p&gt;Your &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; is now setup, woohoo! This however, is just a barebones &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; with the &lt;a href=&quot;https://github.com/ssbc/scuttlebot/tree/master/plugins&quot;&gt;default plugins&lt;/a&gt; installed, such as &lt;a href=&quot;https://github.com/ssbc/ssb-friends&quot;&gt;ssb-friends&lt;/a&gt; which handles the logic of whose feeds you will replicate. There are few other plugins that are used by some of the popular Scuttlebutt clients (ie. Patchbay and Patchwork) which add some helpful functionality. To install these plugins, run the following commands in your second shell:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;sbot plugins.install ssb-about
sbot plugins.install ssb-backlinks
sbot plugins.install ssb-fulltext
sbot plugins.install ssb-private&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Once these plugins are installed, kill the &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot server&lt;/code&gt; that you have running and restart it. One thing to note is that these plugins are installed in &lt;code class=&quot;highlighter-rouge&quot;&gt;.ssb-test/node_modules&lt;/code&gt;. This means that the plugins are installed per network and need to be installed again if you create another network.&lt;/p&gt;

&lt;p&gt;While you &lt;em&gt;can&lt;/em&gt; use your &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; just through the CLI, I wouldn’t recommend that as your primary interface into Scuttlebutt. And now that you’ve installed a few plugins your testnet is now ready for a graphical client like Patchbay, so let’s see if we can get that running!&lt;/p&gt;

&lt;h2 id=&quot;graphical-client&quot;&gt;Graphical Client&lt;/h2&gt;

&lt;p&gt;We will install Patchbay with git so that we have access to the source code to make changes as needed:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git clone git@github.com/ssbc/patchbay.git
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;patchbay
npm install
npm run rebuild&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Patchbay actually uses the &lt;a href=&quot;https://github.com/ssbc/ssb-config&quot;&gt;ssb-config&lt;/a&gt; we were looking at previously to determine how it connects to an &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt;, so in order to get Patchbay to connect to your testnet you will need to make changes to &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb-config&lt;/code&gt;. Clone it now:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git clone git@github.com:ssbc/ssb-config.git&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next open up &lt;code class=&quot;highlighter-rouge&quot;&gt;inject.js&lt;/code&gt; and modify it to match the settings you specifed in your testnet config previously. Once you have saved those changes we’re going to need to make Patchbay look at this ssb-config. In ssb-config run:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;npm link&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is make your ssb-config available to other repos on your machine via npm. Next, navigate back to Patchbay and run:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;npm link ssb-config&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This tells Patchbay to look at your local &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb-config&lt;/code&gt; rather than the one published to npm. Assuming your testnet &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; is still running, you can now start Patchbay by running (you may need to set the enviroment variable &lt;code class=&quot;highlighter-rouge&quot;&gt;ssb_appname&lt;/code&gt; in this session as well):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;npm run dev&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This will open an electron window with Patchbay in it and you should see the message that you posted earlier!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2018-07-08-patchbay.png&quot; alt=&quot;Patchbay&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next&lt;/h2&gt;

&lt;p&gt;You now have a playground setup for all your scuttle experiments. Next, I will dig into a basic plugin for &lt;code class=&quot;highlighter-rouge&quot;&gt;sbot&lt;/code&gt; and what it is useful for. Then, I will take a closer look at Patchbay and how you can build user interfaces for Scuttlebutt.&lt;/p&gt;
</description>
        <pubDate>Sun, 08 Jul 2018 14:00:00 +0000</pubDate>
        <link>https://josiahwitt.com/2018/07/08/scuttlebutt-intro-test-playground.html</link>
        <guid isPermaLink="true">https://josiahwitt.com/2018/07/08/scuttlebutt-intro-test-playground.html</guid>
        
        <category>scuttlebutt</category>
        
        <category>software</category>
        
        
      </item>
    
  </channel>
</rss>
