<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>nickmonad</title>
    <link rel="self" type="application/atom+xml" href="https://nickmonad.blog/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://nickmonad.blog"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-03-30T00:00:00+00:00</updated>
    <id>https://nickmonad.blog/atom.xml</id>
    <entry xml:lang="en">
        <title>Working software runs locally</title>
        <published>2026-03-30T00:00:00+00:00</published>
        <updated>2026-03-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Nick Miller
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nickmonad.blog/2026/working-software-runs-locally/"/>
        <id>https://nickmonad.blog/2026/working-software-runs-locally/</id>
        
        <content type="html" xml:base="https://nickmonad.blog/2026/working-software-runs-locally/">&lt;p&gt;I&#x27;ve been thinking a lot about testing recently.&lt;&#x2F;p&gt;
&lt;p&gt;Although, it&#x27;s kind of a &quot;blessing and a curse&quot; situation. A blessing in the sense that I&#x27;ve had the
opportunity to work on some interesting problems, with some very interesting tools. At work, we&#x27;ve been testing a product that
does simulation testing and as a result, I&#x27;ve seen some of the most powerful software I&#x27;ve ever seen in my professional
career. How is it a curse? Well, testing complex software is... complex. And an increased focus on testing is
usually done after a handful of stressful situations.&lt;&#x2F;p&gt;
&lt;p&gt;In any case, this recent focus on testing has reinforced my feeling that all software should be able to run locally,
on every developer&#x27;s machine in the organization, all the time, with minimal friction.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, the software I&#x27;m working on &lt;em&gt;can&lt;&#x2F;em&gt; always run on my machine, in the sense that all computers
can (theoretically) run all software. What I mean by &quot;run&quot; is more practical and pertinent to testing.
I&#x27;m talking specifically about the ability to start something locally, run a few commands against it, or throw
some predefined requests at it, to see how it behaves. This also includes external dependencies, to a reasonable degree
of fidelity.&lt;&#x2F;p&gt;
&lt;p&gt;In my experience, having the ability to do this repeatedly and reliably increases my ability to understand
and confidently change the system I&#x27;m working on.&lt;&#x2F;p&gt;
&lt;p&gt;As an example of this, I recently found myself trying to debug a configuration issue in a deployed testing environment.
My initial instinct was to stay as close to the deployment as possible, in order to be certain I was making the correct
changes. After an embarrassing amount of time fumbling around with these settings, I finally remembered that I had a
script laying around that could run this component locally. Moving my efforts to my own machine made a huge difference!
I didn&#x27;t have to wait nearly as long for the service to redeploy after a configuration change, and I was able to
prove the problem persisted, regardless of the runtime platform. Having this kind of tight feedback loop gave me the
confidence to dig more into the code and finally find the bug. Once I had a working hypothesis based on the code,
I could validate it within seconds.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve come across a surprising number of situations where existing tooling in a codebase doesn&#x27;t support this.
Or, you can tell that it did at some point, but the specification for it has rotted to the point of being useless.
At best, the service can be compiled and maybe a handful of integration tests can run. I can probably even deploy it!
But, I can&#x27;t run it in a mock environment locally. &lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s interesting to me how this happens, because I don&#x27;t think most systems start out this way. Running things
locally is a critical part of the early development cycle. I think that over time as more external dependencies are
added, and especially as the CI&#x2F;CD pipeline matures, the ability to run a complex system locally moves further out of
reach, mostly without even realizing it.&lt;&#x2F;p&gt;
&lt;p&gt;One reason I think this tends to happen is pressure to move quickly. If a team is under the gun to ship fast and
there is infrastructure in place to do some quick validation in the deployed environment, it&#x27;s easy to forget about
the local experience.&lt;&#x2F;p&gt;
&lt;p&gt;Perhaps though, there&#x27;s a sense that the local experience doesn&#x27;t actually matter all that much, because true
validation comes from production deployment. Charity Majors argues the point about validation in her (now famous?)
article &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;increment.com&#x2F;testing&#x2F;i-test-in-production&#x2F;&quot;&gt;&quot;I test in prod&quot;&lt;&#x2F;a&gt;.
I don&#x27;t think she&#x27;s arguing against running things locally (the word &quot;local&quot; isn&#x27;t even in the article),
but if the idea we should test in prod is taken to a sort of extreme, I can see how it would be easy to come to the
conclusion that production is the only thing that matters, at the cost of other means of validation.
I would argue that both matter: the ability to test locally, &lt;em&gt;and&lt;&#x2F;em&gt; test in production. &lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-2-1&quot;&gt;&lt;a href=&quot;#fn-2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Another reason I think local tooling degrades is the perception that it&#x27;s not actually possible to bring up the system
locally, in a way that a simulated &quot;end-to-end&quot; request could be satisfied. For the most part, I haven&#x27;t ever found
this to be true. There are tons of tools that make these setups possible: public docker images, docker compose, LocalStack,
or straight up writing a custom fake or mock. All are viable options. &lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-3-1&quot;&gt;&lt;a href=&quot;#fn-3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;My general perspective is that if a tool to mock&#x2F;simulate an external dependency doesn&#x27;t already exist and is
accessible, it&#x27;s at least worth considering building your own. This can even just be a handful of endpoints your
specific system actually needs. Recently, I needed to mock an internal API layer so I could satisfy the system
I was actually concerned about testing. All I needed to mock was 5 or 6 endpoints, from a system that actually has
dozens. Finding a minimal footprint and starting there can go a long way. In this case, the mock was just a Python
server passing some JSON back and forth, with a little bit of internal state.&lt;&#x2F;p&gt;
&lt;p&gt;Developing a habit and practice of this sets up for a lot of really interesting testing techniques way beyond
manual validation. Simulation testing is one of those techniques, and it feels like a super power once you
get the hang of it. Pierre Zemb goes into more detail on this in &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pierrezemb.fr&#x2F;posts&#x2F;why-fakes-beat-mocks-and-testcontainers&#x2F;&quot;&gt;&quot;Why Fakes Beat Mocks and Testcontainers&quot;&lt;&#x2F;a&gt;.&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-4-1&quot;&gt;&lt;a href=&quot;#fn-4&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;At some point though, faking&#x2F;mocking &lt;em&gt;can&lt;&#x2F;em&gt; get out of control. I understand the desire to not feel like we&#x27;re maintaining a
whole other system, with all of its complications and quirks. If that starts to happen, it might be worth thinking
about how to bring in that other system you&#x27;re mocking, and run it alongside your system. I think this is actually the preferred option. Especially
if that other system&#x27;s external dependencies could be mocked with a smaller footprint.&lt;&#x2F;p&gt;
&lt;p&gt;In the worst case, build what you can. It&#x27;s OK to scope something down. A little bit goes a long way here and
there&#x27;s always some creativity involved. We make the perfect the enemy of the good far too often.&lt;&#x2F;p&gt;
&lt;p&gt;I want to be careful in setting the expectation here. I don&#x27;t think I should be able to run a system &lt;em&gt;at scale&lt;&#x2F;em&gt;
on my laptop. I&#x27;m not going to run Google&#x27;s primary search infrastructure and recreate a production environment
that serves billions of users. But, I do hope I would be able to at least run a minimally working version of it
(or some critical component of it), in a way that approximates useful results. If I change some aspect of the code,
I should be able to see the impact of that change.&lt;&#x2F;p&gt;
&lt;p&gt;Just like we consider failing unit tests to be a blocker for getting to production, I think we need to start
considering a broken local setup to be a blocker. Certainly not in the absolute sense. If you need to ship a
fix to production &lt;em&gt;right now&lt;&#x2F;em&gt;, please, do it. But, take the time to come back and tidy up. Under normal working
conditions, we should not neglect the workspace right in front of us.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;I have also come across situations where even compiling locally is too much to ask. At some point, the
standard advice becomes &quot;just push it to CI&quot;. This is probably the worst situation to be in. &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;I agree with a lot of the conclusions in that article. And, at the time of this writing, Charity&#x27;s article is nearly 7 years old. Since then, there has been a lot of
development in deterministic simulation testing (DST), which &lt;em&gt;can&lt;&#x2F;em&gt; actually bring the chaos of a production environment
to an environment where there&#x27;s truly no risk. (See &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;antithesis.com&#x2F;&quot;&gt;Antithesis&lt;&#x2F;a&gt;)
Also, DST tends to be far more aggressive than any real production environment.
I still think her fundamental conclusions are sound, but new tooling improves the pre-prod testing story significantly. Ensuring a system has strong and stable
tooling for local testing actually sets it up nicely for simulation testing, which is a big reason why I&#x27;m advocating for it here. &lt;a href=&quot;#fr-2-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;Yes, I am aware that LocalStack has done some weird things recently, with regards to access and pricing.
I don&#x27;t quite know what to make of it, but I remain hopeful that good tooling continues to be developed and is accessible to the broadest possible audience. &lt;a href=&quot;#fr-3-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-4&quot;&gt;
&lt;p&gt;Pierre specifically uses the term &quot;fake&quot; instead of &quot;mock&quot; to describe this technique. I think I would prefer the term
&quot;simulated&quot;, since it&#x27;s so useful in the context of simulation, but &quot;fake&quot; works as well. The term mock might have
some baggage associated with it, depending on who you&#x27;re talking to. Whatever the term is for what I&#x27;m describing here,
the main thing is to be on the same page with your colleagues. &lt;a href=&quot;#fr-4-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Static Allocation with Zig</title>
        <published>2025-12-29T00:00:00+00:00</published>
        <updated>2025-12-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Nick Miller
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nickmonad.blog/2025/static-allocation-with-zig-kv/"/>
        <id>https://nickmonad.blog/2025/static-allocation-with-zig-kv/</id>
        
        <content type="html" xml:base="https://nickmonad.blog/2025/static-allocation-with-zig-kv/">&lt;p&gt;Over the past few months I&#x27;ve been chipping away at a small Redis-compatible key&#x2F;value server called
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nickmonad&#x2F;kv&quot;&gt;&lt;code&gt;kv&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;. The goal is to have something (mostly) production-ready, while implementing
only a small subset of commands. The world doesn&#x27;t necessarily need another key&#x2F;value store, I&#x27;m just interested in
implementing it in Zig and learning about some new (to me) techniques for systems programming.&lt;&#x2F;p&gt;
&lt;p&gt;One of those techniques is static memory allocation during initialization. The idea here is that all memory is requested
and allocated from the OS &lt;em&gt;at startup&lt;&#x2F;em&gt;, and held until termination. I first heard about this while learning about
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tigerbeetle.com&#x2F;&quot;&gt;TigerBeetle&lt;&#x2F;a&gt;, and they reference it explicitly in their development style guide dubbed &quot;TigerStyle&quot;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;All memory must be statically allocated at startup. &lt;strong&gt;No memory may be dynamically allocated (or freed and reallocated)
after initialization.&lt;&#x2F;strong&gt; This avoids unpredictable behavior that can significantly affect performance, and avoids use-after-free.
As a second-order effect, it is our experience that this also makes for more efficient, simpler designs that are more
performant and easier to maintain and reason about, compared to designs that do not consider all possible memory usage
patterns upfront as part of the design.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tigerbeetle&#x2F;tigerbeetle&#x2F;blob&#x2F;main&#x2F;docs&#x2F;TIGER_STYLE.md&quot;&gt;TigerStyle&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Although, this isn&#x27;t as straightforward as it might sound at first. The first question that comes to mind might be:
&quot;How much memory do I allocate?&quot; Of course, the answer depends on the system. If we&#x27;re writing a server, how many
concurrent connections do we allow? How much space is each connection allowed to work with? How much data do we expect
to process at any given time? Are there limits in response size? Do we need all the data at once, or can it streamed in
some fashion?&lt;&#x2F;p&gt;
&lt;p&gt;These are all questions that depend on the nature of the system and the context in which it will operate. I believe
that going through the exercise of answering these questions is ultimately a good thing, as it seems to have a strong
possibility of resulting in more stable systems, and forces us to understand the nature of our program at a deeper level.&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;On the language front, I feel like Zig is currently the best option out there for doing this with &lt;em&gt;relative&lt;&#x2F;em&gt; ease,
considering its design choices around explicit memory allocation and the &lt;code&gt;std.mem.Allocator&lt;&#x2F;code&gt; interface, which allows
the standard library to ship with a variety of different allocators.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s take a look at how we can manage static allocation in &lt;code&gt;kv&lt;&#x2F;code&gt;, considering three areas of request handling in
sequence: connection handling, command parsing, and key&#x2F;value storage.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A lot of this is pretty new to me, and I&#x27;m still wrestling with all these concepts. (And learning Zig!) I&#x27;m sure
there are better ways of handling this stuff. I&#x27;m presenting this as one possible implementation completed as a
learning exercise. I&#x27;ll speak more about the trade-offs and where I think it can go further at the end of this post.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;connection-handling&quot;&gt;Connection Handling&lt;&#x2F;h2&gt;
&lt;p&gt;The first thing we have to consider is how data comes into the system, which we&#x27;ll maintain through the concept
of a &lt;code&gt;Connection&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;A connection represents the communication to a particular client that wants to access the key&#x2F;value store.
Since we&#x27;re using &lt;code&gt;io_uring&lt;&#x2F;code&gt; for asynchronous I&#x2F;O, we have to keep some information around through the life-cycle of
a request, so the kernel can use it. The space for that information is what we&#x27;ll statically allocate and re-use across
different connections as they come and go.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;Connection = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;completion&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Completion &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;undefined&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;posix.socket_t &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;undefined&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;	
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;recv_buffer&lt;&#x2F;span&gt;&lt;span&gt;: *&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ByteArray&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;send_buffer&lt;&#x2F;span&gt;&lt;span&gt;: *&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ByteArray&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Connections also must maintain something called a &quot;completion&quot;. This detail is related to integration with &lt;code&gt;io_uring&lt;&#x2F;code&gt;,
the full details of which are outside the scope of this post. There are some good resources
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unixism.net&#x2F;loti&#x2F;index.html&quot;&gt;here&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man7&#x2F;io_uring.7.html&quot;&gt;here&lt;&#x2F;a&gt;.
I also took some inspiration from TigerBeetle&#x27;s &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tigerbeetle&#x2F;tigerbeetle&#x2F;blob&#x2F;522c0b9d15b112b20c57903987f140f1c16f627b&#x2F;src&#x2F;io&#x2F;linux.zig&quot;&gt;IO module&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;During initialization, we create three pools: one for the Connection structs themselves, one for receive buffers
(requests), and one for send buffers (responses). When a request comes in to the server, a Connection is pulled
from a &lt;code&gt;std.heap.MemoryPool&lt;&#x2F;code&gt;, and then two buffers are associated with that &lt;code&gt;Connection&lt;&#x2F;code&gt;. The buffers are
implemented as &lt;code&gt;ByteArray&lt;&#x2F;code&gt; structs, which are in turn allocated as part of a &lt;code&gt;ByteArrayPool&lt;&#x2F;code&gt;. The &lt;code&gt;ByteArrayPool&lt;&#x2F;code&gt;
is custom and uses a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Free_list&quot;&gt;free list&lt;&#x2F;a&gt; to keep track of which buffers are available
to reserve for a new connection.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;ConnectionPool = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; Pool = std.heap.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MemoryPoolExtra&lt;&#x2F;span&gt;&lt;span&gt;(Connection, .{ .growable = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;recv_buffers&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ByteArrayPool&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;send_buffers&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ByteArrayPool&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;connections&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Pool&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;init&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Config&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;gpa&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;std.mem.Allocator&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    ) !&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ConnectionPool &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; allocation = config.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;allocation&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; recv_size = allocation.connection_recv_size;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; send_size = allocation.connection_send_size;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; pool = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;try&lt;&#x2F;span&gt;&lt;span&gt; Pool.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;initPreheated&lt;&#x2F;span&gt;&lt;span&gt;(gpa, config.connections_max);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; recv_buffers = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;try&lt;&#x2F;span&gt;&lt;span&gt; ByteArrayPool.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;init&lt;&#x2F;span&gt;&lt;span&gt;(gpa, config.connections_max, recv_size);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; send_buffers = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;try&lt;&#x2F;span&gt;&lt;span&gt; ByteArrayPool.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;init&lt;&#x2F;span&gt;&lt;span&gt;(gpa, config.connections_max, send_size);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; .{
&lt;&#x2F;span&gt;&lt;span&gt;            .recv_buffers = recv_buffers,
&lt;&#x2F;span&gt;&lt;span&gt;            .send_buffers = send_buffers,
&lt;&#x2F;span&gt;&lt;span&gt;            .connections = pool,
&lt;&#x2F;span&gt;&lt;span&gt;        };
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;	
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;kv-connection-pool.png&quot; alt=&quot;A diagram showing how the kv connection pool works. A connection has two buffers, one for receive and one for send. Each come from a separate pool.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;At runtime, connections are created and destroyed (marked as available) using these pools and no actual allocation
needs to happen. If no &lt;code&gt;Connection&lt;&#x2F;code&gt; is available in the pool, the request is rejected and the client will have to try again.&lt;&#x2F;p&gt;
&lt;p&gt;This does mean that the server must be configured with an upper limit on the number of connections. Each connection
must also have a limit on how much data it can receive and send.&lt;&#x2F;p&gt;
&lt;p&gt;At first this might seem limiting, but in practice, it creates a more robust system. Databases in particular will
enforce a limit on the number of active connections for that exact reason! For a backend, networked system like &lt;code&gt;kv&lt;&#x2F;code&gt;,
I would say something like 1000 active connections is a pretty reasonable limit. For a public facing system you&#x27;d
likely want more. Of course, this should all be configurable by the user.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;Config&lt;&#x2F;code&gt; struct given to the &lt;code&gt;ConnectionPool&lt;&#x2F;code&gt; represents these user-configured options. Note that the &lt;code&gt;Config&lt;&#x2F;code&gt;
struct has a method &lt;code&gt;.allocation()&lt;&#x2F;code&gt; which is computed after the options have been set. In this case,
the &lt;code&gt;connection_recv_size&lt;&#x2F;code&gt; and &lt;code&gt;connection_send_size&lt;&#x2F;code&gt; depend on other options, such as &lt;code&gt;config.key_count&lt;&#x2F;code&gt; and
&lt;code&gt;config.key_size_max&lt;&#x2F;code&gt;. We&#x27;ll revisit those later.&lt;&#x2F;p&gt;
&lt;p&gt;Now that data can get into the system, the next step is to parse out Redis commands.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;command-parsing&quot;&gt;Command Parsing&lt;&#x2F;h2&gt;
&lt;p&gt;In an attempt to be compatible with Redis (at least a very small subset of it), &lt;code&gt;kv&lt;&#x2F;code&gt; has to parse incoming commands
following the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;redis.io&#x2F;docs&#x2F;latest&#x2F;develop&#x2F;reference&#x2F;protocol-spec&#x2F;&quot;&gt;Redis serialization protocol&lt;&#x2F;a&gt; (&quot;RESP&quot;) format.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s an example of an incoming &lt;code&gt;GET key&lt;&#x2F;code&gt; command.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I won&#x27;t go into detail on how these commands are structured, the RESP document will do a much better job there.
Basically, what we&#x27;re looking at is &quot;Here&#x27;s an array with 2 elements. The first element has 3 characters,
with the content &lt;code&gt;GET&lt;&#x2F;code&gt; and the second element has 3 characters, with the content &lt;code&gt;key&lt;&#x2F;code&gt;.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;In order to parse this command, we need to look at the buffer that contains the request data, create some kind of
iterator over that buffer, and split each entry on the CRLF &lt;code&gt;\r\n&lt;&#x2F;code&gt; byte sequence. Here&#x27;s the signature for &lt;code&gt;parse&lt;&#x2F;code&gt;,
the function that does just that.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Config&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;alloc&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;std.mem.Allocator&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;buf&lt;&#x2F;span&gt;&lt;span&gt;: []&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const u8&lt;&#x2F;span&gt;&lt;span&gt;) !&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Command
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The allocator is used to create some book-keeping structure as we parse through the command. We need to create a list
of &lt;code&gt;[]const u8&lt;&#x2F;code&gt; slices that points into the whole buffer and then is given to a command&#x27;s &lt;code&gt;parse()&lt;&#x2F;code&gt; function, once
we know the command. This has the benefit of being a &quot;zero copy&quot; approach to parsing. No request data needs to be copied,
only pointed to.&lt;&#x2F;p&gt;
&lt;p&gt;Zig&#x27;s &lt;code&gt;std.heap.FixedBufferAllocator&lt;&#x2F;code&gt; is perfect for this kind of operation. During initialization, we ask for buffer
space from a general purpose allocator, and pass it to the &lt;code&gt;FixedBufferAllocator&lt;&#x2F;code&gt;. This allocator works as &quot;bump&quot; allocator,
where each internal allocation happens in a linear fashion, up to the amount of available space. The trade-off here is
that memory allocated within the fixed buffer can&#x27;t be free&#x27;d directly. Instead, the entire buffer is reset after use,
which simply resets an index back to &lt;code&gt;0&lt;&#x2F;code&gt;. (Just about as cheap as an operation can get!)&lt;&#x2F;p&gt;
&lt;p&gt;Since our server is single-threaded&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-2-1&quot;&gt;&lt;a href=&quot;#fn-2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; and processes one request at a time, we can re-use this &lt;code&gt;FixedBufferAllocator&lt;&#x2F;code&gt;
across every request. After the request is processed, the response is copied to a &lt;code&gt;Writer&lt;&#x2F;code&gt; object backed by the
connection&#x27;s &lt;code&gt;send&lt;&#x2F;code&gt; buffer and the &lt;code&gt;FixedBufferAllocator&lt;&#x2F;code&gt; is reset for the next request.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;kv-parsing-state.png&quot; alt=&quot;A diagram showing how parsing state works in kv.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Knowing how much space to give the &lt;code&gt;FixedBufferAllocator&lt;&#x2F;code&gt; depends again on our system configuration. We need space for
the &lt;code&gt;ArrayList&lt;&#x2F;code&gt; of parsed command items, and space for any copied list items that are written back as a response during
command execution. Parsing must be able to support the largest possible command (a list &lt;code&gt;PUSH&lt;&#x2F;code&gt; of maximum size&#x2F;length) and
copying has to support the largest possible response (again, a maximally sized list).&lt;&#x2F;p&gt;
&lt;p&gt;Copying has the extra consideration that we have to actually store the copied list items, which are duplicated when
read from the key&#x2F;value store. During parsing, we just need to keep &lt;em&gt;slices&lt;&#x2F;em&gt; into the request buffer. For the copied
items, we need to keep a list of slices that point to the items, and the items themselves. As long as we give the
&lt;code&gt;FixedBufferAllocator&lt;&#x2F;code&gt; space, we can use it for all these (sub-)allocations.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub const &lt;&#x2F;span&gt;&lt;span&gt;Runner = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Config&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fba&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;std.heap.FixedBufferAllocator&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;kv&lt;&#x2F;span&gt;&lt;span&gt;: *&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Store&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;init&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Config&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;gpa&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;std.mem.Allocator&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;kv&lt;&#x2F;span&gt;&lt;span&gt;: *&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Store&lt;&#x2F;span&gt;&lt;span&gt;) !&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Runner &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; L = config.list_length_max;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; V = config.val_size_max;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; ArrayList([]const u8) of largest possible command.
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; &amp;quot;[L&#x2F;R]PUSH list item1 item2 ... itemL&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; parse_cap = (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;+ L);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;parse_size&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u64 &lt;&#x2F;span&gt;&lt;span&gt;= (parse_cap * &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;@sizeOf&lt;&#x2F;span&gt;&lt;span&gt;([]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; u8));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; ArrayList([]const u8) pointing to duplicated values.
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; copy_size = (L * &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;@sizeOf&lt;&#x2F;span&gt;&lt;span&gt;([]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; u8));
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; copy_data = (L * V);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fba_size&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u64 &lt;&#x2F;span&gt;&lt;span&gt;= parse_size + copy_size + copy_data;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; buffer = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;try&lt;&#x2F;span&gt;&lt;span&gt; gpa.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;alloc&lt;&#x2F;span&gt;&lt;span&gt;(u8, fba_size);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; fba = std.heap.FixedBufferAllocator.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;init&lt;&#x2F;span&gt;&lt;span&gt;(buffer);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; .{
&lt;&#x2F;span&gt;&lt;span&gt;            .config = config,
&lt;&#x2F;span&gt;&lt;span&gt;            .fba = fba,
&lt;&#x2F;span&gt;&lt;span&gt;            .kv = kv,
&lt;&#x2F;span&gt;&lt;span&gt;        };
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;kv-fixed-buffer-space.png&quot; alt=&quot;A diagram showing how the Fixed Buffer Allocator is utilized in kv. The first section is the parse array, the second is the copy array, and the third is copy data.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The underlying &lt;code&gt;Store&lt;&#x2F;code&gt; will use the &lt;code&gt;FixedBufferAllocator&lt;&#x2F;code&gt; to allocate an &lt;code&gt;ArrayList&lt;&#x2F;code&gt; of fixed capacity
(determined by &lt;code&gt;config.list_length_max&lt;&#x2F;code&gt;) and then use the remaining space in the allocator for the copied data.&lt;&#x2F;p&gt;
&lt;p&gt;Hopefully all is clear so far! Now we can move on to the core of the system: key&#x2F;value storage.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;key-value-storage&quot;&gt;Key&#x2F;Value Storage&lt;&#x2F;h2&gt;
&lt;p&gt;Perhaps obviously, the fundamental data structure in &lt;code&gt;kv&lt;&#x2F;code&gt; is a hash map, used to associate user provided keys with
user provided values.&lt;&#x2F;p&gt;
&lt;p&gt;Without looking too closely at the standard library, if you grab one of the provided hash map implementations,
it will accept a &lt;code&gt;std.mem.Allocator&lt;&#x2F;code&gt; and hold onto the allocator for the lifetime of the map. When a key&#x2F;value pair
is added to the map, it will use that same allocator and request the appropriate amount of memory to store that data.
This won&#x27;t work for our case though, since we need to control allocation prior to adding any data to the map.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, Zig also provides an &quot;unmanaged&quot; version of a hash map. Generally, these unmanaged versions of data
structures in the standard library mean that an allocator is not held by the structure itself. It&#x27;s up to us to provide
that allocator when needed. A cool trick we can play with an unmanaged map is ask it to ensure it has enough capacity
up front, and then &quot;assume&quot; that capacity during runtime when adding data to it. The hashing and internal details are
still handled by the map.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;: std.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;StringHashMapUnmanaged&lt;&#x2F;span&gt;&lt;span&gt;(Value) =&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt; .empty&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;try&lt;&#x2F;span&gt;&lt;span&gt; map.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ensureTotalCapacity&lt;&#x2F;span&gt;&lt;span&gt;(gpa, capacity);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;ensure&lt;&#x2F;code&gt; operation can fail with &lt;code&gt;error.OutOfMemory&lt;&#x2F;code&gt;, which is OK during initialization. But, assuming this succeeds,
we no longer need to pass an allocator when we store data in the map.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span&gt;store.map.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;putAssumeCapacity&lt;&#x2F;span&gt;&lt;span&gt;(key, value);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This &lt;em&gt;could&lt;&#x2F;em&gt; in theory fail, in which case there would an assertion failure. It&#x27;s ultimately up to us to check against
the map&#x27;s available capacity before calling this function. But, again, no allocation is required.&lt;&#x2F;p&gt;
&lt;p&gt;Since the map itself doesn&#x27;t do any allocation at runtime, we have to provide space for incoming keys and values. We&#x27;ll
reuse the same &lt;code&gt;ByteArrayPool&lt;&#x2F;code&gt; implementation that we used for connection buffers. Basically, we have a big space
allocated for keys and values and the hash map just maintains an association of pointers from keys to values.
The key&#x2F;value data isn&#x27;t literally &lt;em&gt;stored&lt;&#x2F;em&gt; &quot;in the map.&quot; The allocation that happens in &lt;code&gt;ensureTotalCapacity&lt;&#x2F;code&gt; is for
the internal book-keeping structure of the map, not for the user data.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;navigating-the-map&quot;&gt;Navigating the map&lt;&#x2F;h3&gt;
&lt;p&gt;At the highest level, the primary challenge with storing keys and values in a statically allocated map is that we
could get poor utilization of the allocated space, especially when we need to support keys pointed at lists as values.&lt;&#x2F;p&gt;
&lt;p&gt;To illustrate this, let&#x27;s say our map is configured to allocate space for 5 keys and 5 values. If each key maps to one
value, we get perfect utilization. At the other extreme, if one key maps to a value containing a list of 5 elements,
we have to use all the allocated value space for this one key, preventing other keys from using any value space,
causing the map to be &quot;biased&quot;. The store wouldn&#x27;t be able to hold any more key&#x2F;value pairs, even though there is
allocated memory &quot;on the table.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;kv-map-utilization.png&quot; alt=&quot;A diagram showing three different possibilities of how allocated memory can be used in a static hash map.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Basically, the only way to mitigate this is to make sure there&#x27;s enough allocated space for &lt;em&gt;every&lt;&#x2F;em&gt; key to hold a
list of maximum size. This definitely inflates the amount of space we have to allocate, but the alternative is a
system that doesn&#x27;t support its configured properties. Every key must be &lt;em&gt;able&lt;&#x2F;em&gt; to store a list of maximum size,
even if they don&#x27;t during actual use.&lt;&#x2F;p&gt;
&lt;p&gt;Another issue with static allocation in the context of a map is dealing with map deletions. Our
&lt;code&gt;std.StringHashMapUnmanaged&lt;&#x2F;code&gt; structure uses open-addressing and linear probing to place keys in the map when hash
collisions occur. Deletions are tricky because they can break the map&#x27;s ability to know if a key is actually present
in the map. To handle this, a &quot;tombstone&quot; technique is used to mark a space as logically (but not physically) deleted
in order to preserve accurate lookups.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a lot more to figure out here, but it&#x27;s my understanding that a map will have to periodically rehash the
keys in order to reclaim space if too many tombstones pile up. When this occurs is still a bit of mystery to me.
If it occurs when the map needs to grow to accommodate more key&#x2F;value pairs, we&#x27;ll never actually trigger that
condition in a static context. If it occurs at some other point, based on number of keys compared to capacity,
perhaps that could work. Or maybe, it&#x27;s up to us to call &lt;code&gt;rehash()&lt;&#x2F;code&gt; whenever it appears there is no space left,
and try the operation again.&lt;&#x2F;p&gt;
&lt;p&gt;All of this considered, I think a custom map implementation is more appropriate for the context of static allocation.
This current implementation proves the concept, but definitely leaves room for improvement!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;revisiting-allocation-size&quot;&gt;Revisiting allocation size&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have a method for statically allocating space for these three components (connections, parsing, and storage),
we can finally answer the first question: How much space do we allocate?&lt;&#x2F;p&gt;
&lt;p&gt;In this current iteration of &lt;code&gt;kv&lt;&#x2F;code&gt;, the answer can really only be determined after the fact, once configuration has
been set and all the allocations have been made. There are five options that can be configured by the user,
and two derived properties based on those options.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub const &lt;&#x2F;span&gt;&lt;span&gt;Config = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; Allocation is a calculated set of values (in bytes), based on the given configuration.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; This informs static allocation requested at initialization.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub const &lt;&#x2F;span&gt;&lt;span&gt;Allocation = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;connection_recv_size&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;connection_send_size&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; Maximum number of concurrent connections.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;connections_max&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; Key count is the number of possible keys we can store.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; Internally, the store will allocate (key_count * list_length_max) number
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; of values, such that each key could support the maximum number of list elements.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;key_count&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; The maximum allowable key size in bytes.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;key_size_max&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; The maximum allowable value size in bytes.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;val_size_max&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; The maximum allowable length for a list as a value.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;list_length_max&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;allocation&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Config&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Allocation &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; ... calculate recv and send size ....
&lt;&#x2F;span&gt;&lt;span&gt;		
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; .{
&lt;&#x2F;span&gt;&lt;span&gt;            .connection_recv_size = connection_recv_size,
&lt;&#x2F;span&gt;&lt;span&gt;            .connection_send_size = connection_send_size,
&lt;&#x2F;span&gt;&lt;span&gt;        };
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;connection_recv_size&lt;&#x2F;code&gt; and &lt;code&gt;connection_send_size&lt;&#x2F;code&gt; properties of &lt;code&gt;Allocation&lt;&#x2F;code&gt; depend on some details of the
RESP protocol, but is mostly informed by our user configuration. In the interest of wrapping this post up,
I&#x27;ll gloss over those details, and encourage you to check out &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nickmonad&#x2F;kv&#x2F;blob&#x2F;master&#x2F;src&#x2F;config.zig&quot;&gt;&lt;code&gt;src&#x2F;config.zig&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This &lt;code&gt;Config&lt;&#x2F;code&gt; struct doesn&#x27;t directly specify &lt;em&gt;every&lt;&#x2F;em&gt; aspect of allocation, but it does provide the basis for it.
Something not listed in the &lt;code&gt;Config&lt;&#x2F;code&gt; struct directly is the &quot;list item pool&quot;, part of the key&#x2F;value &lt;code&gt;Store&lt;&#x2F;code&gt; struct.
When keys point to lists as values, there is a linked list backing that value in the hash map, and we need a pool of
structs to assist in the construction and iteration of that linked list.&lt;&#x2F;p&gt;
&lt;p&gt;With some reasonable configuration options set, let&#x27;s see just how much memory we allocate!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; zig build run
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;config
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;connections_max&lt;&#x2F;span&gt;&lt;span&gt; = 1000
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;key_count&lt;&#x2F;span&gt;&lt;span&gt;       = 1000
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;key_size_max&lt;&#x2F;span&gt;&lt;span&gt;    = 1024
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;val_size_max&lt;&#x2F;span&gt;&lt;span&gt;    = 4096
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;list_length_max&lt;&#x2F;span&gt;&lt;span&gt; = 50
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;allocation
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;connection_recv_size&lt;&#x2F;span&gt;&lt;span&gt; = 206299
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;connection_send_size&lt;&#x2F;span&gt;&lt;span&gt; = 205255
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt; capacity = 2048, map size = 0, available = 1638
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;total_requested_bytes&lt;&#x2F;span&gt;&lt;span&gt; = 748213015
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ready!
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Everything here is measured in bytes, so we&#x27;re looking at approximately &lt;code&gt;750 MB&lt;&#x2F;code&gt; of memory for the given configuration.
&lt;code&gt;total_requested_bytes&lt;&#x2F;code&gt; is a feature of Zig&#x27;s &lt;code&gt;std.heap.DebugAllocator&lt;&#x2F;code&gt;. The exact number of bytes will be different
on each run, although it will hover around that value. I think the reason for this is how Zig requests &lt;em&gt;pages&lt;&#x2F;em&gt; of
memory from the OS. It won&#x27;t always be the same and the OS is very likely doing some fancy book-keeping of its own.&lt;&#x2F;p&gt;
&lt;p&gt;If you play around with the configuration options and see how &lt;code&gt;total_requested_bytes&lt;&#x2F;code&gt; changes, it might be
surprising just how much memory is allocated up-front, before any of it is actually used! For example, if we
double &lt;code&gt;val_size_max&lt;&#x2F;code&gt; to &lt;code&gt;8192&lt;&#x2F;code&gt; and &lt;code&gt;list_length_max&lt;&#x2F;code&gt; to &lt;code&gt;100&lt;&#x2F;code&gt;, we&#x27;re looking at about &lt;code&gt;2.8 GB&lt;&#x2F;code&gt; of allocated memory.&lt;&#x2F;p&gt;
&lt;p&gt;In the context of modern servers, this isn&#x27;t a lot, but it can quickly grow as we adjust these parameters.
Should we be asking ourselves: Is this inefficient? What if we don&#x27;t use all that memory?&lt;&#x2F;p&gt;
&lt;p&gt;Like all good engineering decisions, we have to consider them in the context of the problem we&#x27;re trying to
solve, and the guarantees we expect from our systems. With this design, ensuring that each request and each
key&#x2F;value pair &lt;em&gt;can&lt;&#x2F;em&gt; utilize the maximum configured space, seems like a worthy trade-off to make.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;Like most projects, this one took a lot longer than I expected! Trying to incorporate both &lt;code&gt;io_uring&lt;&#x2F;code&gt; and
static allocation was something I had never done before, but I&#x27;m pretty happy with the result.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m looking forward to improving the internal hash map to better fit a static context, consider alternative
allocator implementations to improve memory utilization, and incorporate fuzz testing to find the limits of the system.&lt;&#x2F;p&gt;
&lt;p&gt;Checkout the code on &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nickmonad&#x2F;kv&quot;&gt;GitHub&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;notes&quot;&gt;Notes&lt;&#x2F;h3&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;At the risk of stating the obvious, these limits can (and likely should) be configured at runtime by the user.
These aren&#x27;t values that have to be set at compile time and enforced upon all users in every context,
although some might be. Again, it depends on &lt;em&gt;which&lt;&#x2F;em&gt; part of the system is using the memory.
The point is that once the program starts it will allocate memory, but after that, it does not. &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;Going with a single-threaded design simplifies a lot! Even though processing in &lt;code&gt;kv&lt;&#x2F;code&gt; is single-threaded,
it still enjoys the benefits of I&#x2F;O concurrency via &lt;code&gt;io_uring&lt;&#x2F;code&gt;. The kernel handles writing responses back out to
clients and waiting for that operation to complete, so we don&#x27;t have to worry (as much) about slow clients. &lt;a href=&quot;#fr-2-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Advent of Performance: Go Edition</title>
        <published>2024-06-03T00:00:00+00:00</published>
        <updated>2024-06-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Nick Miller
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nickmonad.blog/2024/advent-of-performance-go-edition/"/>
        <id>https://nickmonad.blog/2024/advent-of-performance-go-edition/</id>
        
        <content type="html" xml:base="https://nickmonad.blog/2024/advent-of-performance-go-edition/">&lt;p&gt;Every so often I like to spend some time working on &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;adventofcode.com&#x2F;&quot;&gt;Advent of Code&lt;&#x2F;a&gt; problems when I need
a break from my primary projects. It&#x27;s also a great way to learn a new language, or sharpen skills in one I already know.&lt;&#x2F;p&gt;
&lt;p&gt;Another fun angle is to spend time making solutions fast. Usually, they are &quot;fast enough&quot; for the purposes of solving
the problem and moving on, just to get the gist of how to think algorithmically. But, in some cases, there is an obvious
slow down when going from part 1 to part 2 of a problem statement, which usually means there&#x27;s a faster alternative.
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;adventofcode.com&#x2F;2015&#x2F;day&#x2F;4&quot;&gt;Day 4&lt;&#x2F;a&gt; from the 2015 problem set provided exactly that opportunity.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mining-adventcoin&quot;&gt;Mining AdventCoin&lt;&#x2F;h2&gt;
&lt;p&gt;The problem statement for this day is essentially a simplified example of how &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.bitcoin.it&#x2F;wiki&#x2F;Mining&quot;&gt;bitcoin mining&lt;&#x2F;a&gt;
works, by finding a hash collision. Given some input string (e.g. &lt;code&gt;yzbqklnj&lt;&#x2F;code&gt;), the goal is to find some &quot;nonce&quot; integer,
that when appended to the input string and hashed (using MD5), the resulting hash will have some number of leading zeros.&lt;&#x2F;p&gt;
&lt;p&gt;For example,&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If your secret key is &lt;code&gt;abcdef&lt;&#x2F;code&gt;, the answer is &lt;code&gt;609043&lt;&#x2F;code&gt;, because the MD5 hash of &lt;code&gt;abcdef609043&lt;&#x2F;code&gt; starts with five zeroes (&lt;code&gt;000001dbbfa...&lt;&#x2F;code&gt;), and it is the lowest such number to do so.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Hashes can&#x27;t be &quot;reversed&quot;, meaning that the only way to do this is to try a bunch of nonces until we find a hash
with the required number of leading zeros.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, Go&#x27;s standard &lt;code&gt;crypto&lt;&#x2F;code&gt; package gives us a function to compute an MD5 hash of a byte array, so we can do this
pretty simply.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;naive&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;prefix &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;seed &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;strings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;TrimSpace&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nonce &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;preimage &lt;&#x2F;span&gt;&lt;span&gt;:= []&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;%s%d&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;seed&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nonce&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hash &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;md5&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Sum&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;preimage&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hash&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;prefix&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;			&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;break &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; found nonce
&lt;&#x2F;span&gt;&lt;span&gt;		}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nonce &lt;&#x2F;span&gt;&lt;span&gt;+= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nonce&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;check&lt;&#x2F;code&gt; function just formats the resulting hash in hex and looks at the prefix. We&#x27;ve separated it out so we can
change it later...&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hash &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;prefix &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hex &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;%x&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hash&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;strings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;HasPrefix&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hex&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;prefix&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Easy enough! We&#x27;ve solved Part 1. For part 2, we just have to run the same loop, but instead of looking
for a hash with 5 leading zeros, we have to find one with 6 leading zeros.&lt;&#x2F;p&gt;
&lt;p&gt;Updating our input and running it again... Oh. Well. It definitely takes longer. Noticeably longer. In these situations,
you always wonder if something else went wrong and you&#x27;ve introduced an infinite loop. In this case, we definitely get correct output,
but it&#x27;s just slow.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; go build&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -o&lt;&#x2F;span&gt;&lt;span&gt; day4 solutions&#x2F;day4&#x2F;main.go
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# find hash with 5 leading zeros (00000)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;day4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -part&lt;&#x2F;span&gt;&lt;span&gt; 1 input&#x2F;day4
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;282749
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;time:&lt;&#x2F;span&gt;&lt;span&gt; 134.816113ms
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# find hash with 6 leading zeros (000000)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;day4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -part&lt;&#x2F;span&gt;&lt;span&gt; 2 input&#x2F;day4
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;9962624
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;time:&lt;&#x2F;span&gt;&lt;span&gt; 4.029076709s
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Technically, nothing is &quot;wrong&quot; with our algorithm here. It&#x27;s just dealing with a much larger space of possibilities.
Generally speaking, the more specific your target hash is, the longer it&#x27;s going to take, effectively trying random
nonce values until you find the needle in a ridiculously large haystack.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, Go is known for making concurrency relatively easy, so let&#x27;s just spin up a goroutine for each CPU core
and try as many different nonces as we can simultaneously.&lt;&#x2F;p&gt;
&lt;p&gt;Originally, I had thought I would have &lt;code&gt;runtime.NumCPU()&lt;&#x2F;code&gt; goroutines as &quot;workers&quot; crunching on our hash function, with
&lt;em&gt;another&lt;&#x2F;em&gt; goroutine that would act as a &quot;ticket counter&quot;, giving out the next nonce over a channel when a worker needed
a new one after failing to find the desired hash prefix.&lt;&#x2F;p&gt;
&lt;p&gt;Although, it didn&#x27;t actually work. At least not in the way I expected. We got the right number, but it took &lt;em&gt;even longer&lt;&#x2F;em&gt; than
the naive solution when looking for 6 leading zeros.&lt;&#x2F;p&gt;
&lt;p&gt;I figured that the overhead of the coordination between all the goroutines was just too much to be worth the
parallel effort. The hashing function is so quick that it was likely the workers were putting too much contention
on the nonce channel, too quickly, causing a lot of runtime context switching and blocking.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;actually-making-it-fast&quot;&gt;Actually making it fast&lt;&#x2F;h2&gt;
&lt;p&gt;I knew that parallelization was definitely the way to go here because each hashing function is completely independent of the
other. No state needs to be carried over between testing nonce &lt;code&gt;32535&lt;&#x2F;code&gt; and nonce &lt;code&gt;871279&lt;&#x2F;code&gt;. Ultimately, &lt;em&gt;that&lt;&#x2F;em&gt; was the key insight
needed to make this work.&lt;&#x2F;p&gt;
&lt;p&gt;If we consider each worker in isolation, we can define a simple formula for determining the next nonce in its sequence to test.
Just make it count upwards, skipping by some fixed amount each iteration. In this case, the number of goroutines. This way, each worker
tests a unique sequence of integers, but doesn&#x27;t overlap with any other worker.&lt;&#x2F;p&gt;
&lt;p&gt;For simplicity, let&#x27;s assume we have 4 workers. If they each count by 4, starting from a different place, they&#x27;ll never overlap.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;worker 0 -&amp;gt; 0, 4, 8, 12, ...
&lt;&#x2F;span&gt;&lt;span&gt;worker 1 -&amp;gt; 1, 5, 9, 13, ...
&lt;&#x2F;span&gt;&lt;span&gt;worker 2 -&amp;gt; 2, 6, 10, 14, ...
&lt;&#x2F;span&gt;&lt;span&gt;worker 3 -&amp;gt; 3, 7, 11, 15, ...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each worker can calculate this sequence with 2 constants, the &quot;base&quot; (starting point), and &quot;N&quot;, the number of workers.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;nonce = base + (i * N)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If &lt;code&gt;i&lt;&#x2F;code&gt; increases indefinitely until we either find a solution, or are told to stop looking, we can parallelize this
and eliminate virtually all coordination between each worker, until it needs to notify us of a solution.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;optimized&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;prefix &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;seed &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;strings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;TrimSpace&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;numWorkers &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;runtime&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;NumCPU&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; wait group ensures all goroutines clean up after cancellation
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;wg sync&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;WaitGroup
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; cancellable context is cancelled once a solution is found
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cancel &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;WithCancel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Background&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; given to each worker
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;solution &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;chan int&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; spawn a worker for each CPU core
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;b &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;b &lt;&#x2F;span&gt;&lt;span&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;numWorkers&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;++ {
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;wg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;go func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;n &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;			&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;defer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;wg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Done&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;			&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;			&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;				&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;select &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;				&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Done&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;					&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;				&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;					&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nonce &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;base &lt;&#x2F;span&gt;&lt;span&gt;+ (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; independently calculate next nonce in sequence
&lt;&#x2F;span&gt;&lt;span&gt;					&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;preimage &lt;&#x2F;span&gt;&lt;span&gt;:= []&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;%s%d&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;seed&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nonce&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;					&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hash &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;md5&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Sum&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;preimage&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;					&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hash&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;prefix&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;						&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;solution &lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nonce
&lt;&#x2F;span&gt;&lt;span&gt;						&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return
&lt;&#x2F;span&gt;&lt;span&gt;					}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;					&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;				}
&lt;&#x2F;span&gt;&lt;span&gt;			}
&lt;&#x2F;span&gt;&lt;span&gt;		}(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;numWorkers&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;winner &lt;&#x2F;span&gt;&lt;span&gt;:= &amp;lt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;solution
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cancel&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;wg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Wait&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;winner&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And the result...&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;day4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -part&lt;&#x2F;span&gt;&lt;span&gt; 2 input&#x2F;day4
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;9962624
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;time:&lt;&#x2F;span&gt;&lt;span&gt; 3.976446064s
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;day4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -part&lt;&#x2F;span&gt;&lt;span&gt; 2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -optimized&lt;&#x2F;span&gt;&lt;span&gt; input&#x2F;day4
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;9962624
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;time:&lt;&#x2F;span&gt;&lt;span&gt; 699.66703ms
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Boom. Huge difference!&lt;&#x2F;p&gt;
&lt;p&gt;This solution actually helps improve performance in part 1 as well.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;day4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -part&lt;&#x2F;span&gt;&lt;span&gt; 1 input&#x2F;day4
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;282749
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;time:&lt;&#x2F;span&gt;&lt;span&gt; 119.919216ms
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;day4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -part&lt;&#x2F;span&gt;&lt;span&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -optimized&lt;&#x2F;span&gt;&lt;span&gt; input&#x2F;day4
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;282749
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;time:&lt;&#x2F;span&gt;&lt;span&gt; 22.183944ms
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;one-more-thing&quot;&gt;One more thing!&lt;&#x2F;h2&gt;
&lt;p&gt;Remember the &lt;code&gt;check&lt;&#x2F;code&gt; function above? It encodes the resulting MD5 hash into hexadecimal and looks at that string
to see if the nonce results in a hash with the desired number of leading zeros.&lt;&#x2F;p&gt;
&lt;p&gt;Primarily leaning on &lt;code&gt;fmt.Sprintf()&lt;&#x2F;code&gt; and the hex formatting directive, it requires allocating heap memory to build the hex string, which must be done
on every iteration of the loop.&lt;&#x2F;p&gt;
&lt;p&gt;Since all we really need to do is check the leading byte values of the hash, we can perform that check
without any heap allocation at all.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Check if the given MD5 hash has the given leading number of zeros.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hash &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;leading &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;numBytes &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;leading &lt;&#x2F;span&gt;&lt;span&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;numBytes&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;++ {
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hash&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;] != &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0x00 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;			&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;		}
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;leading&lt;&#x2F;span&gt;&lt;span&gt;%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; looking for odd number of zeros...
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; since each hex character represents 4 bits, we have to use a bit shift to check our last byte
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; in the hash if the desired number of leading zeros is odd. For example, if we are looking for
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; `000` (odd number of zeros), the byte sequence _could_ be `0x00 0x01`, or `0x00 0x02`, etc...
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; In this case, we bit shift the last byte to the right by 4, and check if that result is 0,
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; ignoring 4 least significant bits.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; last index in hash to check is equal to `numBytes` because of the integer division above.
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; for example, if leading == 5, numBytes == 2 (due to integer division), and we need to
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; check the 3rd byte in the sequence (i.e. index 2)
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hash&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;numBytes&lt;&#x2F;span&gt;&lt;span&gt;] &amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;) == &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0x00
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Because a single digit of a hex value actually represents 4 bits, looking for 6 leading zeros in the resulting hash
means we look at the first 3 bytes. (&lt;code&gt;0x000000&lt;&#x2F;code&gt; split on each byte looks like &lt;code&gt;0x00 0x00 0x00&lt;&#x2F;code&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;If we need to check for an odd number of leading zeros (say, 5 like in part 1), we can lean on some integer division
and a modulo check to ensure we check the last byte in the sequence correctly. As we said, that last 0 to check only
represents 4 bits, so we can use a bit shift to ignore the 4 least significant bits of the byte, and check that result
against the &lt;code&gt;0x00&lt;&#x2F;code&gt; byte value.&lt;&#x2F;p&gt;
&lt;p&gt;Assuming we get through those checks, we&#x27;ve found a solution!&lt;&#x2F;p&gt;
&lt;p&gt;Looking at the run time, this updated version of &lt;code&gt;check&lt;&#x2F;code&gt; improves performance across the board, in both
the naive solution, and optimized solution that uses parallelization!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;day4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -part&lt;&#x2F;span&gt;&lt;span&gt; 1 input&#x2F;day4
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;282749
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;time:&lt;&#x2F;span&gt;&lt;span&gt; 55.216603ms
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;day4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -part&lt;&#x2F;span&gt;&lt;span&gt; 2 input&#x2F;day4
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;9962624
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;time:&lt;&#x2F;span&gt;&lt;span&gt; 1.818860088s
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;day4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -part&lt;&#x2F;span&gt;&lt;span&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -optimized&lt;&#x2F;span&gt;&lt;span&gt; input&#x2F;day4
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;282749
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;time:&lt;&#x2F;span&gt;&lt;span&gt; 11.829511ms
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;day4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -part&lt;&#x2F;span&gt;&lt;span&gt; 2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -optimized&lt;&#x2F;span&gt;&lt;span&gt; input&#x2F;day4
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;9962624
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;time:&lt;&#x2F;span&gt;&lt;span&gt; 319.841772ms
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s a table summarizing the performance improvements for both part 1 and part 2. In each case, I ran the
solutions 10 times and took the average of those reported run times, rounded to 2 decimal places.&lt;&#x2F;p&gt;
&lt;p&gt;(&quot;no alloc&quot; means the version of &lt;code&gt;check&lt;&#x2F;code&gt; that doesn&#x27;t allocate a string and encode to hex)&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Solution&lt;&#x2F;th&gt;&lt;th&gt;Version&lt;&#x2F;th&gt;&lt;th&gt;Run Time&lt;&#x2F;th&gt;&lt;th&gt;Speedup&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Part 1&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;naive&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;118.16 ms&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;1.0x&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;naive + no alloc&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;53.2 ms&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;2.2x&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;optimized&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;20.04 ms&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;5.9x&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;optimized + no alloc&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;10.2 ms&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;11.6x&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Part 2&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;naive&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;4.01 s&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;1.0x&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;naive + no alloc&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;1.81 s&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;2.2x&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;optimized&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;778.41 ms&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;5.2x&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;optimized + no alloc&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;358.53 ms&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;11.2x&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h4 id=&quot;environment&quot;&gt;Environment&lt;&#x2F;h4&gt;
&lt;p&gt;I ran these tests using Go version &lt;code&gt;go1.22.3 linux&#x2F;amd64&lt;&#x2F;code&gt;, on a ThinkPad X1 Extreme Gen 4i, rocking an
11th Gen Intel i7-11800H (16) @ 4.600GHz (reported by &lt;code&gt;neofetch&lt;&#x2F;code&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;All code for this and other solutions can be found on &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nickmonad&#x2F;advent-of-code&#x2F;&quot;&gt;GitHub&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>&quot;I think you might be a scam&quot; - A Lesson in Customer Discovery</title>
        <published>2024-03-20T00:00:00+00:00</published>
        <updated>2024-03-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Nick Miller
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nickmonad.blog/2024/i-think-you-might-be-a-scam/"/>
        <id>https://nickmonad.blog/2024/i-think-you-might-be-a-scam/</id>
        
        <content type="html" xml:base="https://nickmonad.blog/2024/i-think-you-might-be-a-scam/">&lt;p&gt;Learning how to talk to customers has been a fun aspect of building a business. Although, it is by no means &quot;easy.&quot;
Like many engineers, I struggle with this, because what we &lt;em&gt;really&lt;&#x2F;em&gt; know how to do is write code, and we think if we
just buckle down and knock it out quickly, we could validate our idea that way.&lt;&#x2F;p&gt;
&lt;p&gt;That might work for some product ideas, but I&#x27;m becoming increasingly convinced that &quot;build first&quot; approaches are a
great way to waste a bunch of time, effort, and money towards building a product nobody cares about.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m not alone in this way of thinking, and it certainly wasn&#x27;t my idea. My mentor at the startup incubator
I&#x27;m currently in encouraged me to stop building and actually talk to some customers. I definitely wasn&#x27;t
thrilled to hear that at first. I thought I had a strong sense of what I should build, but he rightfully challenged me
on those assumptions.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-mom-test&quot;&gt;The Mom Test&lt;&#x2F;h2&gt;
&lt;p&gt;I sighed and took a step back, and started to learn how to approach customer discovery. My mentor recommended
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.momtestbook.com&#x2F;&quot;&gt;The Mom Test&lt;&#x2F;a&gt;, and now I couldn&#x27;t agree more. It&#x27;s a concise little book that gives you that real, practical advice
on how to approach customer discovery, with a particular emphasis on &lt;em&gt;not biasing the prospect.&lt;&#x2F;em&gt; As Rob Fitzpatrick
outlines his approach, he details a particular mistake he finds a lot of entrepreneurs making, where they&#x27;ll pitch an
idea and immediately ask &quot;What do you think? Is it a good idea?&quot; This leads to all sorts of psychological issues
surrounding human behavior.&lt;&#x2F;p&gt;
&lt;p&gt;The basic issue is that, nobody wants to hurt your feelings. When they are really
thinking &quot;meh, I&#x27;m not sure why I would use that.&quot; Or, &quot;I really don&#x27;t want to switch products right now.&quot;, they are
telling you &quot;Yeah sounds great! Let me know when it launches.&quot; You walk away feeling like a business genius, but in
reality, they just told you what they thought you wanted to hear. This is particularly the case when the person you
are pitching your idea to is your mom. But friends and colleagues are guilty of the same thing.&lt;&#x2F;p&gt;
&lt;p&gt;So, how do we address that problem? &lt;em&gt;Just ask people questions about their lives and jobs.&lt;&#x2F;em&gt; You can start out pretty
broad, but it&#x27;s OK to narrow focus around the industry and problem you are interested in. The idea is that by having
casual conversations, you let your potential customer tell you what their problems actually are, not what you assume
them to be. By hearing the same problems come up across multiple customer calls (the amount varies), you can build way
more confidence in your solution as something people will actually buy. I can&#x27;t do the entire book justice in this post, so if this idea sounds interesting to you,
definitely pick up a copy and read it. (Not a paid promotion, I really just think this book is incredible.)&lt;&#x2F;p&gt;
&lt;p&gt;I took a lot of the advice and strategy of the book to heart and set out on finding contacts. I was able to lean on my
network for this a little bit, but I&#x27;ve also had to do some cold outreach via email. Waiting for my network to follow
up was just taking too long. My product revolves around the volunteering space and fortunately, the space is huge. There is no shortage of non-profit
organizations. I was able to email 7 solid prospects in one morning just by looking at my city&#x27;s chamber of commerce
website.&lt;&#x2F;p&gt;
&lt;p&gt;What I didn&#x27;t fully take into consideration was how my language might sound to some of those prospects. One of them
in particular responded in a way I never expected.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bitter-cold-outreach&quot;&gt;Bitter cold outreach&lt;&#x2F;h2&gt;
&lt;p&gt;I actually have no real idea how to approach cold calling, and in what was perhaps an overly enthusiastic email power
session one Friday morning, I sent this out to a prospective customer.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hello [redacted],&lt;&#x2F;p&gt;
&lt;p&gt;My name is Nick and I&#x27;m part of a startup program at Iowa State, where I&#x27;m doing research on volunteer management
and organization. I came across [redacted; org name] and am interested in learning more about your organization
and volunteer program - what works for you, and maybe some of the challenges. It seems like [redacted; org name]
has a wide range of roles volunteers can fill!&lt;&#x2F;p&gt;
&lt;p&gt;Would a member of your team have 20 or 30 minutes to chat with me over the phone sometime next week?
I have nothing to sell, I&#x27;m just interested in learning more about how your volunteer program operates.&lt;&#x2F;p&gt;
&lt;p&gt;I really appreciate your time!&lt;&#x2F;p&gt;
&lt;p&gt;Thank you,&lt;&#x2F;p&gt;
&lt;p&gt;Nick&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If you happen to be an expert in cold calling and outreach, I&#x27;m sure there are aspects of this email you may find
pretty funny.&lt;&#x2F;strong&gt; I acknowledge that and I&#x27;m looking into how to improve it. In the meantime, please bear with me.
I&#x27;m an engineer.&lt;&#x2F;p&gt;
&lt;p&gt;I fired off that email and hoped to get a response the following week. Sure enough, it came through.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hello Nick,&lt;&#x2F;p&gt;
&lt;p&gt;Thank you for reaching out to us.&lt;&#x2F;p&gt;
&lt;p&gt;Could you provide me with a little more details of what information you&#x27;re looking to gather?
Let me know and I would be happy to help if I can.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks and make it a great day.&lt;&#x2F;p&gt;
&lt;p&gt;[redacted; name]&lt;&#x2F;p&gt;
&lt;p&gt;Volunteer Services Coordinator&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Fair enough. I could see how my initial message might not be clear to somebody, and they just want to be protective
of their time. No problem.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hi [redacted],&lt;&#x2F;p&gt;
&lt;p&gt;Thank you for your response!&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m looking to learn more about your volunteer program, in terms of how it operates. Thinking about the &quot;lifecycle&quot;
of a volunteer - how they show interest, how they get approved &#x2F; onboarded, and how they schedule shifts.
I&#x27;m interested in hearing specifically how you do those kinds of things today, tools you use, and where you see some
of the challenges. I am hoping to develop new solutions for this space, but I first need to validate some of my
assumptions about the process across different organizations.&lt;&#x2F;p&gt;
&lt;p&gt;Hopefully that makes sense. Happy to address any other questions!&lt;&#x2F;p&gt;
&lt;p&gt;Thank you,&lt;&#x2F;p&gt;
&lt;p&gt;Nick&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I sent that reply at 9:00 AM, and at 4:00 PM, I received the following.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hello Nick,&lt;&#x2F;p&gt;
&lt;p&gt;I have to be honest with you... there are a couple red flags to know if this is a scam or not so I&#x27;m going to pass
on providing our information.&lt;&#x2F;p&gt;
&lt;p&gt;Thank you.&lt;&#x2F;p&gt;
&lt;p&gt;[redacted]&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;...&lt;&#x2F;p&gt;
&lt;p&gt;I was honestly shocked.&lt;&#x2F;p&gt;
&lt;p&gt;Maybe it&#x27;s just me, but if I think something is a scam, the last thing I do is reply to it.
Oh, I definitely want to - especially so I can mess with them - but I know I&#x27;m better off not engaging.&lt;&#x2F;p&gt;
&lt;p&gt;(Although, the lone &quot;Thank you.&quot; is probably my favorite part.)&lt;&#x2F;p&gt;
&lt;p&gt;In all seriousness, I expected to be ghosted by a few contacts, but I never expected to be told I might be somebody
trying to execute a scam. I re-read my messages probably a dozen times, pouring over every word, trying to figure out
what I said that was so concerning.&lt;&#x2F;p&gt;
&lt;p&gt;I did end up sending a response that I won&#x27;t show here. I wasn&#x27;t mean or snarky with them, and even apologized for
how I came across, but it&#x27;s just a little embarrassing. I probably over explained what I was trying to do, and even
mentioned things like &quot;I didn&#x27;t lead with that information (about my company and idea) because I didn&#x27;t want to bias you.&quot;
It&#x27;s absolutely the truth, but I&#x27;m sure they don&#x27;t really care at this point. Phrasing it that way might even come
across as me essentially admitting I was trying to play some scammer Jedi mind-trick I was certain would win them over,
but now that they&#x27;ve caught me, we can get to the &quot;real&quot; facts to calm their nerves.&lt;&#x2F;p&gt;
&lt;p&gt;I can laugh about this experience now, but it definitely rocked me at first. It sucks to be told something so blatantly
wrong, especially when you make the effort to be as cordial and concise as possible. It also made me wonder if the
people who ghosted me did so because I came across in a sketchy way.&lt;&#x2F;p&gt;
&lt;p&gt;Moving forward, I will definitely adjust my approach. I think the biggest lessons are,&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Don&#x27;t be afraid to say you are a software developer trying to build a new product.&lt;&#x2F;strong&gt;
Saying you have an idea isn&#x27;t the same as telling them what the idea is, and I think my prospective contact would have
appreciated knowing what exactly I was attempting to do up front. Framing it as &quot;just a conversation&quot; works for
some people, but others might find that to be a little odd.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stop talking so much like an engineer.&lt;&#x2F;strong&gt; In my follow-up response to their question about what exactly
I was looking for, I threw out words like &quot;lifecycle&quot; and started to describe the problem as I would describe
it to another engineer. I went way over the top with detail when I should have just said, &quot;Oh my gosh, I&#x27;m sorry I
wasn&#x27;t clear. I really just want to learn more about your organization so I can better understand the problems facing
volunteer organizers. I do some volunteering myself and I&#x27;m curious if other organizations are facing the same problems.&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Even with all that, you can&#x27;t win them all. People will ghost you, and they may not be very helpful.
The goal is to keep talking with people who are willing to talk with you until you stop hearing new information.
Then, go build a kick-ass product.&lt;&#x2F;p&gt;
&lt;p&gt;And if you&#x27;re going to scam anybody, find your own strategy. This one is mine.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Static Site Generators: Not Just for Blogs</title>
        <published>2023-12-11T00:00:00+00:00</published>
        <updated>2023-12-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Nick Miller
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nickmonad.blog/2023/static-site-generators-not-just-for-blogs/"/>
        <id>https://nickmonad.blog/2023/static-site-generators-not-just-for-blogs/</id>
        
        <content type="html" xml:base="https://nickmonad.blog/2023/static-site-generators-not-just-for-blogs/">&lt;p&gt;Static site generators have become popular within the technical and programming focused communities. They are fast, focused
programs that allow anybody with a little bit of HTML and CSS skill to produce a blog or product page quickly, write all their
content (typically) using &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Markdown&quot;&gt;markdown&lt;&#x2F;a&gt; in a simple folder structure, and commit it
all to git. Truly a gift and a joy to work with, compared to something like Wordpress.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.cloudflare.com&#x2F;learning&#x2F;performance&#x2F;static-site-generator&#x2F;&quot;&gt;Cloudflare&lt;&#x2F;a&gt; defines a
static site generator (SSG) as &lt;strong&gt;&quot;a tool that generates a full static HTML website based on raw data and a set of templates&quot;&lt;&#x2F;strong&gt;.
In this context, &quot;static&quot; means that the end result of the build is a set of HTML pages that don&#x27;t change when the page is
refreshed, not that those pages &lt;em&gt;never&lt;&#x2F;em&gt; change.&lt;&#x2F;p&gt;
&lt;p&gt;Static site generators are incredibly powerful tools available for modern web development and I think a lot of developers
are missing out on what they offer in terms of development time, hosting cost, and overall simplicity.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;blog-bias&quot;&gt;Blog Bias&lt;&#x2F;h2&gt;
&lt;p&gt;Blogs are common examples that come up when researching static site generators and how they can be used. This
makes a lot of sense, as once blog posts are published, they rarely change, and they need to load quickly. In the context
of a company blog, SEO is a major factor as well, since search engine crawlers need to parse content quickly.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, I think a lot of the understanding around static site generation ends here, even among web developers.
For a long time, I was in this camp as well. I knew I could use static site generation effectively for my blog site, or
a simple &quot;about&quot; page here or there, and maybe even a legal statement or privacy policy. Basically, anything where
the content could be shown in a long, continuous block of text.&lt;&#x2F;p&gt;
&lt;p&gt;This understanding makes some product pages, or pages that require laying out collections of blocks in a grid-like
structure difficult to reason about, and nearly impossible to build in some cases. If I can only store content in a big
blob of text, and each document in my repository is a single page, how would I break it up so I can spread out the
content to different sections of the page and style it appropriately? Worse yet, how would the non-technical team members
update copy on the page without having to dig around in the HTML directly?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;options-for-managing-structured-content&quot;&gt;Options for managing structured content&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m going to use &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;zola&lt;&#x2F;a&gt; as my static site generator of choice and show some examples of
creating and rendering &quot;non-blog&quot; content, in a few different ways. This is only because I&#x27;m the most familiar with it,
but there are lots of good options out there, such as &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;hugo&lt;&#x2F;a&gt;. They may or may not work in exactly
the same way, but what&#x27;s important here is taking the ideas and expanding our understanding of how we can utilize SSGs
to their full potential.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-section-and-page-metadata&quot;&gt;1. Section and Page Metadata&lt;&#x2F;h3&gt;
&lt;p&gt;Sometimes called &quot;front matter&quot;, each section or page of our site can have metadata associated with it.&lt;&#x2F;p&gt;
&lt;p&gt;Even in the case of a blog, you&#x27;ll see this metadata above the markdown content, enclosed in some kind of marker, to
separate it from the main content of the post. This would include things like &lt;code&gt;title&lt;&#x2F;code&gt;, &lt;code&gt;description&lt;&#x2F;code&gt;, &lt;code&gt;author&lt;&#x2F;code&gt;, and &lt;code&gt;date&lt;&#x2F;code&gt;,
and read by the static site generator itself to organize and template the page. The front matter of this post looks like,&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;+++
&lt;&#x2F;span&gt;&lt;span&gt;title = &amp;quot;Static Site Generators: Not Just for Blogs&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;description = &amp;quot;Static generation should be the default&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;date = 2023-12-11
&lt;&#x2F;span&gt;&lt;span&gt;[taxonomies]
&lt;&#x2F;span&gt;&lt;span&gt;tags = [ &amp;quot;web&amp;quot; ]
&lt;&#x2F;span&gt;&lt;span&gt;+++
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The easiest way to add structured data to this page would be to use the built-in &lt;code&gt;[extra]&lt;&#x2F;code&gt; field for our custom content.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;+++
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[extra]
&lt;&#x2F;span&gt;&lt;span&gt;seo_keywords = &amp;quot;please, read, my, blog&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;+++
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then, inside of the template that renders the page, we can include these keywords in our block that allows for &lt;code&gt;&amp;lt;meta&amp;gt;&lt;&#x2F;code&gt;
tag keywords to be added,&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;{% extends &amp;quot;base.html&amp;quot; %}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{% block title %}{{ page.title }}{% endblock %}
&lt;&#x2F;span&gt;&lt;span&gt;{% block seo_keywords %}{{ page.extra.seo_keywords }}{% endblock %}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;which could generate HTML that looks like this,&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;# template
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;meta &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;keywords&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{% block seo_keywords %}{% endblock seo_keywords %}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;# rendered
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;meta &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;keywords&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;please, read, my, blog&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To build upon this example, imagine instead we had a product page and we wanted to specify a list of features.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;+++
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[extra]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[[extra.features]]
&lt;&#x2F;span&gt;&lt;span&gt;headline = &amp;quot;Amazing Feature&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;description = &amp;quot;Do something amazing.&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[[extra.features]]
&lt;&#x2F;span&gt;&lt;span&gt;headline = &amp;quot;Awesome Feature&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;description = &amp;quot;Do something awesome, instead.&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;+++
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here, we have separated the content of each feature item from the display and styling of them. In our template,
we can now iterate over each of these feature items and place them however we want on the page, using the &lt;code&gt;page.extra.features&lt;&#x2F;code&gt;
array.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-data-in-templates&quot;&gt;2. Data in templates&lt;&#x2F;h3&gt;
&lt;p&gt;In many cases, it&#x27;s useful to store website copy (especially marketing copy) in a content management system (CMS),
where non-technical team members can edit and review content before it goes live on the website.&lt;&#x2F;p&gt;
&lt;p&gt;The section and page metadata approach falls a bit short, since it still requires modifying files locally and committing
them to git.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, &lt;code&gt;zola&lt;&#x2F;code&gt; offers a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;templates&#x2F;overview&#x2F;#load-data&quot;&gt;&lt;code&gt;load_data&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; function
to bring in data from a remote source, within a template.&lt;&#x2F;p&gt;
&lt;p&gt;As an example, we can build up a SaaS pricing page this way, using an endpoint in our hypothetical CMS.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;{% set token = get_env(name=&amp;quot;API_TOKEN&amp;quot;) %}
&lt;&#x2F;span&gt;&lt;span&gt;{% set data = load_data(url=&amp;quot;https:&#x2F;&#x2F;my.cms.com&#x2F;api&#x2F;data&#x2F;pricing&amp;quot;, format=&amp;quot;json&amp;quot;, headers=[&amp;quot;Accept=application&#x2F;json&amp;quot;, &amp;quot;Authorization=Bearer &amp;quot; ~ token]) %}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;h1&amp;gt;{{ data.tiers[0].name }}&amp;lt;&#x2F;h1&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;ul&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;{% for feature in data.tiers[0].features %}
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;li&amp;gt;{{ feature }}&amp;lt;&#x2F;li&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;{% endfor %}
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;ul&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The &quot;url&quot; passed to &lt;code&gt;load_data&lt;&#x2F;code&gt; can be a local file as well, if it makes sense to store that JSON locally.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This gives us the flexibility to put content somewhere else that is more accessible to a wider group of people on our team.
Any change in the CMS can be detected (hopefully via a webhook) and the site can be rebuilt, previewed and pushed to production.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Side note&lt;&#x2F;strong&gt;: static site generators are &lt;em&gt;fast&lt;&#x2F;em&gt;. This entire site builds in just under 100ms.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;3-automated-markdown-file-generation&quot;&gt;3. Automated markdown file generation&lt;&#x2F;h3&gt;
&lt;p&gt;Finally, there is always the option to generate a set of markdown files to be placed in the &lt;code&gt;content&lt;&#x2F;code&gt; directory. This is the
approach I&#x27;ve taken for the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;bips.dev&quot;&gt;bips.dev&lt;&#x2F;a&gt; site, where each BIP is a separate page, with front matter and markdown
generated from a script that uses &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pandoc.org&#x2F;&quot;&gt;&lt;code&gt;pandoc&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to translate the media wiki format to markdown.&lt;&#x2F;p&gt;
&lt;p&gt;Admittingly, it&#x27;s a bit of a hack, but it does work. Large documentation sites could be built this way with content from a
CMS. This method would be needed whenever the actual structure of the site is dictated by some other source. In the case
of &lt;code&gt;bips.dev&lt;&#x2F;code&gt;, that source is a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bitcoin&#x2F;bips&quot;&gt;public git repository&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;simple-as-the-default&quot;&gt;Simple as the default&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;With the proliferation and increasing sophistication of SSGs, I think the time has come where we can say that the &lt;em&gt;default&lt;&#x2F;em&gt;
choice of any &quot;non-application&quot; website should be static generation. Statically generated sites are faster to develop,
easier to host, and easier to reason about over the lifetime of the project.&lt;&#x2F;p&gt;
&lt;p&gt;I want to be explicit in stating what I hope is the obvious: If you are building a highly interactive application with
lots of business logic, &lt;em&gt;obviously&lt;&#x2F;em&gt; a fully featured programming language and deployment environment is going to be a better option.
I think that statically generated sites still offer some room to include interaction and business logic with client-side
Javascript (&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alpinejs.dev&#x2F;&quot;&gt;alpine.js&lt;&#x2F;a&gt; is a great candidate here), and that can always be included as part of
your templates and build output.&lt;&#x2F;p&gt;
&lt;p&gt;I would even go so far as to say that needing to have a few business logic endpoints shouldn&#x27;t completely eliminate the
possibility of using a static site generator, as you could simply have a reverse proxy forward those requests to an
application server, and just serve the static content for everything else. Some judgement would need to be exercised there,
to see if it makes sense for the use-case, but I would still take a moment to see if that model could satisfy your requirements.&lt;&#x2F;p&gt;
&lt;p&gt;On the infrastructure side, hosting a statically generated site is just about the easiest thing to do.
From a self-hosted &lt;code&gt;nginx&lt;&#x2F;code&gt; instance to a managed platform that watches for changes in your git repo and builds the latest
version of the site with automatic certificate renewal, there are tons of reliable options.&lt;&#x2F;p&gt;
&lt;p&gt;A recent website rebuild I worked on spent far more time than necessary dealing with the heavy frontend framework that was
chosen, mainly because the full scope of how static site generators could be used wasn&#x27;t known at the time.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s tempting to reach for those languages and tools, since they are familiar to a lot of developers. At some point though,
we have to use the best tool for the job, and bringing along all of React to build out a few product pages just doesn&#x27;t seem like
the way to go.&lt;&#x2F;p&gt;
&lt;p&gt;I could be way behind on this. If you&#x27;re reading and find all of it to be blatantly obvious, or something that was figured out
years ago, awesome! In my recent experience, there is still a lack of understanding around SSGs, and I hope we&#x27;ll see a renaissance
soon, where the simple choice is the default choice.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Building a nostr client [1]</title>
        <published>2023-08-31T00:00:00+00:00</published>
        <updated>2023-08-31T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Nick Miller
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nickmonad.blog/2023/building-nostr-client-index-1/"/>
        <id>https://nickmonad.blog/2023/building-nostr-client-index-1/</id>
        
        <content type="html" xml:base="https://nickmonad.blog/2023/building-nostr-client-index-1/">&lt;p&gt;Welcome to the second installation of the &quot;Building a nostr client&quot; series. The goal of this series is to walk through
building a simple nostr client in Rust. If you haven&#x27;t seen the first post &lt;a href=&quot;https:&#x2F;&#x2F;nickmonad.blog&#x2F;2023&#x2F;building-nostr-client-index-0&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;,
I would recommend doing so, as we&#x27;ll build on those concepts. Also, if you aren&#x27;t familiar with nostr and why I&#x27;m doing this,
that would be a good place to start as well!&lt;&#x2F;p&gt;
&lt;p&gt;In the last post we discussed the basic data structures of the protocol; how to build them, and what basic Event signing
could look like.&lt;&#x2F;p&gt;
&lt;p&gt;In this post, I want to start connecting to relays, and build a simple TUI (terminal user interface) to display events.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;building-on-prior-art&quot;&gt;Building on prior art...&lt;&#x2F;h3&gt;
&lt;p&gt;When I first considered doing this series of posts, I had the thought that I would do pretty much everything
&quot;from scratch&quot;. Essentially, use libraries available to me in Rust, but nothing nostr specific. After finding the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;nostr&#x2F;latest&#x2F;nostr&#x2F;index.html&quot;&gt;nostr crate&lt;&#x2F;a&gt; and giving it some thought, I decided that I would, at the very least,
use its core data structures, event generation and signing capabilities to build my client. &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;yukibtc&quot;&gt;yukibtc&lt;&#x2F;a&gt;
and other contributors have done an incredible job with this crate so far. Much better than I could do. It&#x27;s easy
to understand and quite comprehensive.&lt;&#x2F;p&gt;
&lt;p&gt;So, in the interest of time, and future-proofing this project (I know I&#x27;d have to refactor my code to use the &lt;code&gt;nostr&lt;&#x2F;code&gt;
crate at some point anyway), I&#x27;ll be using that crate moving forward. Even so, I want to explore some different methods
of relay interaction that depart from the sibling &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;nostr-sdk&#x2F;latest&#x2F;nostr_sdk&#x2F;index.html&quot;&gt;nostr-sdk&lt;&#x2F;a&gt;
crate, so we&#x27;ll have plenty of code to write there.&lt;&#x2F;p&gt;
&lt;p&gt;Moving on!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;relays&quot;&gt;Relays&lt;&#x2F;h3&gt;
&lt;p&gt;Relays are the backbone of the nostr protocol. They are responsible for accepting events and sending them on to
interested clients. By design, there are many relays a client could possibly connect to. Some require payment for access,
but many are free. Some are built for a specific community or topic in mind, but most right now seem to be pretty
free-form. If one relay goes down, for any reason, temporarily or permanently, clients can just connect to another.&lt;&#x2F;p&gt;
&lt;p&gt;Clients connect to relays and send events (or &quot;notes&quot;) across them, and clients on the other end set up &lt;em&gt;filters&lt;&#x2F;em&gt;
for those events. Not every user is automatically connected to every other user, as they would be on the global
feed of a centralized platform, so there are some issues around discovery, as you need to know which relays
people are posting to. But, in practice, it&#x27;s not a big deal. (And there are some NIPs out there attempting to mitigate this.)&lt;&#x2F;p&gt;
&lt;p&gt;As you start to think about how a client should interact with its configured relays, you tend to realize:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;There is no &quot;should&quot;.&lt;&#x2F;strong&gt; Other than the basic data over-the-wire requirements of the protocol, clients can and actively do
all sorts of things to transmit and receive events to and from relays. Without &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;twitter&#x2F;the-algorithm&quot;&gt;The Algorithm&lt;&#x2F;a&gt;
handed down from on high, there are really no rules here. You want events chronologically? Great. You want events from your
followed accounts and the accounts they follow? Sure. Only show events whose ID ends with the hex digit &lt;code&gt;f&lt;&#x2F;code&gt;? I guess.
Allow the user to selectively send events to some relays but not others? Yes, absolutely.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;It&#x27;s suprisingly deep.&lt;&#x2F;strong&gt; There are a lot of practical elements to consider when building a client&#x27;s view
of the nostr world, especially in a &quot;twitter-like&quot; way. Generally you want to have the option of viewing something other than the &quot;global&quot; feed
of all their connected relays, as it can either be extremely spammy, or just too much information, too quickly. So,
you need some kind of view filtering, even if it&#x27;s just showing events from the user&#x27;s followed accounts. Also, what if the user
is away from the client for a while (say a month) and decides to come back? Do you start showing them events from exactly
where they left off? It might take them a while to scroll through and catch up to current events. Do you fetch older events
if they scroll in reverse chronological order? How many do you fetch at a time?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I could go on with plenty more rhetorical questions related to client&#x2F;relay interaction. It really is the wild west
right now and there are a lot of opportunities to innovate in this space.&lt;&#x2F;p&gt;
&lt;p&gt;All that being said, I want to start as simple as possible. For now, we will support showing events from the user&#x27;s
followed accounts, in the order we receive them. But first, we need to connect to some relays.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;finding-a-connection-3&quot;&gt;Finding a connection &amp;lt;3&lt;&#x2F;h4&gt;
&lt;p&gt;Connecting to a relay is &lt;em&gt;relatively&lt;&#x2F;em&gt; straightforward in principle, but somewhat difficult to manage in practice.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, the really low-level details are already handled for us in a crate called
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tokio-tungstenite&#x2F;latest&#x2F;tokio_tungstenite&#x2F;index.html&quot;&gt;tokio-tungstenite&lt;&#x2F;a&gt;. &lt;code&gt;tungstenite&lt;&#x2F;code&gt; is a popular
Rust crate for handling &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;WebSockets_API&quot;&gt;WebSocket&lt;&#x2F;a&gt; connections, which
nostr uses for its protocol communication layer. The &quot;tokio&quot; side of this crate just brings it into the
most popular ecosystem for asynchronous programming in Rust... &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tokio.rs&#x2F;&quot;&gt;tokio&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I believe the primary motivating factor for using WebSockets in nostr instead of some other transport protocol
is that WebSockets are &lt;em&gt;everywhere&lt;&#x2F;em&gt;, especially modern web browsers. This means clients can work within
or (in our case) outside a browser context. It also means clients can make one connection to a relay, and reuse that
same connection for multiple things, reducing some of the connection overhead on the server.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Another quick caveat: Like many aspects of this series, especially related to Rust, I certainly cannot do the asynchronous programming
story justice here. It&#x27;s another huge topic, deserving of near book-length treatments itself. Very simply put,
asynchronous programming allows applications that primarily do I&#x2F;O to scale quite far, with fewer resources.
This is because I&#x2F;O related tasks generally take waaaay longer than the tasks a CPU must perform, due to the high
latency of the I&#x2F;O device (disk, network, etc). Therefore, we can tell the asynchronous runtime we anticipate running
some long I&#x2F;O task, and that it can literally &lt;em&gt;wait&lt;&#x2F;em&gt; for that task to complete, &lt;em&gt;while another task on the CPU runs&lt;&#x2F;em&gt;,
typically on the same thread.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Essentially, we need to start a bunch of tasks that send and receive events from configured relays on their own time,
and have another task that is aggregating these events together in some way: de-duplicating them,
showing them to the user, saving them to disk, etc.&lt;&#x2F;p&gt;
&lt;p&gt;So, we need a way to spawn the async I&#x2F;O task for each relay we want to interact with, and then communicate with that task
using nifty little tools called &quot;channels&quot;. I&#x27;ll try to illustrate how this is going to look.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;architecture&quot;&gt;Architecture&lt;&#x2F;h4&gt;
&lt;p&gt;Relays are implemented as a Rust &lt;code&gt;struct&lt;&#x2F;code&gt; type, with a few properties. First, it must be configured with a WebSocket
URL, which is published by a relay operator, and is up to our client to store and use. Internally, a &lt;code&gt;Relay&lt;&#x2F;code&gt; must
also create and reference a &quot;channel&quot;, that is used by the client to communicate with it asynchronously. These are
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tokio&#x2F;latest&#x2F;tokio&#x2F;sync&#x2F;mpsc&#x2F;index.html&quot;&gt;&lt;code&gt;tokio::sync::mpsc&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; channels (&lt;strong&gt;&quot;multiple producer, single
consumer&quot;&lt;&#x2F;strong&gt;), and aren&#x27;t exposed directly to the client, but is indirectly through a function interface, as illustrated
below. Relays are created in a &lt;code&gt;disconnected&lt;&#x2F;code&gt; state.&lt;&#x2F;p&gt;
&lt;div align=&quot;center&quot;&gt;&lt;img alt=&quot;nostr relay architecture disconnected&quot; src=&quot;..&#x2F;images&#x2F;nostr-relay-disconnected.png&quot;&#x2F;&gt;&lt;&#x2F;div&gt;
&lt;p&gt;These &quot;control&quot; messages are used to tell the relay to send us events that match a certain filter
(as defined by &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nostr-protocol&#x2F;nips&#x2F;blob&#x2F;master&#x2F;01.md&quot;&gt;NIP-01&lt;&#x2F;a&gt;), publish our own events, or disconnect.
They must be sent over a channel that the &lt;code&gt;Relay&lt;&#x2F;code&gt; manages since there is an underlying async task that must listen for
events from the relay and be responsive.&lt;&#x2F;p&gt;
&lt;p&gt;But first, we have to actually connect to the relay with its WebSocket address, and receive those events from it. When we
call the &lt;code&gt;Relay::connect()&lt;&#x2F;code&gt; method, we use the &lt;code&gt;tungstenite&lt;&#x2F;code&gt; crate underneath to open a WebSocket connection, and spin
up an async task to handle the &quot;up&quot; stream (messages &lt;em&gt;to&lt;&#x2F;em&gt; the relay), and the &quot;down&quot; stream (messages &lt;em&gt;from&lt;&#x2F;em&gt; the relay).
These &quot;up&quot; and &quot;down&quot; handles are also part of a &lt;code&gt;Stream&lt;&#x2F;code&gt; construct that is well beyond the scope of this post, but for
our purposes, &quot;up&quot; is like a &lt;code&gt;mpsc::Sender&lt;&#x2F;code&gt; and &quot;down&quot; is like a &lt;code&gt;mpsc::Receiver&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;div align=&quot;center&quot;&gt;&lt;img alt=&quot;nostr relay architecture connected&quot; src=&quot;..&#x2F;images&#x2F;nostr-relay-connected.png&quot;&#x2F;&gt;&lt;&#x2F;div&gt;
&lt;p&gt;These relay messages are written over the wire as &lt;code&gt;Message&lt;&#x2F;code&gt; types from the &lt;code&gt;tungstenite&lt;&#x2F;code&gt; crate, and are translated
from the &lt;code&gt;nostr::ClientMessage&lt;&#x2F;code&gt; and to &lt;code&gt;nostr::RelayMessage&lt;&#x2F;code&gt; types.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, we have to consider how it looks when there are multiple relays involved, as is expected within a nostr client.
When the &lt;code&gt;Relay::connect()&lt;&#x2F;code&gt; method is called, it expects to be given a &lt;code&gt;mpsc::Sender&lt;&#x2F;code&gt; side of a channel, which can
be cloned and given to &lt;em&gt;every&lt;&#x2F;em&gt; relay we are interested in. These &quot;producers&quot; will all funnel to one &quot;consumer&quot; (&lt;code&gt;mpsc::Receiver&lt;&#x2F;code&gt;),
which acts as our firehose of events coming from all relays we have a connection with. There&#x27;s nothing required about this,
it just makes it easier to deal with in a simple implementation like this one. We can treat all &lt;code&gt;Relay&lt;&#x2F;code&gt; structs as if they
were one big set of relays, producing a single stream of events.&lt;&#x2F;p&gt;
&lt;div align=&quot;center&quot;&gt;&lt;img alt=&quot;nostr relay architecture stream&quot; src=&quot;..&#x2F;images&#x2F;nostr-relay-stream.png&quot;&#x2F;&gt;&lt;&#x2F;div&gt;
&lt;p&gt;As far as technical implementation details, there&#x27;s a lot left to go over here. I think the best way to think about this
is you have an async task (&quot;thread&quot;) driving the inbound&#x2F;outbound connection with the WebSocket relay
itself, and another, at the client level, that is driving the state of the &lt;code&gt;Relay&lt;&#x2F;code&gt; struct, and that is why we need
to use async channels behind &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;...&amp;gt;&amp;gt;&lt;&#x2F;code&gt; primitives, so they can be safely cloned and shared throughout the client as necessary.
The &lt;code&gt;Relay&lt;&#x2F;code&gt; struct is a effectively just a handle over a set of common data structures, making the initial connection and
shuffling data between this async task boundary.&lt;&#x2F;p&gt;
&lt;p&gt;With that, we have a &lt;em&gt;very&lt;&#x2F;em&gt; simple nostr client interacting with relays! Here is some sample output that just shows
us connected to two major public relays, sending a filter for all kind &lt;code&gt;1&lt;&#x2F;code&gt; events after the current timestamp. When the
user hits Ctrl-C, we disconnect from each relay and shutdown.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; RUST_LOG=info cargo run
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Compiling&lt;&#x2F;span&gt;&lt;span&gt; roostr v0.1.0 (&#x2F;var&#x2F;home&#x2F;nickmonad&#x2F;code&#x2F;nostr&#x2F;roostr)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Finished&lt;&#x2F;span&gt;&lt;span&gt; dev &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;unoptimized + debuginfo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt; target(s) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; 3.07s
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Running &lt;&#x2F;span&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;target&#x2F;debug&#x2F;roostr&lt;&#x2F;span&gt;&lt;span&gt;`
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T19:59:28Z&lt;&#x2F;span&gt;&lt;span&gt; WARN  roostr] relay sent non-event message: EndOfStoredEvents(SubscriptionId(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;51102577c2f141aa2a06a10d3c2106b5&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;))
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T19:59:28Z&lt;&#x2F;span&gt;&lt;span&gt; WARN  roostr] relay sent non-event message: EndOfStoredEvents(SubscriptionId(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;5854bf7126a9337f9c35ea55dfdd5227&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;))
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T19:59:43Z&lt;&#x2F;span&gt;&lt;span&gt; INFO  roostr] EventId(0x078495389530eff3f78e86dcb14095f4c10d0bc80e9b1f5f0fa9bdab7c8a3c45)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T19:59:45Z&lt;&#x2F;span&gt;&lt;span&gt; INFO  roostr] EventId(0x48627557bcac6d61d35946a74e5f3cf63a1057a0852b17f22d45bf530eaab8a3)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T19:59:46Z&lt;&#x2F;span&gt;&lt;span&gt; INFO  roostr] EventId(0xe5954fe675db03fc5f0a1b4ea5d19f8de6fbd2cbf4be670fbd145652387d5136)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T19:59:49Z&lt;&#x2F;span&gt;&lt;span&gt; INFO  roostr] EventId(0xfa9d99dc2e1a1e3c7c61dbb26d3929d1c0f82ec513d86403eb1790bcd6d1c25e)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T19:59:50Z&lt;&#x2F;span&gt;&lt;span&gt; INFO  roostr] EventId(0xf2fe94cf82fb3d6dc39811320d43a9cbd0e1eaeb04ff7fb0a03ddea983a4ec21)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T19:59:50Z&lt;&#x2F;span&gt;&lt;span&gt; INFO  roostr] EventId(0xf2fe94cf82fb3d6dc39811320d43a9cbd0e1eaeb04ff7fb0a03ddea983a4ec21)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T19:59:50Z&lt;&#x2F;span&gt;&lt;span&gt; INFO  roostr] EventId(0xe4410e2d3714de65f21aa494ec89fb9e38ec69c05402143c5395cfe0df4e81fb)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T19:59:53Z&lt;&#x2F;span&gt;&lt;span&gt; INFO  roostr] EventId(0x14b7f5f38998a93bb9dda045b72d55a3bab61bde4481b65c415e5503c61eb40c)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T19:59:53Z&lt;&#x2F;span&gt;&lt;span&gt; INFO  roostr] EventId(0x953c7bb5884b57b98f54ddf227a8b437c3e061525e29a132c69f7848085df218)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T19:59:55Z&lt;&#x2F;span&gt;&lt;span&gt; INFO  roostr] EventId(0x9f4a511bd1f6b02ccb4c4daea54a51b5daef6d78b126219fa234962efc5d8de7)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T20:00:03Z&lt;&#x2F;span&gt;&lt;span&gt; INFO  roostr] EventId(0x84fec22e2056751d54c323fa4ff6ac3217d352f6f73eff84779fa3f7e5d48291)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T20:00:07Z&lt;&#x2F;span&gt;&lt;span&gt; INFO  roostr] EventId(0xf7119e7250a7f6eaea5058254d850dcb7882428535f1aba1a3e511c5229aa00f)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;^C[2023-08-26T20:00:11Z&lt;&#x2F;span&gt;&lt;span&gt; WARN  roostr] shutting down!
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T20:00:11Z&lt;&#x2F;span&gt;&lt;span&gt; WARN  roostr::relay] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;wss:&#x2F;&#x2F;relay.damus.io&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt; user disconnect
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[2023-08-26T20:00:11Z&lt;&#x2F;span&gt;&lt;span&gt; WARN  roostr::relay] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;wss:&#x2F;&#x2F;nostr-pub.wellorder.net&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt; user disconnect
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The inital messages showing &lt;code&gt;EndOfStoredEvents&lt;&#x2F;code&gt; from each configured relay are due to the fact we set the &lt;code&gt;since&lt;&#x2F;code&gt;
value on our filter message to be &lt;code&gt;Timestamp::now()&lt;&#x2F;code&gt; and the relay is just saying, &quot;eveything after this will be in
real-time.&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;tui&quot;&gt;TUI&lt;&#x2F;h3&gt;
&lt;p&gt;Now that we have events coming in from the relay, we can start on the real &lt;em&gt;client&lt;&#x2F;em&gt; functionality. As I stated earlier,
this can go really deep on implementation, and wide on feature set. I want to keep things really simple, so
it won&#x27;t do much for now.&lt;&#x2F;p&gt;
&lt;p&gt;To help set some direction on where to focus next, I think we can break it down like so:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;First, read a list of public keys to &quot;follow&quot; from a local config file.&lt;&#x2F;li&gt;
&lt;li&gt;Using the public keys present in that config, setup and send filters to each relay, asking first for kind &lt;code&gt;0&lt;&#x2F;code&gt; events
associated with the public key(s), giving us &quot;profile&quot; metadata, like the user&#x27;s preferred display name, followed by kind &lt;code&gt;1&lt;&#x2F;code&gt; events, the actual text notes,
from the past &lt;strong&gt;day&lt;&#x2F;strong&gt;. (Arbitrary choice.)&lt;&#x2F;li&gt;
&lt;li&gt;Listening on the aggregated event stream, use some kind of mechanism to ensure that events aren&#x27;t shown twice, in the case where
a followed public key is published to more than one of the relays we are interested in. This is quite common, and must be handled
by pretty much every client. There are really straightforward ways of handling this, using a list or map, but those come with tradeoffs
in performance and memory usage over time. We could use a SQLite based implemention, which I think would be ideal for a client like this,
but for now, let&#x27;s just use a simple in-memory set, and vector (list) of events.&lt;&#x2F;li&gt;
&lt;li&gt;Build a simple TUI (terminal user interface) for showing the events and navigating through the list.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is the simplest possible start I can imagine, while building something at least marginally useful. Many factors of a &quot;real&quot; client
won&#x27;t be considered here, but, it&#x27;ll be fun to continue adding features over time.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;starting-up&quot;&gt;Starting up&lt;&#x2F;h4&gt;
&lt;p&gt;First, we need to read our config file, and create our primary &lt;code&gt;mpsc&lt;&#x2F;code&gt; channels. One for that relay &quot;firehose&quot; mentioned
above, and another for terminal events (user input, resize notifications, etc). The first block here is our example
&lt;code&gt;config.toml&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;follow &lt;&#x2F;span&gt;&lt;span&gt;= [
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# fiatjaf
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;npub1sg6plzptd64u62a878hep2kev88swjh3tw00gjsfl8f237lmu63q0uf63m&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# jack
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;npub1qny3tkh0acurzla8x3zy4nhrjz5zd8l9sy9jys09umwng00manysew95gx&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# odell
&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[[relay]]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;wss:&#x2F;&#x2F;relay.damus.io&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[[relay]]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;wss:&#x2F;&#x2F;nostr-pub.wellorder.net&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;In the long term, the config for &lt;code&gt;roostr&lt;&#x2F;code&gt; would be more related to behavior, and not have hardcoded npubs
or relay URLs. These would be requested the first time a user started the client, and stored in a local database.
Pubkeys to follow would ideally come from nostr &lt;code&gt;kind:3&lt;&#x2F;code&gt; events, published via our user&#x27;s pubkey, but I decided
not to implement that right now, since we don&#x27;t have a database yet.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tokio&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; anyhow::Result&amp;lt;()&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; send logs to stderr
&lt;&#x2F;span&gt;&lt;span&gt;    env_logger::builder().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;target&lt;&#x2F;span&gt;&lt;span&gt;(Target::Stderr).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;init&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; load config
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; config = Config::load(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;config.toml&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;).await?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; event&#x2F;relay message bus
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(tx, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; rx) = mpsc::channel::&amp;lt;RelayMessage&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1024&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; events from terminal
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(events, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; input) = mpsc::channel::&amp;lt;TermEvent&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, we can initialize our &lt;code&gt;Relay&lt;&#x2F;code&gt; structs, one for each URL we have listed in our configuration.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; filter for all kind:0 metadata objects
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; metadata = Filter::new()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;(Kind::Metadata)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;authors&lt;&#x2F;span&gt;&lt;span&gt;(config.follows.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; filter for all kind:1 notes
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; notes = Filter::new()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;(Kind::TextNote)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;authors&lt;&#x2F;span&gt;&lt;span&gt;(config.follows.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;since&lt;&#x2F;span&gt;&lt;span&gt;(Timestamp::now().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;sub&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;ONE_DAY&lt;&#x2F;span&gt;&lt;span&gt;)); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; statically defined `Duration` value
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; instantiate configured relays
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; relays: Vec&amp;lt;Relay&amp;gt; = vec![];
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; relay in config.relays {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; r = Relay::new(relay.url.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;        r.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span&gt;(tx.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;()).await?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        r.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;send&lt;&#x2F;span&gt;&lt;span&gt;(ClientMessage::new_req(
&lt;&#x2F;span&gt;&lt;span&gt;            SubscriptionId::generate(),
&lt;&#x2F;span&gt;&lt;span&gt;            vec![metadata.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;(), notes.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;()],
&lt;&#x2F;span&gt;&lt;span&gt;        ))
&lt;&#x2F;span&gt;&lt;span&gt;        .await?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        relays.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(r);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;processing-and-displaying-events&quot;&gt;Processing and displaying events&lt;&#x2F;h4&gt;
&lt;p&gt;Once the client has completed this initial startup, it can run its primary processing loop, reading events
from the relays, and user input from the terminal.&lt;&#x2F;p&gt;
&lt;p&gt;Since we aren&#x27;t using SQLite right now as we prove all this out, we&#x27;ll use an in-memory &quot;database&quot; structure.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Simple &amp;quot;database&amp;quot; implementation,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; storing a list of Events, and a map of Metadata objects, accessible via pubkey
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(Default)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub struct &lt;&#x2F;span&gt;&lt;span&gt;Database {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;events&lt;&#x2F;span&gt;&lt;span&gt;: Vec&amp;lt;Event&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;metadata&lt;&#x2F;span&gt;&lt;span&gt;: HashMap&amp;lt;String, Metadata&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;metadata&lt;&#x2F;code&gt; attribute is important, as we want to show every pubkey&#x27;s &quot;username&quot; as they have published them
under the &lt;code&gt;kind:0&lt;&#x2F;code&gt; event. Since we have a filter setup for those, we can update this struct as a mapping between the
followed pubkeys and their metadata. The &lt;code&gt;events&lt;&#x2F;code&gt; attribute is a simple vector of all the &lt;code&gt;kind:1&lt;&#x2F;code&gt; events we receive.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Obviously, this isn&#x27;t really scalable for &quot;real-world&quot; usage. We&#x27;d ideally push these to a local SQLite file,
and maybe use these in-memory structures for a bounded cache. Just indefinitely growing this vector isn&#x27;t
efficient at all, but again, this is more of a proof-of-concept.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Finally, we can run our primary loop. Essentially, the client blocks while it waits for one of two types of
events: user input or relay events. For user events, the TUI will be given those events (other than the quit event),
and it will update its own state, like which nostr note is selected based on user selection.
For relay events, the database is updated. Incoming nostr events are de-duplicated, and then stored in the event list.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; setup our &amp;quot;database&amp;quot; and set of &amp;quot;seen&amp;quot; events, for de-duplication
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; database = Database::default();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; seen: HashSet&amp;lt;EventId&amp;gt; = HashSet::new();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; setup tui
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; tui = Tui::new()?;
&lt;&#x2F;span&gt;&lt;span&gt;tui.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;events&lt;&#x2F;span&gt;&lt;span&gt;(events);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; initial draw
&lt;&#x2F;span&gt;&lt;span&gt;tui.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;draw&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;database);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; main event loop
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; as messages&#x2F;events come in, de-duplicate them
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; and store them for display on the UI
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;loop &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    tokio::select! {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; get events from the terminal
&lt;&#x2F;span&gt;&lt;span&gt;        event = input.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;recv&lt;&#x2F;span&gt;&lt;span&gt;() =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; event {
&lt;&#x2F;span&gt;&lt;span&gt;                Some(event) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if let &lt;&#x2F;span&gt;&lt;span&gt;TermEvent::Key(KeyEvent{ code, .. }) = event {
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; code == KeyCode::Char(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;q&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;) {
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; user quit - disconnect from relays
&lt;&#x2F;span&gt;&lt;span&gt;                            log::warn!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[client] user quit!&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;disconnect&lt;&#x2F;span&gt;&lt;span&gt;(relays).await.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;break&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;                        }
&lt;&#x2F;span&gt;&lt;span&gt;                    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; pass event to TUI struct, so it can update its own state
&lt;&#x2F;span&gt;&lt;span&gt;                    tui.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;update&lt;&#x2F;span&gt;&lt;span&gt;(event, &amp;amp;database);
&lt;&#x2F;span&gt;&lt;span&gt;                },
&lt;&#x2F;span&gt;&lt;span&gt;                _ =&amp;gt; {}
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; get events from relay(s)
&lt;&#x2F;span&gt;&lt;span&gt;        msg = rx.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;recv&lt;&#x2F;span&gt;&lt;span&gt;() =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; msg {
&lt;&#x2F;span&gt;&lt;span&gt;                Some(msg) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; msg {
&lt;&#x2F;span&gt;&lt;span&gt;                        RelayMessage::Event { event, .. } =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; event.kind {
&lt;&#x2F;span&gt;&lt;span&gt;                                Kind::Metadata =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; update metadata map, from pubkey to metadata
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; metadata = Metadata::from_json(&amp;amp;event.content).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;                                    database.metadata.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;insert&lt;&#x2F;span&gt;&lt;span&gt;(event.pubkey.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(), metadata);
&lt;&#x2F;span&gt;&lt;span&gt;                                },
&lt;&#x2F;span&gt;&lt;span&gt;                                Kind::TextNote =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; process note, ignore if we have seen already
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;!seen.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;contains&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;event.id) {
&lt;&#x2F;span&gt;&lt;span&gt;                                        database.events.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(event.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;as_ref&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;                                        seen.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;insert&lt;&#x2F;span&gt;&lt;span&gt;(event.id);
&lt;&#x2F;span&gt;&lt;span&gt;                                    }
&lt;&#x2F;span&gt;&lt;span&gt;                                }
&lt;&#x2F;span&gt;&lt;span&gt;                                _ =&amp;gt; {}
&lt;&#x2F;span&gt;&lt;span&gt;                            }
&lt;&#x2F;span&gt;&lt;span&gt;                        },
&lt;&#x2F;span&gt;&lt;span&gt;                        _ =&amp;gt; log::warn!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;relay sent non-event message: {:?}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, msg)
&lt;&#x2F;span&gt;&lt;span&gt;                    }
&lt;&#x2F;span&gt;&lt;span&gt;                }
&lt;&#x2F;span&gt;&lt;span&gt;                None =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;                    log::warn!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;all relay senders dropped&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;break&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;                }
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; event(s) processed, draw another &amp;quot;frame&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    tui.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;draw&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;database);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In both cases, after the event is processed, the TUI is re-drawn. In principle, there is some contention here. If there
is a delay in processing a relay event, user events will have to wait as they queue from the terminal input thread,
potentially causing a lag in the responsiveness. But, in practice, espcially in this case, the relay event processing
is so minimal, that it doesn&#x27;t have an impact. If there were heavier processing involved, the client would likely have
to split off that processing onto another async task or thread and signal its completion back to this thread.&lt;&#x2F;p&gt;
&lt;p&gt;As for the actual processing requirements and performance of re-drawing the TUI, we don&#x27;t have to worry about that
in practice either, since it&#x27;s just... outputing text to the terminal.&lt;&#x2F;p&gt;
&lt;p&gt;Since this post is already way too long, I&#x27;ll pass on explaining the actual TUI rendering in detail. Suffice it to
say, I chose to use &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;ratatui&#x2F;latest&#x2F;ratatui&#x2F;index.html&quot;&gt;ratatui&lt;&#x2F;a&gt;, the successor to &lt;code&gt;tui-rs&lt;&#x2F;code&gt;. It
provides really simple ways to put the terminal in the correct mode it needs to be in, and build up layout components.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;demo&quot;&gt;Demo!&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;This may not render correctly on mobile.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;script async id=&quot;asciicast-WvpaikA286wzkJ5RqBIIT6VzT&quot; src=&quot;https:&#x2F;&#x2F;asciinema.org&#x2F;a&#x2F;WvpaikA286wzkJ5RqBIIT6VzT.js&quot;&gt;&lt;&#x2F;script&gt;
&lt;p&gt;Look at that TUI &lt;strong&gt;magic!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;next-up&quot;&gt;Next Up&lt;&#x2F;h3&gt;
&lt;p&gt;If you&#x27;ve stuck around this far, thanks for hanging in there. It took me way longer to publish this post than I wanted
it to take. It&#x27;s been a really busy summer - full of good and beautiful things, just not as much coding time outside of work.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a lot that can still be done to make this TUI more functional, useful, and pretty. I think the first
thing I would do is make the rendered height of each note fixed and consistent, and allow the user to expand longer notes
if they want to read them. Since the terminal doesn&#x27;t have a &quot;continuous&quot; scrolling frame, like a fully graphical UI would
have, notes of different length can really break the rendering flow while scrolling through, as you can see in the demo.&lt;&#x2F;p&gt;
&lt;p&gt;But, for now, let&#x27;s move onto other things!&lt;&#x2F;p&gt;
&lt;p&gt;In the next (and likely final) installation of this series, I&#x27;ll add some basic key management, paving the way
for signing and publishing events.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Building a nostr client [0]</title>
        <published>2023-03-26T00:00:00+00:00</published>
        <updated>2023-03-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Nick Miller
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nickmonad.blog/2023/building-nostr-client-index-0/"/>
        <id>https://nickmonad.blog/2023/building-nostr-client-index-0/</id>
        
        <content type="html" xml:base="https://nickmonad.blog/2023/building-nostr-client-index-0/">&lt;p&gt;Over the past few weeks, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;nostr.how&#x2F;&quot;&gt;nostr&lt;&#x2F;a&gt; has continued to take much of my attention. I&#x27;m still
fascinated by this protocol and I&#x27;m very curious to see where it goes in the coming years.&lt;&#x2F;p&gt;
&lt;p&gt;If you haven&#x27;t heard of nostr yet, it stands for &quot;Notes and Other Stuff Transmitted over Relays&quot;, and is a pretty
simple, yet powerful, idea for defining and growing a social network. Instead of a centralized server (or even
federated servers that talk to each other) it uses the concept of &quot;relays&quot; that allow clients to connect to them and,
you guessed it, &lt;em&gt;transmit&lt;&#x2F;em&gt; &quot;notes&quot; and &quot;other stuff&quot;, using a cryptographic key pair to sign and verify that data.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;i&gt;By the way, how do people pronouce this thing? Is it &quot;nos-ter&quot;, &quot;nose-ter&quot;, ... something else? I tend towards the
first way for some reason.&lt;&#x2F;i&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;As the protocol&#x27;s &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nostr-protocol&#x2F;nostr&quot;&gt;GitHub&lt;&#x2F;a&gt; states:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The simplest open protocol that is able to create a censorship-resistant global &quot;social&quot; network once and for all.
It doesn&#x27;t rely on any trusted central server, hence it is resilient; it is based on cryptographic keys and
signatures, so it is tamperproof; it does not rely on P2P techniques, and therefore it works.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Clients have the option to connect to as many or as few relays as they want, some of which are free and open to the
public, and some of which require payment to access. This allows for a wide variety of communities to coalesce
around certain relays if they choose, and move to another if the one they are using decides to censor them for
any reason.&lt;&#x2F;p&gt;
&lt;p&gt;The implications of this are pretty amazing, and I think it will open up a lot of opportunity in so many directions.
Not only from the standpoint of free speech, but also for relay operators and client developers to build features in
a competitive way, very quickly raising the bar for the network as a whole. We are still very much in the early days,
and things are a little bit crazy, but the hype is real, and I believe for good reason.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;let-s-build-a-client&quot;&gt;Let&#x27;s build a client!&lt;&#x2F;h3&gt;
&lt;p&gt;Besides all the reasons I mentioned above, I&#x27;m excited about nostr, because, as a programmer, I get to &lt;em&gt;build&lt;&#x2F;em&gt; things.
My own things. Things that define how I interact with the network. There are a lot of good clients out there already,
but I figured, &quot;why not try and build my own?&quot;&lt;&#x2F;p&gt;
&lt;p&gt;This is the first entry (index &lt;code&gt;0&lt;&#x2F;code&gt;) in a series of posts I intend to write outlining the basic discovery and process
of building a client. Hopefully we&#x27;ll run into some interesting challenges that need thoughtful solutions, and we&#x27;ll
progress towards something relatively usable.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll be writing this series as I develop the client (seriously, I haven&#x27;t written a single line of code as I
write this sentence), which is something I&#x27;ve never done before. I&#x27;ll do my best to strike a balance between content
and pace. I don&#x27;t want to dump every single thought in my head out into this post, otherwise it could take hours
to read, but I want to show enough to get an idea of what it takes to build a client at a technical level. I&#x27;ll also
have to stop writing these posts at &lt;em&gt;some&lt;&#x2F;em&gt; point (there&#x27;s more to life, ya know?). But, I&#x27;ll keep developing the
client after I wrap up this series, and anyone can follow along on GitHub once we hit that point.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hopefully I won&#x27;t give so much detail that it&#x27;s exhausting to read. I&#x27;m hoping I&#x27;ll get better about tightening up
these posts as I go.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Wait, what about the name? After looking at all sorts of weird combinations of &quot;rust&quot; and &quot;nostr&quot;, I came up
with &lt;code&gt;roostr&lt;&#x2F;code&gt;. I like it because it gives the project its own identity, and is actually pronoucable as &quot;rooster&quot;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I&#x27;m aware of the whole ostrich thing nostr has going for it. I just wanted some combination of &quot;rust&quot; and &quot;nostr&quot;
and settled on something unique in the space!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;the-stack&quot;&gt;THE STACK&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;ll be writing this client using Rust. That&#x27;s it. That&#x27;s the stack.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;ll be a terminal-based client, because frankly it&#x27;s just easier, but I also love the challenge of making a usable
terminal UI. There&#x27;s a neat minimalism there.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I will definitely assume some Rust knowledge for this series. There are a million ways to learn Rust and I can&#x27;t
really teach it any better. I&#x27;ll make clarifying notes if I think something is particularly odd about Rust that
needs explaining, but I want to keep it to a minimum.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;the-protocol&quot;&gt;The Protocol&lt;&#x2F;h3&gt;
&lt;p&gt;Nostr, as a protocol, is refreshingly simple. The main GitHub &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nostr-protocol&#x2F;nostr&quot;&gt;repository&lt;&#x2F;a&gt;
is a high-level look at the protocol and motivations behind it, but the NIPs
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nostr-protocol&#x2F;nips&quot;&gt;repository&lt;&#x2F;a&gt; is where all the action is.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nostr-protocol&#x2F;nips&#x2F;blob&#x2F;master&#x2F;01.md&quot;&gt;NIP-01&lt;&#x2F;a&gt; is one of the few mandatory NIPs that relays
and clients must implement, and is the obvious starting point for us. Highly recommend giving it a quick once over
before continuing.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;events&quot;&gt;Events&lt;&#x2F;h4&gt;
&lt;p&gt;Looking at the first available struct defined in NIP-01, let&#x27;s take a stab at defining it in Rust. I want to start
with the event generation and signing portion first, before we start connecting to relays.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub enum &lt;&#x2F;span&gt;&lt;span&gt;Kind {
&lt;&#x2F;span&gt;&lt;span&gt;    SetMetadata,     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; kind: 0
&lt;&#x2F;span&gt;&lt;span&gt;    Text,            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; kind: 1
&lt;&#x2F;span&gt;&lt;span&gt;    RecommendServer, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; kind: 2
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub enum &lt;&#x2F;span&gt;&lt;span&gt;TagId {
&lt;&#x2F;span&gt;&lt;span&gt;    PubKey,  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; tag: &amp;quot;p&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    Event,   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; tag: &amp;quot;e&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    Unknown, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; ... not sure how to handle this yet
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; simple 3-tuple that will be rendered as a JSON array
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; e.g. [&amp;quot;e&amp;quot;, &amp;quot;&amp;lt;hex&amp;gt;&amp;quot;, &amp;quot;wss:&#x2F;&#x2F;some.relay.net&amp;quot;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub struct &lt;&#x2F;span&gt;&lt;span&gt;Tag(TagId, String, Option&amp;lt;String&amp;gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Event is our core data structure.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; It will likely not be built directly like this, but rather from a function,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; where the `id` will generated and the entire thing (possibly) signed.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub struct &lt;&#x2F;span&gt;&lt;span&gt;Event {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;pubkey&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;created_at&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;: Kind,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span&gt;: Vec&amp;lt;Tag&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As noted in the comments, we have some things to figure out as we get further, but for now this gives us a solid
place to stand. It may make more sense to use more specific types for some of these fields, and we will certainly
need to expand upon these, but I want to start simple for now.&lt;&#x2F;p&gt;
&lt;p&gt;Next, we need a way to generate the &lt;code&gt;id&lt;&#x2F;code&gt; for the event.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;To obtain the event.id, we sha256 the serialized event.
The serialization is done over the UTF-8 JSON-serialized string
(with no white space or line breaks) of the following structure:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;[
&lt;&#x2F;span&gt;&lt;span&gt;  0,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;pubkey, as a (lowercase) hex string&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;created_at, as a number&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;kind, as a number&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;tags, as an array of arrays of non-null strings&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;content, as a string&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I&#x27;ll be honest, I&#x27;m a little confused by the &quot;with no white space or line breaks&quot; comment. Does that also mean
for the &lt;em&gt;content&lt;&#x2F;em&gt;? I could see it either way, but I&#x27;m going to lean towards we don&#x27;t manipulate the content in any
way to obtain this event &lt;code&gt;id&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Before we can actually sign the event, we need that &lt;code&gt;id&lt;&#x2F;code&gt;, which means we need a method to generate the JSON string
described above, based on the event.&lt;&#x2F;p&gt;
&lt;p&gt;In the Rust world, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;serde.rs&#x2F;&quot;&gt;&lt;code&gt;serde&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; has become a go-to crate for (ser)ialization, and
(de)serialization, and has support for JSON, so it makes sense for us to start there. &lt;code&gt;serde&lt;&#x2F;code&gt; supports the notion
of a &lt;code&gt;Serialize&lt;&#x2F;code&gt; trait (or &quot;interface&quot;), we can implement to tell &lt;code&gt;serde&lt;&#x2F;code&gt; how we want our custom types to look when
they are represented as JSON. Explaining all the details of &lt;code&gt;serde&lt;&#x2F;code&gt; is definitely outside of the scope of this
post, but this should give you a rough idea.&lt;&#x2F;p&gt;
&lt;p&gt;For &lt;code&gt;Kind&lt;&#x2F;code&gt;, it&#x27;s pretty straightforward, just map each &lt;code&gt;enum&lt;&#x2F;code&gt; variant to a number.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;Serialize &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;Kind {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;serialize&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;S&amp;gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;serializer&lt;&#x2F;span&gt;&lt;span&gt;: S) -&amp;gt; Result&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;S::&lt;&#x2F;span&gt;&lt;span&gt;Ok, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;S::&lt;&#x2F;span&gt;&lt;span&gt;Error&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;where
&lt;&#x2F;span&gt;&lt;span&gt;        S: Serializer,
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;        serializer.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;serialize_u32&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;match &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            Kind::SetMetadata =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            Kind::Text =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            Kind::RecommendServer =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        })
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;Tag&lt;&#x2F;code&gt; type is a little more complicated, since the 3rd element of the array is optional, according to NIP-01.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;Serialize &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;Tag {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;serialize&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;S&amp;gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;serializer&lt;&#x2F;span&gt;&lt;span&gt;: S) -&amp;gt; Result&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;S::&lt;&#x2F;span&gt;&lt;span&gt;Ok, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;S::&lt;&#x2F;span&gt;&lt;span&gt;Error&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;where
&lt;&#x2F;span&gt;&lt;span&gt;        S: Serializer,
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; serialize tag
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; tup = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if let &lt;&#x2F;span&gt;&lt;span&gt;Some(_) = &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            serializer.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;serialize_tuple&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;)?
&lt;&#x2F;span&gt;&lt;span&gt;        } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            serializer.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;serialize_tuple&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;)?
&lt;&#x2F;span&gt;&lt;span&gt;        };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; serialize tag &amp;quot;id&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        tup.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;serialize_element&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;match &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            TagId::PubKey =&amp;gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;            TagId::Event =&amp;gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;e&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;            TagId::Unknown =&amp;gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;unknown&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        })?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; serialize tag &amp;quot;payload&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        tup.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;serialize_element&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; serialize recommended relay (if we have it)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if let &lt;&#x2F;span&gt;&lt;span&gt;Some(relay) = &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            tup.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;serialize_element&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;relay)?;
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        tup.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;end&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;m sure they are cleaner ways of handling both of these cases, but in the spirit of just getting things working,
let&#x27;s stick with it.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, we need to consider the &lt;code&gt;Event&lt;&#x2F;code&gt; - the center of all this. I started to go down the route
of implementing &lt;code&gt;Serialize&lt;&#x2F;code&gt; for &lt;code&gt;Event&lt;&#x2F;code&gt;, so it would produce the &lt;code&gt;[0,...]&lt;&#x2F;code&gt; format described above, which we use
in determining the event &lt;code&gt;id&lt;&#x2F;code&gt;, but quickly realized that wouldn&#x27;t be so great.&lt;&#x2F;p&gt;
&lt;p&gt;One thing that&#x27;s really cool about Rust, or even just modern statically typed languages in general, is that we can
encode certain aspects of our problem domain into the types themselves. In this case, &lt;code&gt;EventData&lt;&#x2F;code&gt; is something dynamic,
generated by the user, and can reasonably be &quot;edited&quot;, up until the point we are ready to sign the event for
publication to the network. Once that event is signed though, it doesn&#x27;t make sense that we should be able to edit
that &lt;code&gt;EventData&lt;&#x2F;code&gt; anymore, since changing it necessarily changes the &lt;code&gt;id&lt;&#x2F;code&gt;, which changes the signature
(more on that later).&lt;&#x2F;p&gt;
&lt;p&gt;So, what if our &lt;code&gt;Event&lt;&#x2F;code&gt; type represented this idea directly?&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(serde::Serialize)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub struct &lt;&#x2F;span&gt;&lt;span&gt;Event {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    #[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;serde&lt;&#x2F;span&gt;&lt;span&gt;(flatten)]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;: EventData, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; this is the old &amp;quot;Event&amp;quot; type, without the id and sig
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The astute reader might comment that since our &lt;code&gt;Event&lt;&#x2F;code&gt; fields are &quot;pub&quot; here, it doesn&#x27;t actually quite
get us to the point where we have full encapsulation within this module. That&#x27;s something I hope to address
fully, with accessor methods later on.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;With this design in mind, it wouldn&#x27;t make sense to write &lt;code&gt;Serialize&lt;&#x2F;code&gt; for &lt;code&gt;EventData&lt;&#x2F;code&gt; in such a way that doesn&#x27;t result in
the standard JSON representation of the struct, which we want to send over the wire to the relays. We want to lean
on &lt;code&gt;serde&lt;&#x2F;code&gt;&#x27;s provided implemention of &lt;code&gt;Serialize&lt;&#x2F;code&gt; for structs, and include the fields of &lt;code&gt;EventData&lt;&#x2F;code&gt; into our final
&lt;code&gt;Event&lt;&#x2F;code&gt; JSON representation, at the same level as &lt;code&gt;id&lt;&#x2F;code&gt; and &lt;code&gt;sig&lt;&#x2F;code&gt; (hence the &lt;code&gt;flatten&lt;&#x2F;code&gt; directive). So, we can&#x27;t
use &lt;code&gt;Serialize&lt;&#x2F;code&gt; to build our array for determining the event &lt;code&gt;id&lt;&#x2F;code&gt;. We&#x27;ll have to write a simple function to do that
using string formatting.&lt;&#x2F;p&gt;
&lt;p&gt;OK! That was a bit of a detour, but hopefully a fruitful one. Here&#x27;s what we have now for &lt;code&gt;EventData&lt;&#x2F;code&gt; and &lt;code&gt;Event&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(serde::Serialize)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub struct &lt;&#x2F;span&gt;&lt;span&gt;EventData {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;pubkey&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;created_at&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;: Kind,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span&gt;: Vec&amp;lt;Tag&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;EventData {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; String {
&lt;&#x2F;span&gt;&lt;span&gt;        hex::encode(
&lt;&#x2F;span&gt;&lt;span&gt;            Sha256::new()
&lt;&#x2F;span&gt;&lt;span&gt;                .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;chain_update&lt;&#x2F;span&gt;&lt;span&gt;(format!(
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; just lean on serde here as well, for each element.
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; makes the format string nicer to look at
&lt;&#x2F;span&gt;&lt;span&gt;                    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[0,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;                    json::to_string(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.pubkey).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                    json::to_string(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.created_at).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                    json::to_string(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.kind).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                    json::to_string(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.tags).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                    json::to_string(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.content).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                ))
&lt;&#x2F;span&gt;&lt;span&gt;                .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;finalize&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;        )
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Event can only be generated from EventData,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; with a provided key for signing.
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(serde::Serialize)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub struct &lt;&#x2F;span&gt;&lt;span&gt;Event {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    #[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;serde&lt;&#x2F;span&gt;&lt;span&gt;(flatten)]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;: EventData,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, we can create a simple test case for &lt;code&gt;EventData&lt;&#x2F;code&gt;, and make sure we are getting some &lt;code&gt;id&lt;&#x2F;code&gt; back from it.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;event_id&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; sample event
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; https:&#x2F;&#x2F;www.nostr.guru&#x2F;e&#x2F;c8d24e78cfedd658688bdfc23a2f7049f0032989096f3e1c5df1e5585efaa393
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; data = EventData {
&lt;&#x2F;span&gt;&lt;span&gt;        pubkey: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;5fe74dc9a7349be18269007d8e7bdf7599869cb677fe3f2794ebd821f146fe81&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;into&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;        created_at: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1675631070&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        kind: Kind::Text,
&lt;&#x2F;span&gt;&lt;span&gt;        tags: vec![],
&lt;&#x2F;span&gt;&lt;span&gt;        content: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hello, nostr&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;into&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    assert_eq!(
&lt;&#x2F;span&gt;&lt;span&gt;        data.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;c8d24e78cfedd658688bdfc23a2f7049f0032989096f3e1c5df1e5585efaa393&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;signatures&quot;&gt;Signatures&lt;&#x2F;h4&gt;
&lt;p&gt;The next critical piece we need to consider is signing the events with a key pair.&lt;&#x2F;p&gt;
&lt;p&gt;By utilizing digital signatures, nostr clients and relays can verify the integrity of messages. They are the core piece
of a user&#x27;s identity as they move from one relay to another. This what people mean when they say nostr is
&quot;tamperproof&quot;. As long as you practice secure handling of your keys, no one can generate fake events associated with
your public key.&lt;&#x2F;p&gt;
&lt;p&gt;As stated in the NIP-01 specification,&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Each user has a keypair. Signatures, public key, and encodings are done according to the Schnorr signatures
standard for the curve secp256k1.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The whole cryptography thing is a &lt;em&gt;h u g e&lt;&#x2F;em&gt; topic to cover, and I am by no means an expert, so I won&#x27;t go into much
detail. The basic idea is that everybody has a public key and a private key. This is called the &quot;key pair&quot;.
A user can &quot;sign&quot; data with their private key, and share their public key with the world. Anybody can then look
at the signature data, and verify it was in fact signed by the user with the associated public key, all without
revealing the private key itself, so it can be re-used.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, there is a lot of prior art out there we can utilize to get this working pretty easily.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll be using the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;secp256k1&#x2F;latest&#x2F;secp256k1&#x2F;index.html&quot;&gt;&lt;code&gt;secp256k1&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; crate, which has support
for Schnorr signatures as well. We&#x27;ll also need the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;bech32&#x2F;0.9.1&#x2F;bech32&#x2F;&quot;&gt;&lt;code&gt;bech32&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; crate to
support encoding and decoding of keys in a user-friendly way.&lt;&#x2F;p&gt;
&lt;p&gt;First, let&#x27;s build the signing context and key pair, by reading a value from the environment. Nostr keys use the &lt;code&gt;bech32&lt;&#x2F;code&gt;
encoding, so private keys begin with &lt;code&gt;nsec1&lt;&#x2F;code&gt; and public keys begin with &lt;code&gt;npub1&lt;&#x2F;code&gt;. This makes it easier for a human
looking at values to know which they are dealing with. The public key can be derived from the private key, so we
only need to include the latter in our configuration.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(Parser, Debug)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub struct &lt;&#x2F;span&gt;&lt;span&gt;Config {
&lt;&#x2F;span&gt;&lt;span&gt;    #[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;clap&lt;&#x2F;span&gt;&lt;span&gt;(env = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;ROOSTR_PRIVATE_KEY&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;: SecretString,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub struct &lt;&#x2F;span&gt;&lt;span&gt;Signer {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;: Secp256k1&amp;lt;All&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;keypair&lt;&#x2F;span&gt;&lt;span&gt;: KeyPair,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;Signer {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;from_config&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;: &amp;amp;Config) -&amp;gt; anyhow::Result&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Self&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; decode `nsec1...` into Vec&amp;lt;u5&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(_, decoded, _) = bech32::decode(&amp;amp;config.key.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;expose_secret&lt;&#x2F;span&gt;&lt;span&gt;())?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; create Vec&amp;lt;u8&amp;gt; from Vec&amp;lt;u5&amp;gt; (&amp;quot;base32&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; bytes: Vec&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; = Vec::from_base32(&amp;amp;decoded)?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; generate a secret key on the curve from the byte vector
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; context = Secp256k1::new();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; key = SecretKey::from_slice(&amp;amp;bytes)?;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; keypair = KeyPair::from_secret_key(&amp;amp;context, &amp;amp;key);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        Ok(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Self &lt;&#x2F;span&gt;&lt;span&gt;{ context, keypair })
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    ...
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;From a security standpoint, this isn&#x27;t ready for production. I&#x27;m currently doing just about the bare minimum here
in order to get this working, by using this &lt;code&gt;SecretString&lt;&#x2F;code&gt; type imported from the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;secrecy&#x2F;0.8.0&#x2F;secrecy&#x2F;&quot;&gt;&lt;code&gt;secrecy&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
crate, which basically just zeros out the memory allocation when the value&#x27;s memory is freed, and makes it very explicit
when we expose the secret value to other functions. We&#x27;ll revisit this topic of key management in a later post.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Now that we have that, we can write the function that will sign our event &lt;code&gt;id&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;Signer {
&lt;&#x2F;span&gt;&lt;span&gt;    ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; public key returned in the &amp;quot;x only&amp;quot; format,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; which drops the first parity byte
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;public_key&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; String {
&lt;&#x2F;span&gt;&lt;span&gt;        format!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{:x}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.keypair.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;x_only_public_key&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;sign&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; anyhow::Result&amp;lt;String&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; message = Message::from_slice(&amp;amp;hex::decode(id)?)?;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; signature = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;sign_schnorr&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;message, &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.keypair);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        Ok(format!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{signature:x}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;))
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I had to find the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;secp256k1&#x2F;latest&#x2F;secp256k1&#x2F;struct.KeyPair.html#method.x_only_public_key&quot;&gt;&lt;code&gt;KeyPair::x_only_public_key&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
function after I had originally just tried to test with &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;secp256k1&#x2F;latest&#x2F;secp256k1&#x2F;struct.KeyPair.html#method.public_key&quot;&gt;&lt;code&gt;KeyPair::public_key&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
which seems to include some kind of &quot;parity&quot; byte at the front of the hex output. I can&#x27;t see that being relevant
when looking at the raw data of other valid nostr events out in the wild, so I don&#x27;t think it&#x27;s necessary. This makes
sense actually when we see that &quot;X only&quot; intends the key to be used only for signature verification.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, we can put our types together in the way we described above to generate a signed &lt;code&gt;Event&lt;&#x2F;code&gt; from &lt;code&gt;EventData&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(serde::Serialize)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub struct &lt;&#x2F;span&gt;&lt;span&gt;EventData {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;created_at&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;: Kind,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span&gt;: Vec&amp;lt;Tag&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;EventData {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;pubkey&lt;&#x2F;span&gt;&lt;span&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; String {
&lt;&#x2F;span&gt;&lt;span&gt;        hex::encode(
&lt;&#x2F;span&gt;&lt;span&gt;            Sha256::new()
&lt;&#x2F;span&gt;&lt;span&gt;                .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;chain_update&lt;&#x2F;span&gt;&lt;span&gt;(format!(
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; just lean on serde here as well, for each element.
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; makes the format string nicer to look at
&lt;&#x2F;span&gt;&lt;span&gt;                    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[0,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;                    json::to_string(&amp;amp;pubkey).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                    json::to_string(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.created_at).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                    json::to_string(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.kind).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                    json::to_string(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.tags).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                    json::to_string(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.content).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                ))
&lt;&#x2F;span&gt;&lt;span&gt;                .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;finalize&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;        )
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;sign&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;signer&lt;&#x2F;span&gt;&lt;span&gt;: &amp;amp;Signer) -&amp;gt; anyhow::Result&amp;lt;Event&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; pubkey = signer.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;public_key&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; id = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;pubkey);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; sig = signer.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;sign&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;id)?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        Ok(Event {
&lt;&#x2F;span&gt;&lt;span&gt;            id,
&lt;&#x2F;span&gt;&lt;span&gt;            sig,
&lt;&#x2F;span&gt;&lt;span&gt;            pubkey,
&lt;&#x2F;span&gt;&lt;span&gt;            data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        })
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You may notice, I also changed up the the type definitions a little bit here. I found it a bit awkward to have the
&lt;code&gt;pubkey&lt;&#x2F;code&gt; of the signing key pair on the &lt;code&gt;EventData&lt;&#x2F;code&gt; struct. So instead, I added it as a parameter to the &lt;code&gt;id()&lt;&#x2F;code&gt;
generation function, which is then utilized in the &lt;code&gt;sign()&lt;&#x2F;code&gt; function. This all seemed a bit cleaner to me,
as I really only have to give the signing &quot;context&quot; &lt;em&gt;once&lt;&#x2F;em&gt; when &lt;code&gt;EventData&lt;&#x2F;code&gt; is actually signed and produces
an &lt;code&gt;Event&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s also worthwhile to note some Rust semantics here. In the &lt;code&gt;sign()&lt;&#x2F;code&gt; function signature (no pun intended), there&#x27;s
this &lt;code&gt;self&lt;&#x2F;code&gt; parameter, which doesn&#x27;t include the more common &lt;code&gt;&amp;amp;&lt;&#x2F;code&gt;, or immutable reference. In this case, we
actually want the &lt;code&gt;sign()&lt;&#x2F;code&gt; function to take &quot;ownership&quot; of the &lt;code&gt;EventData&lt;&#x2F;code&gt;, consuming it in a such a way that prevents
the caller from using &lt;em&gt;their&lt;&#x2F;em&gt; reference to it anymore, which helps us to encode this idea that once you sign &lt;code&gt;EventData&lt;&#x2F;code&gt;,
it really shouldn&#x27;t be modified anymore.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;the-result&quot;&gt;The Result&lt;&#x2F;h4&gt;
&lt;p&gt;Finally, we can generate a fully valid nostr &lt;code&gt;Event&lt;&#x2F;code&gt;!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;clap::Parser;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;roostr::event::{Kind, EventData};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;roostr::signature::Signer;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;roostr::Config;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; anyhow::Result&amp;lt;()&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    dotenv::dotenv().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;ok&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; config = Config::parse();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; signer = Signer::from_config(&amp;amp;config)?;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; data = EventData {
&lt;&#x2F;span&gt;&lt;span&gt;        created_at: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1678648700&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        kind: Kind::Text,
&lt;&#x2F;span&gt;&lt;span&gt;        tags: vec![],
&lt;&#x2F;span&gt;&lt;span&gt;        content: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hello, reader!&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;into&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; event = data.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;sign&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;signer)?;
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{:#?}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, event);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    Ok(())
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s &lt;code&gt;cargo run&lt;&#x2F;code&gt; that.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;Event {
&lt;&#x2F;span&gt;&lt;span&gt;    id: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;bba6b589e2cc685eb26b5558184ed3022bc5181364d16b83be73410d37fed84e&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    sig: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;c91c91e86b6359e5ba364cd40450886ed3f34e0b7a0f5765c23f8426390ff5e23ac2c7846a351bca62b18482414a2dea7d09051b20745ff9107f11e8320a8ea7&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    pubkey: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;e241ea8967a43f8c67eff8b16311a204fcc50906e5c2c5c87d1ec7422ea1a6d5&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    data: EventData {
&lt;&#x2F;span&gt;&lt;span&gt;        created_at: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1679855436&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        kind: Text,
&lt;&#x2F;span&gt;&lt;span&gt;        tags: [],
&lt;&#x2F;span&gt;&lt;span&gt;        content: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hello, reader!&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;next-up&quot;&gt;Next Up&lt;&#x2F;h3&gt;
&lt;p&gt;I do plan on releasing this on GitHub in the near future. Once I have simple relay interaction working, I think it&#x27;ll
make sense to do so. Part of this exercise is for my own learning experience as well, so I&#x27;m not quite ready to accept
pull requests just yet.&lt;&#x2F;p&gt;
&lt;p&gt;In the next post in this series, we&#x27;ll look at connecting to relays, and building a simple user interface to read
messages from those relays.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>nostr NIP-05 DNS Verification for a Zola-based Static Site</title>
        <published>2023-02-05T00:00:00+00:00</published>
        <updated>2023-02-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Nick Miller
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nickmonad.blog/2023/nostr-verification/"/>
        <id>https://nickmonad.blog/2023/nostr-verification/</id>
        
        <content type="html" xml:base="https://nickmonad.blog/2023/nostr-verification/">&lt;p&gt;&lt;i&gt;Skip down to the &lt;code&gt;NIP-05 &amp;amp; Zola&lt;&#x2F;code&gt; section if you already know about nostr and want to get right into the technical
specifics about nostr DNS verification with zola.&lt;&#x2F;i&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For the past few weeks, I&#x27;ve been doing some research on &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nostr-protocol&#x2F;nostr&quot;&gt;nostr&lt;&#x2F;a&gt;,
a new approach for decentralized social-networking.&lt;&#x2F;p&gt;
&lt;p&gt;What I appreciate about nostr is how radically simple the protocol really is. Anybody who is reasonably technical can
understand the protocol at a high-level in just a few minutes, and it&#x27;s fun to think about all the challenges
and possibilities that will arise as it scales to millions of users.&lt;&#x2F;p&gt;
&lt;p&gt;I love how straightforward and to-the-point this answer is from the protocol FAQ, which I think is really getting at
where all the hype is coming from.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;i&gt;This is very simple. Why hasn&#x27;t anyone done it before?&lt;&#x2F;i&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t know, but I imagine it has to do with the fact that people making social networks are either companies
wanting to make money or P2P activists who want to make a thing completely without servers.
They both fail to see the specific mix of both worlds that Nostr uses.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Sometimes the best solutions are a blend of various approaches that, on their own, were optimized too far in one
direction or another, as noted in the answer above. Nostr takes a different approach. It uses well-defined and
standardized technology that has been around for quite some time, but never put together in this specific way. I think
a lot of people see that same magic in bitcoin as well.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll save more philosophical rambling about nostr for another time. Now that I&#x27;ve finally setup a key-pair and
started posting to relays, I wanted to make a quick post about how I setup DNS verification of my public key using
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;getzola.org&quot;&gt;&lt;code&gt;zola&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, the static site generator this blog is rendered with.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;nip-05&quot;&gt;NIP-05&lt;&#x2F;h3&gt;
&lt;p&gt;The nostr protocol development process has the notion of a &quot;NIP&quot;, or a &quot;Nostr Implementation Possibility&quot;, in a similar
vein to bitcoin&#x27;s &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;bips.dev&quot;&gt;BIPs&lt;&#x2F;a&gt; (&quot;Bitcoin Improvement Proposals&quot;). These are ways the community can propose
new ideas or extentions to the base nostr protocol. Some may be mandatory, but most are optional, and can be specified
for relays, clients, or both.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nostr-protocol&#x2F;nips&#x2F;blob&#x2F;master&#x2F;05.md&quot;&gt;NIP-05&lt;&#x2F;a&gt; describes how clients can use DNS and a simple
HTTP &lt;code&gt;GET&lt;&#x2F;code&gt; request to add an extra mapping for a user on nostr. This can give some extra assurance to a follower
that who they are following is legitimate, or mark them as part of an organization (maybe a company or development group).&lt;&#x2F;p&gt;
&lt;p&gt;Basically, a user can sign a nostr event published to relays that sets metadata about their &quot;account&quot;
(technically, their public key). One of those fields that may be set is a &lt;code&gt;nip05&lt;&#x2F;code&gt; field, containing a &lt;code&gt;name@domain.com&lt;&#x2F;code&gt;
value. Clients can make a request to &lt;code&gt;https:&#x2F;&#x2F;domain.com&#x2F;.well-known&#x2F;nostr.json?name=name&lt;&#x2F;code&gt; and if the public key returned
matches the public key of the signed metadata event, that &quot;account&quot; is a considered verified against that domain.&lt;&#x2F;p&gt;
&lt;p&gt;To be fair, if that domain is ever compromised in some way, the key mapping could also be compromised, misleading
followers. But, it&#x27;s a reasonable level of assurance, considering we&#x27;re operating in a decentralized context.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;nip-05-zola&quot;&gt;NIP-05 &amp;amp; Zola&lt;&#x2F;h3&gt;
&lt;p&gt;Since I happen to own a custom domain, and I use &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;getzola.org&quot;&gt;&lt;code&gt;zola&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to render my content as a static site, I
wanted to figure out a way to add &lt;code&gt;NIP-05&lt;&#x2F;code&gt; verification to my nostr profile.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, &lt;code&gt;zola&lt;&#x2F;code&gt; can do this pretty easily, but the setup is not immediately obvious. We know that for page content,
we should have markdown files and folders under our &lt;code&gt;content&#x2F;&lt;&#x2F;code&gt; directory, and that these map to URL paths during the
build process.&lt;&#x2F;p&gt;
&lt;p&gt;Following the &lt;code&gt;NIP-05&lt;&#x2F;code&gt; specification, we need to have a path like &lt;code&gt;&#x2F;.well-known&#x2F;nostr.json&lt;&#x2F;code&gt; under our domain we want
to verify against. If we create that file at &lt;code&gt;content&#x2F;.well-known&#x2F;nostr.json&lt;&#x2F;code&gt;, build our site and try to navigate there,
we actually get a &lt;code&gt;404&lt;&#x2F;code&gt; response.&lt;&#x2F;p&gt;
&lt;p&gt;This is because zola won&#x27;t build any directory or include files in the final path structure if there isn&#x27;t a &lt;code&gt;_index.md&lt;&#x2F;code&gt;
present in that directory. To get around this, we can create one at &lt;code&gt;content&#x2F;.well-known&#x2F;_index.md&lt;&#x2F;code&gt; that looks like,&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;+++
&lt;&#x2F;span&gt;&lt;span&gt;title = &amp;quot;nostr.json&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;render = false
&lt;&#x2F;span&gt;&lt;span&gt;+++
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Looking at the section configuration options &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;content&#x2F;section&#x2F;#front-matter&quot;&gt;here&lt;&#x2F;a&gt;
we can see that the &lt;code&gt;render&lt;&#x2F;code&gt; option is used to prevent any actual template from being rendered into the final path,
but still leaves &lt;code&gt;&#x2F;.well-known&#x2F;nostr.json&lt;&#x2F;code&gt; available to request.&lt;&#x2F;p&gt;
&lt;p&gt;Once &lt;code&gt;_index.md&lt;&#x2F;code&gt; and &lt;code&gt;nostr.json&lt;&#x2F;code&gt; are present under &lt;code&gt;content&#x2F;.well-known&#x2F;&lt;&#x2F;code&gt;, we&#x27;re ready to build, re-publish our site,
and set our &lt;code&gt;nip05&lt;&#x2F;code&gt; identifier in a nostr client that supports it. (I used &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;iris.to&quot;&gt;iris.to&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;If you want to see the final result for this site, checkout
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;nickmonad.blog&#x2F;.well-known&#x2F;nostr.json&quot;&gt;nickmonad.blog&#x2F;.well-known&#x2F;nostr.json&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;quick-things-to-note&quot;&gt;Quick things to note.&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;If you are not using your own server to host your zola static site, your hosting provider must support setting
custom header responses for specific paths. In this case, I had to ensure I was setting &lt;code&gt;Access-Control-Allow-Origin: *&lt;&#x2F;code&gt;
as a response header to all &lt;code&gt;&#x2F;.well-known&#x2F;*&lt;&#x2F;code&gt; requests, so Javascript web clients can make that request.&lt;&#x2F;li&gt;
&lt;li&gt;If you want to just verify your top-level domain, you need to set &lt;code&gt;&quot;_&quot;&lt;&#x2F;code&gt; as the &quot;name&quot; in your &lt;code&gt;nostr.json&lt;&#x2F;code&gt;. See the
link above for this site as an example.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Initial Commit</title>
        <published>2022-12-09T00:00:00+00:00</published>
        <updated>2022-12-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Nick Miller
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nickmonad.blog/2022/initial-commit/"/>
        <id>https://nickmonad.blog/2022/initial-commit/</id>
        
        <content type="html" xml:base="https://nickmonad.blog/2022/initial-commit/">&lt;p&gt;I did the thing.&lt;&#x2F;p&gt;
&lt;p&gt;As they say, the 3rd (or maybe 10th) time is the charm. And just after I turned 30 too.&lt;&#x2F;p&gt;
&lt;p&gt;Keep an eye out for some upcoming posts. I&#x27;m currently setting up NixOS on my homelab server, so that should be a
fun one to write about.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; anyhow::Result&amp;lt;()&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;initial commit!&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;    Ok(())
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
</feed>
