<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Zola Gonano</title>
    <description>The latest articles on DEV Community by Zola Gonano (@zolagonano).</description>
    <link>https://dev.to/zolagonano</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1217963%2Ff2bf04fe-5c94-4a1b-8a69-24cd20efcd21.jpeg</url>
      <title>DEV Community: Zola Gonano</title>
      <link>https://dev.to/zolagonano</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zolagonano"/>
    <language>en</language>
    <item>
      <title>RawLink: Building a VPN Protocol From Scratch</title>
      <dc:creator>Zola Gonano</dc:creator>
      <pubDate>Mon, 19 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/zolagonano/rawlink-building-a-vpn-protocol-from-scratch-2aab</link>
      <guid>https://dev.to/zolagonano/rawlink-building-a-vpn-protocol-from-scratch-2aab</guid>
      <description>&lt;p&gt;I always wanted to give making a new VPN/proxy protocol a shot to understand what happens at the packet level to evade censorship or make a secure tunnel to a server, and to see what it takes to actually design and implement one from scratch.&lt;/p&gt;

&lt;p&gt;What I wanted to make was something very simple, something without too much overhead and complication that would still result in a reliable encrypted tunnel that could bypass DPI (deep packet inspection) firewalls and be used as a censorship circumvention tool.&lt;/p&gt;

&lt;p&gt;But little did I know how much more complicated it would get than I initially intended. The goal was simple, though: have a simple client-server model in which the user would connect to the client through a SOCKS5 proxy, and the client would send encrypted packets through the established session, encrypting everything with a pre-shared key to keep things simple.&lt;/p&gt;

&lt;p&gt;So, with the help of AI (a GLM 5.3-flash model, if you’re wondering), I got to coding, tweaking my designs as I implemented them.&lt;/p&gt;

&lt;p&gt;The project became RawLink.&lt;/p&gt;

&lt;p&gt;What started as a simple encrypted TCP tunnel eventually grew into a protocol with packet framing, multiplexed streams, authenticated encryption, replay protection, header masking, traffic obfuscation, SSH protocol mimicry, backpressure, connection pooling, and failure detection.&lt;/p&gt;

&lt;p&gt;Here, I wanted to write a blog post to explain what’s going on with the protocol and how it works, in case anyone ends up using it, and to share my experience developing something with the assistance of AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  The original idea
&lt;/h2&gt;

&lt;p&gt;At its core, RawLink follows a client-server model.&lt;/p&gt;

&lt;p&gt;The client runs a local SOCKS5 proxy. Applications connect to it as they would connect to any other SOCKS5 proxy, and the client forwards those connections through an encrypted tunnel to a remote server. The server then opens the requested connections and relays data in both directions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser / Application
        |
        | SOCKS5
        v
   RawLink Client
        |
        | Encrypted TCP Tunnel
        |
   RawLink Server
        |
        | Ordinary TCP Connections
        v
   Destination Servers

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I chose SOCKS5 because it’s very simple. Using it, the client doesn’t need to understand HTTP or the application-layer protocols being transported. It simply knows where a connection should go and how to move bytes between the local socket and the remote server.&lt;/p&gt;

&lt;p&gt;I chose TCP as the carrier protocol because it already handles reliable delivery, ordering, retransmission, and congestion control. Implementing those mechanisms myself would lead to something really messy, unreliable, and overcomplicated, and it wouldn’t be as good.&lt;/p&gt;

&lt;p&gt;But it still came with trade-offs, of course. TCP introduces head-of-line blocking, and tunnelling multiple connections through a single TCP connection can make the effects of packet loss much worse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond encryption
&lt;/h2&gt;

&lt;p&gt;Well, we all know that encryption alone is not enough to bypass sophisticated censorship firewalls. Nowadays, firewalls can identify patterns in encrypted traffic and block it with ease.&lt;/p&gt;

&lt;p&gt;Encryption should be there to protect the contents of a connection, though, but it doesn’t automatically hide the fact that a particular protocol is being used. A DPI system still checks packet sizes, timing, framing, handshake patterns, and other characteristics without decrypting a single byte.&lt;/p&gt;

&lt;p&gt;For the encryption, I went with AES-256-GCM since most hardware has built-in hardware acceleration for it, and the GCM mode provides authentication for encrypted packets without me having to implement it from scratch.&lt;/p&gt;

&lt;p&gt;So, I knew that I needed something beyond just encryption, something to either make the packets look like nothing in particular or make them look like something ordinary (like TLS or, in this case, SSH).&lt;/p&gt;

&lt;p&gt;And I ended up with an optional obfuscation layer, but I decided to keep it separate in the architecture. RawLink’s core protocol would handle streams, packets, encryption, and session management, while the optional obfuscation layer would change how those packets are carried over the network, allowing me to add more obfuscation schemes later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the packet format
&lt;/h2&gt;

&lt;p&gt;The next step was deciding what a packet should actually look like.&lt;/p&gt;

&lt;p&gt;I needed enough information in each packet to identify the session, determine the packet’s position in the sequence, and decrypt the payload. At the same time, I wanted to keep the fixed overhead small.&lt;/p&gt;

&lt;p&gt;What I ended up with was a packet format that uses a 17-byte header followed by an encrypted body and an authentication tag.&lt;/p&gt;

&lt;p&gt;The header contains a protocol version, a key-phase bit, reserved bits, a session identifier, and a packet number. The encrypted body contains the actual protocol frames.&lt;/p&gt;

&lt;p&gt;The version field allows incompatible protocol revisions to reject each other. Rather than letting different implementations interpret the same bytes in different ways, a peer that doesn’t understand the version can reject the packet.&lt;/p&gt;

&lt;p&gt;The session identifier serves two purposes. It lets the server identify the session, and it contributes to deriving the session’s cryptographic keys. This means that two tunnels using the same PSK don’t automatically end up using identical session keys.&lt;/p&gt;

&lt;p&gt;The packet number increments with every packet sent in a particular direction. It gives the receiver a way to track packet progression and provides the input needed to construct a unique nonce for each packet, which is needed for GCM encryption.&lt;/p&gt;

&lt;p&gt;At first, I had to keep the header readable because the receiver needs some of this information before it can decrypt the body. That decision would eventually create another problem, which I’ll get to later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authenticated encryption
&lt;/h2&gt;

&lt;p&gt;As I said, I ended up with AES-256-GCM for encrypting the packets.&lt;/p&gt;

&lt;p&gt;What matters here is the GCM mode, which provides authenticated encryption, meaning the receiver can verify that the ciphertext and the authenticated metadata have not been modified. This is very important for preventing tampering when transferring data.&lt;/p&gt;

&lt;p&gt;The header is supplied as additional authenticated data (AAD). Although AAD isn’t encrypted by the AEAD operation, it is bound to the authentication tag. Changing an authenticated header field therefore causes verification to fail.&lt;/p&gt;

&lt;p&gt;RawLink also derives separate keys for client-to-server and server-to-client traffic using HKDF-SHA256, with the session identifier incorporated into the derivation.&lt;/p&gt;

&lt;p&gt;I kept the directions separate to avoid sharing the same key and nonce space between both sides. This is really important with AES-GCM, where reusing a key-nonce pair can compromise security.&lt;/p&gt;

&lt;p&gt;The nonce is generated from a per-direction salt and the packet number. Since packet numbers advance monotonically, the protocol can generate nonces without negotiating a new random nonce for every packet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why use a pre-shared key?
&lt;/h3&gt;

&lt;p&gt;I just didn’t want to get into complicated handshakes, key derivation, and certificates.&lt;/p&gt;

&lt;p&gt;A 32-byte PSK gives the client and server a shared secret from which session-specific keys can be derived. It keeps configuration straightforward and avoids introducing certificate infrastructure just to establish the tunnel.&lt;/p&gt;

&lt;p&gt;Well… It wasn’t that great, and it had its downsides, since key distribution now becomes the user’s responsibility. It also meant no forward secrecy, which kinda sucks, but I’m okay with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiplexing connections with frames
&lt;/h2&gt;

&lt;p&gt;A browser doesn’t make just one connection when loading a website. It may open multiple connections to fetch HTML, images, scripts, and other resources.&lt;/p&gt;

&lt;p&gt;Creating a separate tunnel for every application connection would be wasteful. Instead, RawLink multiplexes multiple logical streams over one established TCP connection.&lt;/p&gt;

&lt;p&gt;To do that, I introduced a framing layer. This way, each frame is a small, typed message containing a type, a body length, and a body. Different frame types represent different protocol events, including opening a stream, transmitting data, closing a stream, resetting a stream, requesting a remote TCP connection, and sending keepalives.&lt;/p&gt;

&lt;p&gt;This way, I could fit multiple frames into one packet, allowing the sender to batch several operations together rather than paying the fixed packet overhead for every small piece of data. Like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application Connections
        |
      Streams
        |
       Frames
        |
      Packets
        |
  Carrier / Obfuscation
        |
       TCP

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this model, each layer does a specific thing. The stream layer manages application connections, the frame layer represents protocol operations, the packet layer handles encryption and authentication, and the carrier layer handles transmission over TCP.&lt;/p&gt;

&lt;p&gt;This model would also make debugging easier. If a stream fails to close correctly, the problem can be investigated at the stream and frame layers, narrowing down the area that needs debugging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why stream offsets?
&lt;/h3&gt;

&lt;p&gt;RawLink’s DATA frames carry a stream identifier, an offset, and a payload.&lt;/p&gt;

&lt;p&gt;TCP already guarantees ordered delivery, so at first glance, explicit offsets might seem unnecessary. But offsets provide a way to track how much data has been transmitted and received on each logical stream independently.&lt;/p&gt;

&lt;p&gt;A close frame includes the final offset, allowing the receiver to check whether the expected amount of stream data has arrived before considering the stream complete.&lt;/p&gt;

&lt;p&gt;This gives the stream protocol an explicit notion of completeness rather than relying only on the underlying socket’s lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits on the DATA frame
&lt;/h2&gt;

&lt;p&gt;During implementation, I had to limit individual DATA frames to 16 KiB.&lt;/p&gt;

&lt;p&gt;When I was testing, a large transfer would continuously produce data, while another connection would only need to send a few small messages. If bulk data dominated the sender’s work, smaller operations could experience unnecessary delays. This was what was happening, and it was really, really annoying to figure out what was causing it.&lt;/p&gt;

&lt;p&gt;Splitting bulk transfers into smaller frames gave the session more opportunities to process other queued work between chunks.&lt;/p&gt;

&lt;p&gt;And 16 KiB wasn’t a magical number, either. It just gives the implementation a unit of work rather than allowing a single enormous data frame to dominate all the processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The header problem
&lt;/h2&gt;

&lt;p&gt;At this point, RawLink had authenticated encryption, separate directional keys, session-specific key derivation, and a framing protocol, and it was kind of working fine.&lt;/p&gt;

&lt;p&gt;But the header was still readable.&lt;/p&gt;

&lt;p&gt;The packet number seemed a bit problematic to me. The first packet in a direction would carry one number, the next would carry the next, and so on. A DPI system could potentially recognize that progression even though the packet body looked like random ciphertext.&lt;/p&gt;

&lt;p&gt;I needed to hide the header without breaking the receiver’s ability to process packets.&lt;/p&gt;

&lt;p&gt;The solution I came up with was to add a header-masking layer using an XChaCha20 keystream.&lt;/p&gt;

&lt;p&gt;Instead of sending the header directly, RawLink would XOR-mask its bytes before putting the packet on the wire. The receiver reverses the operation before attempting to decrypt the packet body.&lt;/p&gt;

&lt;p&gt;This didn’t provide any security benefits for the protocol, but it changed how the header would appear to someone observing it.&lt;/p&gt;

&lt;p&gt;It wasn’t all that simple, though. The server needs the session identifier to derive the session-specific masking keys, but the session identifier is itself inside the header that must be unmasked. The server can’t use information it hasn’t recovered yet.&lt;/p&gt;

&lt;p&gt;The fix I came up with was a two-stage key schedule. The first client packet uses masking material derived from the PSK alone. Once the server unmasks that initial packet and recovers the session identifier, subsequent packets can use masking keys derived from the PSK, session identifier, and direction.&lt;/p&gt;

&lt;p&gt;This lets the protocol move from an initial state into a session-specific state without adding a separate handshake round trip.&lt;/p&gt;

&lt;h2&gt;
  
  
  The session’s deadlock problem
&lt;/h2&gt;

&lt;p&gt;Encrypting the packets was one piece of the puzzle in building a reliable tunnel. The session implementation also needed to behave sensibly when many streams were active, buffers filled up, or one side stopped responding.&lt;/p&gt;

&lt;p&gt;To achieve this, RawLink uses two main threads per session: a receiver and a sender.&lt;/p&gt;

&lt;p&gt;The receiver reads incoming data, processes the carrier framing, unmasks and decrypts packets, checks replay protection, and dispatches frames to the appropriate streams.&lt;/p&gt;

&lt;p&gt;The sender, meanwhile, owns outbound work. It collects queued stream commands, constructs frames and packets, encrypts them, and writes them to the carrier.&lt;/p&gt;

&lt;p&gt;The sender sleeps until work becomes available and is woken through a condition variable rather than continuously polling.&lt;/p&gt;

&lt;p&gt;Dividing the work this way keeps the receive path from having to perform unrelated send-side operations. It also gives outbound packet construction and socket writes a single owner, reducing the number of places that need to coordinate access to session state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backpressure
&lt;/h2&gt;

&lt;p&gt;Each stream has a limited send buffer, with a maximum of 4 MiB of unsent data. When the buffer fills, the application writing into that stream experiences backpressure instead of allowing memory usage to grow indefinitely.&lt;/p&gt;

&lt;p&gt;I had to go with this model to avoid implementing a separate acknowledgement-window protocol (which, at this point, I didn’t even want to think about) just to regulate how much application data can accumulate in userspace. At the same time, I had to fix the annoying reliability issues I was having when testing with real data.&lt;/p&gt;

&lt;p&gt;The point was that the application shouldn’t be allowed to enqueue unlimited data while the network or remote endpoint is stalled.&lt;/p&gt;

&lt;p&gt;Bounded queues make overload visible instead of turning it into infinite memory growth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knowing when a connection has died
&lt;/h2&gt;

&lt;p&gt;A tunnel can fail without either endpoint receiving a clean close notification.&lt;/p&gt;

&lt;p&gt;A cable can be unplugged. A firewall can silently discard packets. The TCP socket might remain apparently open even though communication is no longer possible.&lt;/p&gt;

&lt;p&gt;Without some form of liveness check, a dead tunnel can remain around indefinitely.&lt;/p&gt;

&lt;p&gt;I had to introduce PING and PONG frames to detect this situation. While no other traffic is being received, the sender schedules probes at randomized intervals between 1.2 and 3.6 seconds.&lt;/p&gt;

&lt;p&gt;The session considers the connection dead when there has been ten seconds of receive silence and at least one probe remains unanswered.&lt;/p&gt;

&lt;p&gt;The implementation also uses randomized keepalive timing and variable padding. This avoids making every idle connection send an identical, perfectly periodic message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the client recover automatically
&lt;/h2&gt;

&lt;p&gt;I didn’t want users to have to manually reconnect every time the remote server restarted or a temporary network failure occurred. And temporary network failures were occurring a lot during testing, which was pretty annoying.&lt;/p&gt;

&lt;p&gt;So I came up with a tunnel manager responsible for maintaining the pool of outbound sessions.&lt;/p&gt;

&lt;p&gt;When a tunnel dies, the manager attempts to establish another one using backoff with increasing delays. The delay increases after repeated failures, while randomization prevents many clients from reconnecting at exactly the same time after a shared outage.&lt;/p&gt;

&lt;p&gt;A session that stays alive for a while resets the backoff to its minimum, so a temporary failure doesn’t leave the client waiting unnecessarily long during the next recovery attempt.&lt;/p&gt;

&lt;p&gt;The manager can also open additional tunnels when existing ones approach their stream limits. New streams are routed to the least-loaded available tunnel, and surplus tunnels can be retired after a period of inactivity.&lt;/p&gt;

&lt;p&gt;This way, streams are the units of application work, while tunnels are the units of transport failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens when strangers connect?
&lt;/h2&gt;

&lt;p&gt;When exposing any port to the internet, people WILL try to connect to it. And not all of them will be legitimate users. They might be scanners or people just trying to exhaust resources.&lt;/p&gt;

&lt;p&gt;RawLink limits how many unauthenticated connections can be held simultaneously and requires a connection to provide an authenticated packet within a short admission window. Authenticated sessions are subject to a separate concurrency limit.&lt;/p&gt;

&lt;p&gt;This matters because cryptographic validation isn’t free, and maintaining a large number of incomplete connections consumes resources even if none of them successfully authenticate.&lt;/p&gt;

&lt;p&gt;If a wrong PSK causes an immediate close while an idle connection remains open for several seconds, an observer can potentially distinguish those cases. That behavior can give away a lot of information about the server’s authentication process.&lt;/p&gt;

&lt;p&gt;Because of that, RawLink attempts to make admission failures close within a randomized time window, including failures involving timeouts, malformed data, incorrect keys, and carrier errors.&lt;/p&gt;

&lt;p&gt;This one might be overkill, and it probably is, but it’s what I ended up with.&lt;/p&gt;

&lt;h2&gt;
  
  
  The censorship problem
&lt;/h2&gt;

&lt;p&gt;At this point, RawLink was connecting and forwarding traffic, but that still didn’t make it a circumvention tool, so I had to design and implement the obfuscation layer.&lt;/p&gt;

&lt;p&gt;And I ended up with two completely different modes to do so.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making traffic look random
&lt;/h3&gt;

&lt;p&gt;The first mode, &lt;code&gt;rand&lt;/code&gt;, was designed to make the connection look like a stream of random bytes rather than a recognizable custom protocol.&lt;/p&gt;

&lt;p&gt;It uses a handshake built around fresh random values and a message authentication code derived from the PSK. The handshake has a fixed size, including when the server responds to an invalid probe.&lt;/p&gt;

&lt;p&gt;After the handshake, the carrier uses a masked framing header and a per-connection keystream. Frames can also contain random padding, so the size of a transmitted frame doesn’t directly reveal the exact size of its application payload.&lt;/p&gt;

&lt;p&gt;I also included optional dummy traffic during idle periods. Without it, a quiet tunnel might produce no packets for a while and then suddenly resume transmitting application data. Depending on the surrounding traffic patterns, that behavior can itself provide a useful signal to an observer.&lt;/p&gt;

&lt;p&gt;One issue with this was that padding and dummy traffic consume bandwidth, and randomness alone doesn’t make a protocol indistinguishable from every other protocol.&lt;/p&gt;

&lt;p&gt;This mode as a whole was just an attempt to reduce protocol fingerprinting, but it still wasn’t something I would be happy with. An observer may still classify a connection based on packet-size distributions, directionality, timing, or other signals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mimicking SSH
&lt;/h3&gt;

&lt;p&gt;With this mode, rather than making the connection look like noise, I tried to make the initial exchange resemble an ordinary OpenSSH session.&lt;/p&gt;

&lt;p&gt;A custom protocol that looks like an entirely new transport stands out, whereas a connection that follows a familiar protocol’s handshake has a more plausible explanation for its existence.&lt;/p&gt;

&lt;p&gt;The implementation mimics the SSH identification exchange and key-exchange message sequence, including the transition into the encrypted phase. The RawLink handshake then takes place in the post-key-exchange portion of the connection.&lt;/p&gt;

&lt;p&gt;Here, the design goal is to reproduce the observable structure that an SSH-aware observer expects to see, rather than merely putting an SSH-looking string at the beginning of an otherwise custom connection.&lt;/p&gt;

&lt;p&gt;Also, mimicking SSH is only useful where SSH-like traffic is permitted and sufficiently common to blend into the surrounding network. A network that blocks SSH, restricts it to approved servers only, or uses behavioral analysis may still identify or block the tunnel.&lt;/p&gt;

&lt;h2&gt;
  
  
  What RawLink is, and what it isn’t
&lt;/h2&gt;

&lt;p&gt;Well, it’s nothing special now that I look at it, but it was still something interesting to build, even if AI was involved.&lt;/p&gt;

&lt;p&gt;I don’t usually generate code with AI because sometimes it’s easier to write code than to read someone else’s code, and for a good part of the project, it felt like that. But I eventually got the hang of it.&lt;/p&gt;

&lt;p&gt;This project became much larger than I intended, but it surely taught me a lot about networks and how censorship systems work. But a large project is not necessarily a good one, and this project is no exception. The security of the protocol is not proven yet, the encryption hasn’t been audited by anyone besides me, and it lacks a lot of features, like perfect forward secrecy and so on.&lt;/p&gt;

&lt;p&gt;If you’re interested in seeing how it turned out, I’ve published it as free and open-source software on GitHub, and I appreciate any feedback or improvements to the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://github.com/zolagonano/rawlink" rel="noopener noreferrer"&gt;http://github.com/zolagonano/rawlink&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vpn</category>
      <category>tunneling</category>
      <category>networks</category>
      <category>encrypted</category>
    </item>
    <item>
      <title>AI Disclosure: My Blog Posts and Other Projects</title>
      <dc:creator>Zola Gonano</dc:creator>
      <pubDate>Sat, 12 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/zolagonano/ai-disclosure-my-blog-posts-and-other-projects-3g8d</link>
      <guid>https://dev.to/zolagonano/ai-disclosure-my-blog-posts-and-other-projects-3g8d</guid>
      <description>&lt;p&gt;I use AI a lot in my day-to-day life, mostly for personal research and quick questions, or to write code for things that I’m not going to publish online as Free and Open Source Software and I’m too lazy to write them myself.&lt;/p&gt;

&lt;p&gt;What I don’t use it for is my blog posts, books, and public coding projects, and the reason is very simple: I respect everyone who reads these posts and books or uses my projects, and I highly appreciate their time, and I can’t bring myself to wasting their time with AI-generated slop. Even if it takes much more time and effort on my side and they don’t turn out perfect.&lt;/p&gt;

&lt;p&gt;Besides that, the internet, and especially personal and technical blogs, used to be our own independent publications where everything we wrote had a piece of our opinions in them. We were putting a part of our consciousness out there, and nowadays it’s just AI-generated slop to gain fame or followers quickly.&lt;/p&gt;

&lt;p&gt;So, obviously, that’s not something that I’d like to contribute to. That’s why this blog will remain AI (semi)-free and ad-free.&lt;/p&gt;

&lt;p&gt;Semi-free because I do use AI in my books, blogs, and even code, but not in a generative sense. Here are my reasons and how I use AI for them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I am not a native English speaker. Even though I am fluent at it now, I still make mistakes, especially in grammar. So after I finish writing my blog posts or a book chapter, I use AI to fix my typos and grammar errors, which, besides saving me so much time, also allows me to focus purely on writing and not care about minor grammar errors or typos. And this is the exact prompt I use:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Also, I sometimes get stuck when researching and reading documents and codebases, or I get exhausted if there are too many, and I use an AI with a deep research feature to help me with researching.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I do not write or architect my code with AI, but it’s nice to use AI to improve what’s already written, especially security-wise. (Also, I sometimes let AI write my code’s documentation when I have already explained to it what it should EXACTLY write.)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If anything has been AI-generated (most likely personal scripts that I have decided to publish), I will explicitly state that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Did I Explain All This?
&lt;/h2&gt;

&lt;p&gt;Because everyone who’s reading or using my works deserves this level of transparency about how those are made. And I believe a lot more people should be open about that, as it’s becoming harder and harder to distinguish between what’s human-generated and what’s AI-generated.&lt;/p&gt;

&lt;p&gt;I personally like to read imperfect and flawed articles and books that someone actually put thought and energy into rather than something someone generated to farm views and chase money.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>disclosure</category>
    </item>
    <item>
      <title>Replicating Continuwuity without touching the codebase</title>
      <dc:creator>Zola Gonano</dc:creator>
      <pubDate>Sat, 04 Jul 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/zolagonano/replicating-continuwuity-without-touching-the-codebase-4iic</link>
      <guid>https://dev.to/zolagonano/replicating-continuwuity-without-touching-the-codebase-4iic</guid>
      <description>&lt;blockquote&gt;
  &lt;p&gt;This post is written by a human being (me).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Personally I am not a fan of RocksDB because of it being such a headache to get basic things working like replication, clustering, high availability. But I recently used continuwuity to host a matrix homeserver hosting thousands of users (it never meant to grow that much) and the whole time I had this fear of losing everyone’s data because I had no replication or backups of the database and no reliable way of doing so.&lt;/p&gt;

&lt;p&gt;What couldn’t have been done?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Omnipaxos&lt;/strong&gt;&lt;br&gt;
Omnipaxos is an interesting tool. It is a consensus layer that sits in between your program and your rocksdb database allowing you to have real time replications and high availability clustering.&lt;/p&gt;

&lt;p&gt;But this required changing continuwuity’s codebase which meant hours of reading code and changing and taking a huge risk swapping the homeserver to an untested modified version in production like this.&lt;/p&gt;

&lt;p&gt;So this option wasn’t really a good one for this case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rocksplicator&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rocksplicator is a tool developed by Pinterest to provide replication and clustering for RocksDB database but the documents weren’t very clear and I simply couldn’t get it running.
But logically this might be one of the better options for this job if you get it running properly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lsyncd&lt;/strong&gt;&lt;br&gt;
Lsyncd is a tool that watches for file changes and transfers the changes over to the specified destination using rsync.&lt;/p&gt;

&lt;p&gt;This is perfect for a lot of syncing tasks but as long as the files aren’t constantly opened and changed since that would cause corrupted data on the destination and repairing rocksdb database isn’t much of an easy task either.&lt;/p&gt;

&lt;p&gt;Although I ended up using lsyncd for media storage of the homeserver and the database without causing corruption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What worked?&lt;/strong&gt;&lt;br&gt;
RocksDB database doesn’t allow multiple mutable connections but it can be opened as read-only alongside another connection, and it also has a feature named checkpointing.&lt;/p&gt;

&lt;p&gt;Checkpointers are essentially a checkpoint in time in which the database can be restored to without any corruption. And it does that using links in the filesystem so it doesn’t generate new copies of the database every time a checkpoint is created.
This keeps every original wording and sentence structure intact while fixing spelling, grammar, capitalization consistency, and minor punctuation issues.
And Lsyncd can be used here to detect the corresponding files to the links and transfer the checkpoints to the destination.&lt;/p&gt;

&lt;p&gt;But there was a catch. This couldn’t be done in real time and had to be done periodically. Which is fine in this case, being a few minutes behind in a matrix homeserver isn’t such a big issue and the server will eventually catch up after some time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The execution&lt;/strong&gt;&lt;br&gt;
To execute the idea I decided to go with rust and wrote a simple program that creates a checkpoint and preserves the previous checkpoint (in case of incidents during checkpointing) every time it’s executed.&lt;/p&gt;

&lt;p&gt;So it’s a very basic tool, it’s nothing fancy.&lt;/p&gt;

&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rocksdb&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;ColumnFamilyDescriptor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;checkpoint&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Checkpoint&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Usage: {} &amp;lt;db_path&amp;gt; &amp;lt;checkpoint_root&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;db_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;backup_root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="nf"&gt;.create_if_missing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cf_names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;list_cf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cf_descriptors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cf_names&lt;/span&gt;
        &lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;ColumnFamilyDescriptor&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
        &lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create_dir_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/tmp/rocksdb_secondary"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create_dir_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backup_root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;open_cf_descriptors_as_secondary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;db_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/tmp/rocksdb_secondary"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;cf_descriptors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.try_catch_up_with_primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backup_root&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;current_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checkpoint_current"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;prev_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checkpoint_prev"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;staging_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checkpoint_staging"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;staging_dir&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;remove_dir_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;staging_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;checkpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Checkpoint&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;checkpoint&lt;/span&gt;&lt;span class="nf"&gt;.create_checkpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;staging_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;prev_dir&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;remove_dir_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;prev_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// this is to prevent lsyncd from re uploading everything&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_dir&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;current_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;prev_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;staging_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;current_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"Checkpoint created successfully at: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;current_dir&lt;/span&gt;&lt;span class="nf"&gt;.display&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then the current_checkpoint directory is synced to the destination server using lsyncd. When syncing with rsync it needs them to convert the hardlinks into actual files. In rsync it can be done by using the -a and –no-hard-links args.&lt;/p&gt;

&lt;p&gt;Also I wrote a systemd service and timer to run the checkpointer periodically:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;/etc/systemd/system/checkpointer.service&lt;/strong&gt;&lt;/p&gt;

&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;Conduwuit&lt;/span&gt; &lt;span class="err"&gt;RocksDB&lt;/span&gt; &lt;span class="err"&gt;Checkpointer&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;oneshot&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;/usr/local/bin/checkpointer&lt;/span&gt; &lt;span class="err"&gt;/var/lib/conduwuit/db&lt;/span&gt; &lt;span class="err"&gt;/var/lib/conduwuit/checkpoints&lt;/span&gt;
&lt;span class="py"&gt;LimitNOFILE&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1048576&lt;/span&gt; &lt;span class="c"&gt;# Increase if you have a very large database&lt;/span&gt;

&lt;span class="c"&gt;# retry checkpointing if a file is still used by primary connection&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;on-failure&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;

&lt;span class="py"&gt;StartLimitBurst&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;/etc/systemd/system/checkpointer.timer&lt;/strong&gt;&lt;/p&gt;

&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;Run&lt;/span&gt; &lt;span class="err"&gt;checkpointer&lt;/span&gt; &lt;span class="err"&gt;on&lt;/span&gt; &lt;span class="err"&gt;timer&lt;/span&gt;

&lt;span class="nn"&gt;[Timer]&lt;/span&gt;
&lt;span class="py"&gt;OnBootSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="err"&gt;min&lt;/span&gt;
&lt;span class="py"&gt;OnUnitActiveSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="err"&gt;min&lt;/span&gt; &lt;span class="c"&gt;# Increase&lt;/span&gt;
&lt;span class="py"&gt;Persistent&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;timers.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It could’ve been done with tokio and timers in the rust code itself but I didn’t want to bloat the code with a functionality that already exists in linux (Also cronjobs would’ve worked fine too).&lt;/p&gt;





&lt;p&gt;The codes are also available on my GitHub at &lt;a href="https://github.com/zolagonano/rocksdb_checkpointer" rel="noopener noreferrer"&gt;github.com/zolagonano/rocksdb_checkpointer&lt;/a&gt; and all contributions on it are welcome and appreciated.&lt;/p&gt;

</description>
      <category>continuwuity</category>
      <category>replication</category>
      <category>rocksdb</category>
    </item>
    <item>
      <title>Haraka Hash: A Look at AES-based PoW for Blockchains</title>
      <dc:creator>Zola Gonano</dc:creator>
      <pubDate>Fri, 22 Aug 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/zolagonano/haraka-hash-a-look-at-aes-based-pow-for-blockchains-5gc</link>
      <guid>https://dev.to/zolagonano/haraka-hash-a-look-at-aes-based-pow-for-blockchains-5gc</guid>
      <description>&lt;p&gt;Haraka Hash is really interesting to me, it’s not a typical checksum hash function like SHA, or Blake, nor it is a KDF like Argon2 or scrypt. Instead, Haraka is designed as an &lt;strong&gt;AES-based permutation&lt;/strong&gt; , optimized for short inputs and extremely high throughput on modern CPUs with AES-NI instructions.&lt;/p&gt;

&lt;p&gt;But why? Why would anyone use this? Well, for crypto…&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use Haraka in Crypto?
&lt;/h2&gt;

&lt;p&gt;Haraka’s core idea is to leverage the hardware acceleration built into almost every modern processor to perform cryptographic mixing much faster than traditional sponge-based constructions. This makes it particularly attractive for &lt;strong&gt;blockchain applications&lt;/strong&gt; , where hash functions aren’t just about integrity of the hash function, rather it’s about fairness and security of the network (against 51% attacks for example).&lt;/p&gt;

&lt;p&gt;In Proof-of-Work (PoW) systems, miners repeatedly compute hashes to find a value below a certain target. Traditional hashes like SHA-256 or Blake2, while very secure cryptographically, don’t exploit modern CPU features fully, which means high-end GPUs or ASICs dominate mining, reducing the fairness of incentives in the network and reduce overall security and stability of the network.&lt;/p&gt;

&lt;p&gt;Haraka flips this: by using &lt;strong&gt;AES instructions&lt;/strong&gt; , CPUs can achieve high throughput cheaply and efficiently, allowing ordinary users with commodity hardware to compete on a more level playing field. This CPU-friendliness is a &lt;strong&gt;decentralization lever&lt;/strong&gt; , reducing the concentration of mining power in the hands of specialized operators (like ASIC farms in case of bitcoin).&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Benefits
&lt;/h3&gt;

&lt;p&gt;Because Haraka favors CPUs, mining success depends less on specialized hardware and more on &lt;strong&gt;wide participation&lt;/strong&gt;. This helps prevent centralization, which is a common vulnerability in PoW networks.&lt;/p&gt;

&lt;p&gt;By leveling the computational playing field, Haraka makes it more costly and difficult for attackers to dominate the network. More participants mean greater &lt;strong&gt;network resilience&lt;/strong&gt; , reducing the likelihood of reorgs or double-spend attacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can It Help Monero?
&lt;/h2&gt;

&lt;p&gt;The 51% attack risk on Monero, which was &lt;a href="https://dev.to/blog/posts/protect-the-freedom-money"&gt;recently highlighted by the Qubic&lt;/a&gt; mining pool, shows how a single actor controlling the majority of hash power can threaten network integrity, by reversing transactions, censoring others, or reorganizing recent blocks.&lt;/p&gt;

&lt;p&gt;Switching Monero’s Proof-of-Work to &lt;strong&gt;Haraka Hash&lt;/strong&gt; could improve the network in several ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CPU-Level Optimization:&lt;/strong&gt; Haraka uses AES-NI instructions for extremely fast hashing on ordinary CPUs. This makes it easier for smaller miners to compete, flattening the hashrate distribution and raising the cost of a 51% attack.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Decentralization:&lt;/strong&gt; More accessible mining encourages broader participation, distributing control across many miners instead of allowing dominance by one pool or a coordinated fleet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Energy Efficiency:&lt;/strong&gt; Haraka reduces CPU cycles per hash, lowering electricity costs for small miners and further promoting a diverse mining ecosystem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using with Hybrid Consensus:&lt;/strong&gt; In hybrid PoW/PoS systems, Haraka’s efficiency allows frequent block attempts without overloading hardware, making attacks that rely solely on hash power far less feasible. (Like what VerusCoin does for it’s consensus, &lt;a href="https://dev.to/blog/posts/veruscoin-everything-that-ethereum_isnt"&gt;read more&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In short, Haraka wouldn’t completely eliminate the possibility of a 51% attack, but it would &lt;strong&gt;raise the barrier significantly&lt;/strong&gt; , making sudden reorganizations or coordinated attacks much harder and costlier.&lt;/p&gt;

</description>
      <category>hash</category>
      <category>aes</category>
      <category>pow</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Making of SARE: How I Designed a File Format for Encrypted Data</title>
      <dc:creator>Zola Gonano</dc:creator>
      <pubDate>Thu, 07 Aug 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/zolagonano/making-of-sare-how-i-designed-a-file-format-for-encrypted-data-1m92</link>
      <guid>https://dev.to/zolagonano/making-of-sare-how-i-designed-a-file-format-for-encrypted-data-1m92</guid>
      <description>&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt; : This might sound like a stupid idea, but believe me, I had good reasons for it.&lt;/p&gt;

&lt;p&gt;In the previous post on the making of &lt;strong&gt;SARE&lt;/strong&gt; , I went over how and why I integrated master seeds into the architecture. That laid the groundwork for consistent key derivation across different cryptographic operations. If you haven’t read that post, you can find it &lt;a href="https://dev.to/blog/posts/making-of-sare-master-seeds-in-hybrid-post-quantum-encryption"&gt;here&lt;/a&gt; - that’ll help this one make more sense.&lt;/p&gt;

&lt;p&gt;At this point, I had all the core building blocks in place.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I had &lt;strong&gt;HybridKEM&lt;/strong&gt; working to exchange shared secrets between parties.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I had &lt;strong&gt;HybridSign&lt;/strong&gt; to sign data and prove its authenticity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And I had encryption ready using AEAD algorithms.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I still needed a way to store the encrypted data. Not just dump ciphertext into a file, but structure it in a way that made sense for machines, future-proofing, and minimal complexity. So this post is about how I designed the binary format that sits at the start of every SARE-encrypted file, and how it helps keep everything structured, verifiable, and extensible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Bother With a Custom Format
&lt;/h2&gt;

&lt;p&gt;You might ask, “Why not just use an existing container format?” Like PGP, PKCS #7, or even ZIP with metadata extensions?&lt;/p&gt;

&lt;p&gt;And yeah, I could have done that. But those formats bring a ton of complexity, assumptions, and legacy cruft I didn’t need. My use case was specific: I wanted a simple, self-contained format that supports hybrid crypto, optional signatures, forward compatibility, and human-readable metadata. Something compact, easy to parse, and still flexible enough to evolve.&lt;/p&gt;

&lt;p&gt;So I built my own.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Header Layout
&lt;/h2&gt;

&lt;p&gt;Here’s the layout I came up with for the file header:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First 9 bytes are the string &lt;code&gt;"SARECRYPT"&lt;/code&gt; - this acts as the magic bytes, so we can verify that the file is using the right format.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, 8 bytes (little-endian) indicating the total length of the header section. This tells the parser how much of the file to read before hitting the encrypted payload.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then 4 bytes for the file format version. This allows backward compatibility.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After that, 8 bytes (little-endian) indicating the length of the metadata section.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And finally, another 8 bytes (little-endian) giving the length of the signature section.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s the fixed structure. What comes next are two variable-length sections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Metadata - encoded as BSON.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Signature - also encoded as BSON. May be empty if not used.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By explicitly length-prefixing each section, the format becomes fully self-framed. No guesswork, no delimiters, no relying on external schemas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why BSON
&lt;/h2&gt;

&lt;p&gt;You might wonder why I went with &lt;strong&gt;BSON&lt;/strong&gt; instead of something more efficient like CBOR or even plain binary structs.&lt;/p&gt;

&lt;p&gt;There were a few practical reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Good &lt;strong&gt;Serde support&lt;/strong&gt; in Rust. BSON integrates cleanly and lets me serialize complex structs with minimal boilerplate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extensibility&lt;/strong&gt;. BSON supports optional fields and nested structures, so the format can grow without breaking old readers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Self-describing&lt;/strong&gt;. Each field includes a key, which helps debugging and forward compatibility.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Readable with tooling&lt;/strong&gt;. BSON can be dumped and inspected if needed. Helps during development and debugging.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I didn’t want to define and maintain a custom binary schema. Encoding Rust structs to BSON with Serde got me everything I needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Goes Into Metadata
&lt;/h2&gt;

&lt;p&gt;The metadata holds all the crypto parameters needed to decrypt and verify the file. Things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Which encryption algorithm was used (e.g., &lt;code&gt;AES256GCM&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What key derivation function (&lt;code&gt;scrypt&lt;/code&gt;, &lt;code&gt;argon2&lt;/code&gt;, etc.) and its parameters&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Salt&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nonce&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optional comment&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optional KEM and signature metadata&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this is captured in this struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Serialize,&lt;/span&gt; &lt;span class="nd"&gt;Deserialize)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HeaderMetadataFormat&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[serde(skip_serializing_if&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Option::is_none"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;flatten)]&lt;/span&gt;
    &lt;span class="n"&gt;kem_metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;KEMMetadataFormat&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="nd"&gt;#[serde(skip_serializing_if&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Option::is_none"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;flatten)]&lt;/span&gt;
    &lt;span class="n"&gt;signature_metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SignatureMetadataFormat&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="nd"&gt;#[serde(flatten)]&lt;/span&gt;
    &lt;span class="n"&gt;encryption_metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EncryptionMetadataFormat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="nd"&gt;#[serde(skip_serializing_if&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Option::is_none"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything is optional where it makes sense. If there’s no signature, it’s omitted. If there’s no KEM data, same thing. And because BSON supports this kind of structure naturally, the result stays clean.&lt;/p&gt;

&lt;p&gt;This makes the format flexible enough to support both password-based encryption and hybrid public key schemes, depending on the use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Encoding Works
&lt;/h2&gt;

&lt;p&gt;Header encoding is pretty mechanical. Build a buffer in the right order, write out the fixed parts first, and then append the variable sections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="nf"&gt;.extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAGIC_BYTES&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "SARECRYPT"&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;header_buffer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;header_buffer&lt;/span&gt;&lt;span class="nf"&gt;.extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.version&lt;/span&gt;&lt;span class="nf"&gt;.to_le_bytes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;metadata_bson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.metadata&lt;/span&gt;&lt;span class="nf"&gt;.encode&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;header_buffer&lt;/span&gt;&lt;span class="nf"&gt;.extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;metadata_bson&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.to_le_bytes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;header_buffer&lt;/span&gt;&lt;span class="nf"&gt;.extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;metadata_bson&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.signature&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;signature_bson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="nf"&gt;.encode_bson&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;header_buffer&lt;/span&gt;&lt;span class="nf"&gt;.extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signature_bson&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.to_le_bytes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;header_buffer&lt;/span&gt;&lt;span class="nf"&gt;.extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signature_bson&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;header_buffer&lt;/span&gt;&lt;span class="nf"&gt;.extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="mi"&gt;0u64&lt;/span&gt;&lt;span class="nf"&gt;.to_le_bytes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="nf"&gt;.extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header_buffer&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.to_le_bytes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="nf"&gt;.extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header_buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;header&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is a binary blob with this structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MAGIC_BYTES || HEADER_LEN || VERSION || METADATA_LEN || METADATA || SIGNATURE_LEN || SIGNATURE?

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No complexity, no dependencies, and everything is length-prefixed. I like simplicity.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Decoding Works
&lt;/h2&gt;

&lt;p&gt;Decoding is just the reverse of encoding. You use a cursor to step through the byte stream, read the lengths, and slice each section accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FormatError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;verify_magic_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;FormatError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;FailedToDecode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrSection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HEADER&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;header_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;read_u64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;read_u32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;metadata_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;read_u64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;metadata_bson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;metadata_length&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HeaderMetadataFormat&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;metadata_bson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;metadata_length&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;signature_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;read_u64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;signature_length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;signature_bson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;signature_length&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;signature_length&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;SignatureFormat&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;decode_bson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signature_bson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;None&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HeaderFormat&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each failure returns a typed &lt;code&gt;FormatError&lt;/code&gt;, so issues can be logged or surfaced in a user-friendly way. Without those ugly panics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing and Roundtrip Validation
&lt;/h2&gt;

&lt;p&gt;To make sure encoding and decoding are consistent, I wrote tests that decode a sample header, re-encode it, and compare the result to a fixed base64 snapshot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;header_format_decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;decoded_header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nn"&gt;HeaderFormat&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;BASE64_STANDARD&lt;/span&gt;&lt;span class="nf"&gt;.decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENCODED_HEADER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected_header&lt;/span&gt;&lt;span class="nf"&gt;.encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;decoded_header&lt;/span&gt;&lt;span class="nf"&gt;.encode&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps catch regressions and ensures the binary layout stays deterministic and compatible. I don’t care about minor reordering of fields in BSON, but the full re-encoded header needs to match.&lt;/p&gt;

&lt;p&gt;After that comes the encrypted body - usually raw ciphertext. The metadata tells you how to decrypt it, what keys to derive, what nonce to use, etc.&lt;/p&gt;

&lt;p&gt;Because the header is self-contained, the decryption pipeline can be fully stateless. You load the header, parse it, and then stream the rest.&lt;/p&gt;




&lt;p&gt;In the next post, I’ll break down how the actual encryption pipeline works and how all of this ties together with key management.&lt;/p&gt;

&lt;p&gt;SARE is available at: &lt;a href="https://sareproject.github.io" rel="noopener noreferrer"&gt;https://sareproject.github.io&lt;/a&gt; and &lt;a href="https://github.com/SareProject" rel="noopener noreferrer"&gt;https://github.com/SareProject&lt;/a&gt; Giving the project a star would be good motivation for me to get back to this old project and hopefully make something usable out of it.&lt;/p&gt;

</description>
      <category>encryption</category>
      <category>postquantom</category>
      <category>formatting</category>
    </item>
    <item>
      <title>Making of SARE: Master Seeds in Hybrid Post-Quantum Encryption</title>
      <dc:creator>Zola Gonano</dc:creator>
      <pubDate>Tue, 05 Aug 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/zolagonano/making-of-sare-master-seeds-in-hybrid-post-quantum-encryption-gje</link>
      <guid>https://dev.to/zolagonano/making-of-sare-master-seeds-in-hybrid-post-quantum-encryption-gje</guid>
      <description>&lt;p&gt;A few years ago, I started working on a project called the Sare Project. Sare stands for Safe At Rest Encryption. I wanted to make a hybrid post-quantum encryption library and tool to be a post-quantum replacement for GPG.&lt;/p&gt;

&lt;p&gt;I have been away from this project for almost a year, and when I opened the codebase I didn’t know what I had done. So I thought as I read my own old code, I could explain why I did certain things and what ideas led me down those paths.&lt;/p&gt;

&lt;p&gt;The first issue was that I wanted something hybrid, meaning it would be the best of both worlds. If the post-quantum algorithms failed, we’d still have our normal algorithms like elliptic curves.&lt;/p&gt;

&lt;p&gt;The issue was that I needed one post-quantum KEM (Key Encapsulation Mechanism) for encapsulation. Encapsulation in cryptography is a method to &lt;strong&gt;securely establish a shared secret key&lt;/strong&gt; between two parties over an insecure channel, using &lt;strong&gt;asymmetric (public-key) cryptography&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The way that these KEMs work is that they generate a public key and a secret key. Then, using the recipient’s public key, a &lt;strong&gt;ciphertext&lt;/strong&gt; (called an “encapsulation”) and a &lt;strong&gt;shared secret&lt;/strong&gt; are produced. On decapsulation, the recipient uses their secret key to recover the shared secret from the ciphertext.&lt;/p&gt;

&lt;p&gt;The idea I had was to combine a post-quantum KEM with an elliptic curve DH (Diffie-Hellman) key exchange. The way that DH key exchanges work is that two parties (Alice and Bob for example) each generate a private/public key pair and exchange public keys to compute a shared secret using properties of modular arithmetic.&lt;/p&gt;

&lt;p&gt;The difference with KEMs is that instead of exchanging keys via exponentiation, the sender &lt;strong&gt;encapsulates&lt;/strong&gt; a secret into a ciphertext using the recipient’s public key. The recipient &lt;strong&gt;decapsulates&lt;/strong&gt; using their private key to recover the secret.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /sare-core/src/hybrid_kem/mod.rs&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;DHKeyPair&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;public_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;secret_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SecretVec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DHAlgorithm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;KEMKeyPair&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;public_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;secret_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SecretVec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;KEMAlgorithm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HybridKEM&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;dh_keypair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DHKeyPair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;kem_keypair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;KEMKeyPair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;HybridKEM&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dh_keypair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DHKeyPair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kem_keypair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;KEMKeyPair&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;HybridKEM&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;dh_keypair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;kem_keypair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;calculate_raw_shared_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;kem_cipher_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;dh_sender_public_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SecretVec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SecretVec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;HybridKEMError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;binding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dh_sender_public_key&lt;/span&gt;&lt;span class="nf"&gt;.to_vec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;diffie_hellman&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;DiffieHellman&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.dh_keypair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;kem_decapsulation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="nn"&gt;Decapsulation&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.kem_keypair.secret_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.kem_keypair.algorithm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;dh_shared_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;diffie_hellman&lt;/span&gt;&lt;span class="nf"&gt;.calculate_shared_key&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;kem_shared_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kem_decapsulation&lt;/span&gt;&lt;span class="nf"&gt;.decapsulate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kem_cipher_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;dh_shared_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kem_shared_secret&lt;/span&gt;&lt;span class="py"&gt;.shared_secret&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This meant that only for exchanging keys, I’d need two secret keys to be generated.&lt;/p&gt;

&lt;p&gt;And that wasn’t it. Another issue was that these exchange algorithms couldn’t be used to sign messages. So once again I had to generate two different keys: one for post-quantum signing and one for signing with elliptic curves. I would include both signatures in the signed outputs so the recipient could verify both: once with the post-quantum algorithm and once with the classical elliptic curve algorithm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /sare/blob/main/sare-core/src/hybrid_sign/mod.rs&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ECKeyPair&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;public_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;secret_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SecretVec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ECAlgorithm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ECSignature&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;keypair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="n"&gt;ECKeyPair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;PQKeyPair&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;public_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;secret_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SecretVec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PQAlgorithm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;PQSignature&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;keypair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="n"&gt;PQKeyPair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wanted the system to be modular, so we could have multiple options for each component’s algorithm. It wouldn’t be a fixed algorithm, and it could be expanded as new algorithms become available.&lt;/p&gt;

&lt;p&gt;For example, in &lt;code&gt;HybridKEM&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /sare/blob/main/sare-core/src/hybrid_kem/mod.rs&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;DHAlgorithm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;X25519&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;KEMAlgorithm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Kyber768&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the issue was that I had to store four different private keys and four different public keys. What I did for public keys relates to how I decided to format keys, which I will explain in the next article about the making of SARE.&lt;/p&gt;

&lt;p&gt;For private keys, I decided to use what most crypto wallets use: a master seed from which all other keys are derived. This way, the user only needs to store a single key, and all private keys are derived from that. The master seed was defined as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /sare/blob/main/sare-core/src/seed/mod.rs&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Seed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;raw_seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SecretVec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I went with a 128-byte seed, just in case aliens come to Earth with their supercomputers. Even they shouldn’t have the power to brute-force it.&lt;/p&gt;

&lt;p&gt;For comparison, to brute-force AES-256 you need to guess&lt;code&gt;115,792,089,237,316,195,423,570,985,008,687,907,853,269,984,665,640,564,039,457,584,007,913,129,639,936&lt;/code&gt; possibilities.&lt;/p&gt;

&lt;p&gt;To brute-force SARE’s 128-byte master seed, you’d need to guess&lt;code&gt;179,769,313,486,231,590,772,930,519,078,902,473,361,797,697,894,230,657,273,430,081,157,732,675,805,500,963,132,708,477,322,407,536,021,120,113,879,871,393,357,658,789,768,814,416,622,492,847,430,639,474,124,377,767,893,424,865,485,276,302,219,601,246,094,119,453,082,952,085,005,768,838,150,682,342,462,881,473,913,110,540,827,237,163,350,510,684,586,298,239,947,245,938,479,716,304,835,356,329,624,224,137,216&lt;/code&gt; possibilities. That’s &lt;code&gt;2^768&lt;/code&gt; times more than AES-256.&lt;/p&gt;

&lt;p&gt;It might seem like a large master seed to remember, but when using Bitcoin’s mnemonic phrases, this turns into 96 words, which is reasonable. You can even write it down on paper if you want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /sare/blob/main/sare-core/src/seed/mod.rs&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Seed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SecretVec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Seed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;raw_seed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;raw_seed_buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="n"&gt;OsRng&lt;/span&gt;&lt;span class="nf"&gt;.fill_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;raw_seed_buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;Seed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;raw_seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;SecretVec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_seed_buffer&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;to_mnemonic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SecretString&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;seed_chunks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.raw_seed&lt;/span&gt;&lt;span class="nf"&gt;.expose_secret&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.chunks_exact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;mnemonic_phrase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seed_chunks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;mnemonic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Mnemonic&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_entropy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Language&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;English&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;mnemonic_phrase&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mnemonic&lt;/span&gt;&lt;span class="nf"&gt;.phrase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="n"&gt;mnemonic_phrase&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nn"&gt;SecretString&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mnemonic_phrase&lt;/span&gt;&lt;span class="nf"&gt;.trim_end&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this solution, users don’t store multiple keys. They store a single master seed, from which all of their keys are derived. If the algorithms change, the master seed stays the same, providing built-in backward compatibility.&lt;/p&gt;

&lt;p&gt;To store this master seed, users have two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store it symmetrically encrypted using AES-KW with a passphrase (which is recommended)&lt;/li&gt;
&lt;li&gt;Store it unencrypted (not recommended)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;KeyWrap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;input_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SecretVec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;KeyWrap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SecretVec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EncryptionError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;input_key&lt;/span&gt;&lt;span class="nf"&gt;.expose_secret&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;EncryptionError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidKeyLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KeyWrap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;input_key&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;SecretVec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EncryptionError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="nf"&gt;.expose_secret&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;input_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.input_key&lt;/span&gt;&lt;span class="nf"&gt;.expose_secret&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.as_slice&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;kek&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;KekAes256&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;kek&lt;/span&gt;&lt;span class="nf"&gt;.wrap_with_padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="nf"&gt;.expose_secret&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To derive unique keys for each algorithm from the master seed, I used magic bytes. Each algorithm gets a different identifier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ED25519_MAGIC_BYTES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;210&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// 0xED25519 in little endian&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;DILITHIUM3_MAGIC_BYTES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;211&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// 0xCD3 in little endian&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;X25519_MAGIC_BYTES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// 0x25519 in little endian&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;KYBER768_MAGIC_BYTES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;104&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// 0x768 in little endian&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, the derivation process was fairly simple. I used HKDF-SHA256 for keys that needed to be 32 bytes and HKDF-SHA512 for keys that needed to be 64 bytes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /sare/blob/main/sare-core/src/kdf/mod.rs&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;HKDF&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="n"&gt;SecretVec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HKDFAlgorithm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;HKDF&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;input_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;expand_sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;additional_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;okm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;KDFError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;hkdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Hkdf&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Sha256&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.salt&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.input_data&lt;/span&gt;&lt;span class="nf"&gt;.expose_secret&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;hkdf&lt;/span&gt;&lt;span class="nf"&gt;.expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;additional_context&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;okm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;expand_sha512&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;additional_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;okm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;KDFError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;hkdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Hkdf&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Sha512&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.salt&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.input_data&lt;/span&gt;&lt;span class="nf"&gt;.expose_secret&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;hkdf&lt;/span&gt;&lt;span class="nf"&gt;.expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;additional_context&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;okm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;In the next article in this series, I’ll try to explain more of it. A lot of things are too basic to explain in a blog post, but if I find anything interesting I’ll try to explain the reasoning behind it and how I implemented it.&lt;/p&gt;

&lt;p&gt;SARE is available at: &lt;a href="https://sareproject.github.io" rel="noopener noreferrer"&gt;https://sareproject.github.io&lt;/a&gt; and &lt;a href="https://github.com/SareProject" rel="noopener noreferrer"&gt;https://github.com/SareProject&lt;/a&gt;Giving the project a star would be good motivation for me to get back to this old project and hopefully make something usable out of it. Right now, it’s just a library.&lt;/p&gt;

</description>
      <category>encryption</category>
      <category>postquantom</category>
      <category>seeds</category>
    </item>
    <item>
      <title>Protect The Freedom Money (Monero)</title>
      <dc:creator>Zola Gonano</dc:creator>
      <pubDate>Wed, 30 Jul 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/zolagonano/protect-the-freedom-money-monero-2h2a</link>
      <guid>https://dev.to/zolagonano/protect-the-freedom-money-monero-2h2a</guid>
      <description>&lt;p&gt;There’s been a lot of talk lately about &lt;strong&gt;Qubic&lt;/strong&gt; , a mining pool that might be trying to pull off a &lt;strong&gt;51% attack&lt;/strong&gt; on &lt;strong&gt;Monero&lt;/strong&gt;. If you’re not familiar, a 51% attack happens when one miner or mining pool gets control of more than half of the total network hashrate in a proof-of-work system, like Monero. That kind of control gives them a lot of power over the network.&lt;/p&gt;

&lt;p&gt;With over 51% of the hashrate, they could do things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Double spend transactions (basically reversing their own payments).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Censor or block other people’s transactions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mess with the network by causing reorgs (basically rewriting recent blocks).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They &lt;em&gt;can’t&lt;/em&gt; create coins out of thin air or steal from other wallets, but even so, just having that much control is a major threat. It totally undermines the idea that Monero is decentralized and trustless.&lt;/p&gt;

&lt;p&gt;What makes this worse is that Monero already tries to avoid this kind of centralization. It uses an algorithm called &lt;strong&gt;RandomX&lt;/strong&gt; , which is designed to favor CPU mining over ASICs, making it easier for individuals to mine and harder for any one player to dominate. But now it looks like Qubic might be gaming the system, or maybe they’ve just built up a huge chunk of the network.&lt;/p&gt;

&lt;p&gt;Even if Qubic hasn’t done anything malicious yet, just the fact that they &lt;em&gt;could&lt;/em&gt; is enough to mess with the trust in the system. That’s a big deal for a privacy coin like Monero, where trustless operation is kind of the whole point.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do to protect Monero?
&lt;/h2&gt;

&lt;p&gt;If Monero’s at risk of a 51% attack, especially from a pool like Qubic gaining too much hashrate, then the community needs to act fast and decisively. Here’s what can be done:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Move your hash away from Qubic&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Stop mining with Qubic&lt;/strong&gt;. If you’re a miner and you care about Monero staying secure and decentralized, you’re actively helping a dangerous situation by mining with a pool that’s near or above 51%. There are other pools out there and better yet, there’s &lt;strong&gt;P2Pool&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Use and promote P2Pool&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;P2Pool&lt;/strong&gt; is a decentralized mining pool that doesn’t have a central operator. No one owns it, and it’s much harder for it to become a single point of failure. If more miners use P2Pool, the network naturally becomes more decentralized. It also pays directly to your wallet, so there’s no middleman holding your coins.&lt;/p&gt;

&lt;p&gt;The downside is it can be a bit trickier to set up, especially for new miners but that’s where education and community support come in. If you have the technical knowledge, help others get started on P2Pool.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Social coordination&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This kind of threat doesn’t always need a technical fix. Sometimes, a social one works faster. If enough people in the community call it out, on Reddit, Twitter, forums, wherever, it creates pressure. Most miners don’t want to kill the coin they’re mining. They just want rewards. So if enough people raise awareness that Qubic is a threat, some of that hash might migrate on its own.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Prepare for a hard fork if needed&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If Qubic doesn’t back down and miners don’t shift away, Monero might need to consider a &lt;strong&gt;hard fork&lt;/strong&gt; to change the mining algorithm again, like it’s done in the past to kick out ASICs. This isn’t ideal, and it shouldn’t be the first move, but it is an option. A hard fork could break Qubic’s hold if it’s relying on some kind of custom setup.&lt;/p&gt;

&lt;p&gt;But forks might split the community if not handled well, and they disrupt normal users. So this should be a last resort.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;Ongoing education and decentralization&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Long-term, the only real defense is decentralization. That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;More miners running their own nodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Less reliance on big pools.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Better tools to make mining easier for individuals.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And maybe most importantly, more awareness. A lot of miners don’t even realize how dangerous centralization is until it’s too late.&lt;/p&gt;

</description>
      <category>monero</category>
      <category>crypto</category>
      <category>web3</category>
      <category>privacy</category>
    </item>
    <item>
      <title>A Technical Look at Iran’s Internet Shutdowns</title>
      <dc:creator>Zola Gonano</dc:creator>
      <pubDate>Sun, 13 Jul 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/zolagonano/a-technical-look-at-irans-internet-shutdowns-2d27</link>
      <guid>https://dev.to/zolagonano/a-technical-look-at-irans-internet-shutdowns-2d27</guid>
      <description>&lt;p&gt;Every time mass protests erupt in Iran, a familiar pattern follows: the flow of information stops. The internet slows to a crawl or disappears entirely.&lt;/p&gt;

&lt;p&gt;But how does a modern country survive cutting itself off from the internet? Wouldn’t that break everything?&lt;/p&gt;

&lt;p&gt;Not quite, because the Islamic Republic has spent the last decade building an internet within the internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The National Information Network (NIN): Isolation by Design
&lt;/h2&gt;

&lt;p&gt;Iran’s &lt;strong&gt;National Information Network (NIN)&lt;/strong&gt; is a state-controlled intranet designed to keep domestic services running even when international connectivity is cut off. Think of it as a national sandbox: websites, banking portals, messaging apps, and e-government services that function entirely within Iran’s borders.&lt;/p&gt;

&lt;p&gt;This setup serves two primary functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It &lt;strong&gt;enables selective blackouts&lt;/strong&gt; : the state can block international platforms (like WhatsApp, Instagram, or news sites) while keeping local services (like state media, banking apps) fully operational.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It &lt;strong&gt;forces ISPs to route traffic through government-controlled gateways&lt;/strong&gt; , making it easier to monitor, filter, or shut down parts of the network on demand.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Layered on top of this is the &lt;strong&gt;IRGFW&lt;/strong&gt; —the Iranian Great Firewall. Modeled after China’s Great Firewall (GFW), but with stricter enforcement and more centralized control, it filters, blocks, and surveils traffic across the country. On paper, it seems solid and impenetrable.&lt;/p&gt;

&lt;p&gt;But there’s always a hole. Always.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Can Be Done in This Situation?
&lt;/h2&gt;

&lt;p&gt;The Iranian internet blockade is aggressive but it’s not perfect. Like any large-scale filtering system, it relies on outdated metadata, and static blocklists. And that’s where cracks begin to form.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Known Flaw: IPs Are Rented, Not Owned
&lt;/h3&gt;

&lt;p&gt;IPv4 addresses are limited and constantly reallocated. Most are rented and passed between hosting providers, resold between datacenters, or migrated across regions. The Iranian filtering system uses &lt;strong&gt;GeoIP databases and BGP information&lt;/strong&gt; to decide which IP ranges to trust and which to block. But those records lag behind the changes.&lt;/p&gt;

&lt;p&gt;An IP that once belonged to a local Iranian provider may now be assigned to a server in Amsterdam, but unless the IRGFW updates its filters in real time (which it doesn’t), that IP might still be allowed through.&lt;/p&gt;

&lt;p&gt;This opens up a small but real opportunity: &lt;strong&gt;scanning IP space&lt;/strong&gt; for reachable proxies, VPNs, or relays that haven’t yet been added to blocklists.&lt;/p&gt;

&lt;p&gt;Yes, it’s mostly brute-force. But when everything else is down, even a single working IP can be a tunnel into the world.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pingtunnel: Slow, but Better Than Nothing
&lt;/h3&gt;

&lt;p&gt;To prevent scanning or circumvention, the IRGFW has tried to block most outbound protocols. But one thing they haven’t (yet) blocked completely is &lt;strong&gt;ICMP&lt;/strong&gt; which is the protocol behind &lt;code&gt;ping&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;ICMP packets are typically used for diagnostics (checking whether a server is alive), and blocking them outright would break a lot of legitimate network functionality. So they’re still allowed, even under heavy shutdown conditions.&lt;/p&gt;

&lt;p&gt;That’s where tools like &lt;strong&gt;Pingtunnel&lt;/strong&gt; come in.&lt;/p&gt;

&lt;p&gt;Pingtunnel allows you to &lt;strong&gt;smuggle data inside ICMP packets&lt;/strong&gt; , essentially tunneling TCP traffic over a stream of pings. It’s really slow and It’s prone to packet loss. But it works, especially for text-based communication, command-line access, or sending small files.&lt;/p&gt;

&lt;p&gt;It’s not secure against DPI or timing analysis. But in a shutdown scenario, &lt;strong&gt;“slow but working” is better than nothing&lt;/strong&gt;. Even a shell session or basic messaging tool can be a critical line to the outside world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starlinks Behind NATs: Iranians Bypass Censorship Despite Criminalization
&lt;/h2&gt;

&lt;p&gt;Despite Iran’s very aggressive criminalization of Starlink receivers, many Iranians still manage to get their hands on one of them. Starlink connections can be securely shared with others by routing traffic through NAT-enabled local routers using encrypted tunnels like WireGuard.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Does “Behind NAT” Means?
&lt;/h3&gt;

&lt;p&gt;Starlink provides an independent internet connection via satellite, bypassing local ISP infrastructure entirely. However, because Starlink devices receive &lt;strong&gt;dynamic, often private IPv4/IPv6 addresses&lt;/strong&gt; and operate outside traditional ISP infrastructure, they are typically &lt;strong&gt;behind NATs&lt;/strong&gt; when connected to local Iranian networks or user devices.&lt;/p&gt;

&lt;p&gt;Here’s what this looks like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;Starlink terminal&lt;/strong&gt; (user terminal + router) connects to the satellite network and obtains an IP address from Starlink’s global network.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Meanwhile, the user’s devices in Iran are connected to the &lt;strong&gt;local ISP’s network&lt;/strong&gt; , which uses its own IP addressing and NAT systems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The user sets up a &lt;strong&gt;local router or gateway inside Iran&lt;/strong&gt; that bridges these two networks, routing local traffic through the Starlink link.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How This Setup Bypasses Censorship
&lt;/h3&gt;

&lt;p&gt;The key is that &lt;strong&gt;Iranian ISPs and firewalls cannot effectively block Starlink traffic without physically confiscating or disabling the terminals&lt;/strong&gt; , because Starlink’s satellites communicate directly with user terminals over encrypted, proprietary protocols.&lt;/p&gt;

&lt;p&gt;To share this Starlink connection with friends and family inside Iran, users employ &lt;strong&gt;WireGuard VPN tunnels&lt;/strong&gt; :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;WireGuard Server Setup:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The user sets up a &lt;strong&gt;WireGuard server on a local Iranian ISP router&lt;/strong&gt; that acts as the gateway. This router has two interfaces:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Traffic Routing:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Incoming VPN connections from trusted friends or family connect to the WireGuard server on the Iranian ISP router (over the local Iranian network). The router then &lt;strong&gt;routes this decrypted traffic outbound via the Starlink interface&lt;/strong&gt; , effectively using Starlink as the internet exit point.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;NAT and IP Translation:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Because the Iranian ISP router is behind the ISP’s NAT and firewall, it &lt;strong&gt;performs NAT translation&lt;/strong&gt; on the incoming WireGuard traffic and routes it over the Starlink network, which has its own IP addressing scheme.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;WireGuard Configuration Sharing:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The WireGuard config files containing public keys, endpoint addresses, allowed IPs, and ports are shared securely with trusted users. This allows them to establish encrypted tunnels into the Starlink-connected router from their own devices within Iran.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why This Setup Is Resilient
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Starlink traffic is encrypted and satellite-based&lt;/strong&gt; , making it extremely difficult for IRGFW to inspect or block without physical interference.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;WireGuard uses UDP and a small handshake footprint&lt;/strong&gt; , making detection and blocking via DPI harder.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;NAT hides the underlying IP structure&lt;/strong&gt; , so Iranian firewalls see only standard encrypted traffic to the local router — they can’t easily distinguish if that traffic is then routed over Starlink.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This also &lt;strong&gt;allows multiple users to share a single Starlink terminal&lt;/strong&gt; , maximizing scarce and expensive hardware.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Survive Inside the NIN?
&lt;/h2&gt;

&lt;p&gt;Sometimes, when the National Information Network (NIN) is up but Iran’s connection to the global internet is completely cut off, you simply &lt;em&gt;can’t&lt;/em&gt; reach friends and family outside the country. Even communicating inside Iran becomes a challenge.&lt;/p&gt;

&lt;p&gt;So what are your options?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SMS?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
SMS in Iran is unencrypted. The government can—and does—intercept, read, and store messages at will. Plus, SMS is slow, costly, and unreliable for anything beyond short texts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phone calls?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Calls use GSM networks, which are fully controlled by the state. Voice calls can be intercepted, recorded, and monitored without any user consent. Privacy here is nonexistent.&lt;/p&gt;




&lt;h3&gt;
  
  
  Exploiting the Local Network: Self-Hosted Encrypted Services
&lt;/h3&gt;

&lt;p&gt;But here’s the catch: the NIN &lt;em&gt;does&lt;/em&gt; allow traffic inside Iran’s local network. This means you can run services completely inside Iran, avoiding the censored global internet but still enabling communication.&lt;/p&gt;

&lt;p&gt;One powerful option is setting up your own &lt;strong&gt;end-to-end encrypted messaging and calling service&lt;/strong&gt;. like a &lt;strong&gt;Matrix Synapse server&lt;/strong&gt; which can be fully hosted on a VPS inside an Iranian ISP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Matrix?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Matrix is an open-source protocol that supports secure, E2E-encrypted messaging, voice, and video calls. It’s decentralized, so anyone can run their own server. If your server is inside Iran, and your contacts connect locally, you avoid international censorship and heavy filtering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do you need?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A VPS hosted by an Iranian ISP, so the server is reachable inside the NIN.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A domain name pointing to that VPS, ideally registered with an Iranian registrar or configured to resolve within the NIN’s DNS system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Matrix Synapse server software installed and configured on the VPS.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How does it help?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Since the traffic never leaves the country’s internal network, it’s harder for the government to block without cutting off the NIN entirely. And because Matrix uses strong encryption, even if the government inspects packets, the content remains private.&lt;/p&gt;




&lt;p&gt;Surviving inside the NIN means adapting to its limitations and exploiting its architecture. Running your own encrypted local communication service allows Iranians to stay connected securely, even when the global internet is a no-go.&lt;/p&gt;

&lt;p&gt;It’s not perfect. there are risks, and setting up these services requires some technical knowledge. but it’s a crucial lifeline in a digital environment designed to isolate and surveil.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Digital Autonomism: Personal Autonomy through Technology</title>
      <dc:creator>Zola Gonano</dc:creator>
      <pubDate>Tue, 25 Mar 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/zolagonano/digital-autonomism-personal-autonomy-through-technology-5d64</link>
      <guid>https://dev.to/zolagonano/digital-autonomism-personal-autonomy-through-technology-5d64</guid>
      <description>&lt;p&gt;Freedom starts from within, once we break through the external controls and take control over our own lives and decisions.&lt;/p&gt;

&lt;p&gt;Although avoiding external controls might not sound feasible at the moment, just like avoiding cholera was not feasible a hundred years ago, the history of humanity is full of impossibilities that now just seem stupid to us.&lt;/p&gt;

&lt;p&gt;Just like how absurd it sounds that people died from cholera because they didn’t separate sewers from drinking water, our situation will sound absurd a hundred years later—that we didn’t have control over our own lives, our own finances, our own privacy, our own data, our own minds even.&lt;/p&gt;

&lt;p&gt;Bitcoin and cryptocurrencies broke a mental barrier for us, we suddenly realized things can work better without corruption looking over them, but that barrier can be pushed even further. We can control more than our finances, the Internet allows it, cryptography allows it, decentralized systems allow it.&lt;/p&gt;

&lt;p&gt;We can have free markets, free media, free finances, free information, free expressions, free privacy, free thoughts and beliefs, all through technology, by controlling the matters ourselves rather than authoritarian entities, by putting the trust into ourselves, and the systems we will build on top of cryptographic methods. We will put our trust in math rather than humans because math won’t get corrupt when given power—math is the power.&lt;/p&gt;

&lt;p&gt;We can have markets that people are free to trade on, markets that would be permissionless and trustless in every sense possible.&lt;/p&gt;

&lt;p&gt;We can have media that are run by us, the humans—not the algorithms, not the companies profiting off of them. We would profit off of our own content, we would get paid for hosting the content.&lt;/p&gt;

&lt;p&gt;We can have freedom of expression, information, and thoughts without censorship. We can have a place where no one’s voice can be suppressed.&lt;/p&gt;

&lt;p&gt;We can have free privacy—an Internet where privacy is built into it, not something that would take our conscious effort and time and a lot of sketchy things to get into it.&lt;/p&gt;

&lt;p&gt;For now, we have free finances, digital money that would be like gold in your safe, with the difference that crypto is safer. We have loans without banks, we have digital cash like Monero without federal reserves, we have borderless international money worth more than any currency on Earth.&lt;/p&gt;

&lt;p&gt;We can eventually replace the Internet we know today with something that is truly decentralized—from the network infrastructure to the protocols, to everything running on it. An Internet where it is a basic right to have control over yourself, your data, and your decisions.&lt;/p&gt;

&lt;p&gt;The world will move, the tools will be built, the knowledge will spread, and the barriers will fall. All that will remain will be for us to take back control, to build, to adopt, to be free.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Don't use Telegram, but if you have to, at least use Partisan Telegram</title>
      <dc:creator>Zola Gonano</dc:creator>
      <pubDate>Sat, 22 Mar 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/zolagonano/dont-use-telegram-but-if-you-have-to-at-least-use-partisan-telegram-2f50</link>
      <guid>https://dev.to/zolagonano/dont-use-telegram-but-if-you-have-to-at-least-use-partisan-telegram-2f50</guid>
      <description>&lt;p&gt;Telegram is really close to the worst when it comes to privacy, security, and anonymity, which highly matters if you are an activist, a protester, a journalist, or even a normal paranoid human being.&lt;/p&gt;

&lt;p&gt;Telegram is a platform that keeps on animating its emoji reactions in every update and milking users for money by giving them a “premium” subscription that essentially provides nothing “premium” in exchange. I have had Telegram since it came out on Android, and that’s about 10 years ago, but apparently, that wasn’t enough time for Telegram to add E2EE to their platform.&lt;/p&gt;

&lt;p&gt;Of course, it provides so-called “Secret Chats,” but what is the use of it if it is only available on mobile clients and its encryption is self-rolled, provoking one of the most important laws in cryptography, which states explicitly: “DO NOT ROLL YOUR OWN CRYPTO.”&lt;/p&gt;

&lt;p&gt;But besides its lack of proper E2EE, you might have to use it. I have to use it. Just because everyone I know uses it. And that’s a huge reason, believe it or not—you cannot go live on your own island forever. I have tried, and I have failed.&lt;/p&gt;

&lt;p&gt;With all this hate speech I did on Telegram, it is time to introduce a client called “Partisan Telegram” that not only fixes the issues with E2EE but also provides some other kind of security to it.&lt;/p&gt;

&lt;p&gt;Partisan Telegram brings operational security to Telegram, and by using it, you are still trusting Telegram with your data, which is not something you should do, honestly. Do not trust anyone with your data, especially if they can profit off your data or be pressured to hand out your data (be realistic—no one would shut down their platform in a country for you, let alone go to jail for you).&lt;/p&gt;

&lt;p&gt;Partisan Telegram adds some privacy and security features that can be life-saving for protesters and activists in some parts of the world where freedom is far from a luxury, in places where expressing it is considered a crime and punished severely.&lt;/p&gt;

&lt;p&gt;I like to keep this post short, so I will just explain how it works and what it offers.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;Partisan Telegram tries its best to obfuscate its existence so it wouldn’t put you into more trouble if you’re caught with it. So everything feels and looks like vanilla Telegram.&lt;/p&gt;

&lt;p&gt;It uses the lock feature of Telegram, where you put a 4-digit passcode to access your Telegram. By using Partisan Telegram, you can set multiple passcodes, which can then be programmed to do certain things when entered. This is its main functionality.&lt;/p&gt;

&lt;p&gt;For example, you can set the passcode “1234” to show only one account that you have nothing on. Or you can program it to send an alert message to your contacts when activated or to log off your accounts on all devices. And you will have a main passcode that will give you full access to your Telegram.&lt;/p&gt;

&lt;p&gt;But how would this be useful? Imagine you’re caught mid-protest and someone is investigating your phone. You can just give them the fake passcode that you have programmed to log out your accounts and remove any evidence from the device without them noticing while alerting your friends who might have been involved. The scenarios can be endless.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Offers
&lt;/h2&gt;

&lt;p&gt;Partisan Telegram’s main focus is your OPSEC, as it cannot add E2EE to Telegram without breaking its backward compatibility with other versions of Telegram. And it is relatively good at it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configurability
&lt;/h3&gt;

&lt;p&gt;Partisan Telegram has great configurability. You can set endless fake passcodes and configure each to do a certain thing for a specific situation.&lt;/p&gt;

&lt;p&gt;You can set a timer to go off or set a number of failed attempts to trigger a fake passcode, which will then trigger other events you have programmed it to do.&lt;/p&gt;

&lt;p&gt;It can also take pictures when entering a wrong or fake passcode, which you can later check to see if someone has attempted to unlock your Telegram or not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Triggering Killswitch by Receiving a Message
&lt;/h3&gt;

&lt;p&gt;One main feature it provides is that the fake passcode actions or killswitch can be triggered if you receive a specific message.&lt;/p&gt;

&lt;p&gt;This can be very useful if you lost your phone, or it got stolen or taken from you. You can remotely delete all your accounts from that phone and send an SOS message to your key contacts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Providing Additional Security Measures
&lt;/h3&gt;

&lt;p&gt;It also provides some other security measures, like brute-force protection for the passcodes, which Telegram itself doesn’t. With brute-force protection, the time between login attempts will increase every time.&lt;/p&gt;

&lt;p&gt;It can also be set to delete cache and drafts on exit, which the original Telegram doesn’t offer.&lt;/p&gt;

&lt;p&gt;It can prevent accidental clicks by double-checking with you when you accidentally touch the subscribe button or send a reaction to a message.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delete After Read
&lt;/h3&gt;

&lt;p&gt;It provides automatic delete-after-read for specific messages. You can send a message with a timer, and it will be deleted after the timer has passed. It is not like Telegram’s auto-delete, which deletes all messages from both sides after a period of time and is also visible when enabled or disabled, which can be more suspicious in some cases.&lt;/p&gt;

&lt;p&gt;Other than that, Partisan Telegram also provides auto-delete to delete all of your messages in a group chat automatically. This can come in handy if you need to delete your messages and leave. Although all group chat events, like deleting or editing messages, will be visible to the admins of the group chat for 72 hours.&lt;/p&gt;

&lt;h3&gt;
  
  
  File Protection
&lt;/h3&gt;

&lt;p&gt;Partisan Telegram can prevent information extraction from your phone by encrypting the data stored on the phone, including messages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Encrypted Group Chats
&lt;/h3&gt;

&lt;p&gt;Partisan Telegram recently introduced an experimental feature for having an encrypted chat with a group of people, which can be useful for organizing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Perks
&lt;/h2&gt;

&lt;p&gt;It can also bypass some of the annoying paywalls that Telegram has recently put up. For example, it will allow you to add more than three accounts without subscribing to Telegram Premium.&lt;/p&gt;

&lt;p&gt;It can provide additional badges to certain accounts, helping you detect suspicious accounts and scammers before you interact with them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Precautions
&lt;/h2&gt;

&lt;p&gt;You should research before using anything, especially when it doesn’t have audits and a well-known, reputed developer team. But Partisan Telegram is fully open-source and can be compared against the original Telegram codebase to see exactly what has been modified. You can compile your own version instead of trusting their binaries.&lt;/p&gt;

&lt;p&gt;You can find Partisan Telegram at &lt;a href="https://github.com/wrwrabbit/Partisan-Telegram-Android" rel="noopener noreferrer"&gt;https://github.com/wrwrabbit/Partisan-Telegram-Android&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is a good project for a good purpose, but if you can avoid Telegram and use something like Signal (which includes E2EE and is built with privacy in mind), do it.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How Monero Fulfilled Satoshi's Promise</title>
      <dc:creator>Zola Gonano</dc:creator>
      <pubDate>Fri, 07 Mar 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/zolagonano/how-monero-fulfilled-satoshis-promise-4g0p</link>
      <guid>https://dev.to/zolagonano/how-monero-fulfilled-satoshis-promise-4g0p</guid>
      <description>&lt;p&gt;Since Trump’s election and even before that, news kept coming out about how crypto was going to change the world, how the US was going to become the crypto capital of the world, and how they wanted to build strategic reserves of Bitcoin, Ethereum, Solana, Ripple, and others. At the same time, however, they de-listed Monero from major exchanges like Binance and even criminalized this cryptocurrency.&lt;/p&gt;

&lt;p&gt;But if we take a closer look at Monero, we can see that it was the true crypto—it was what Satoshi wanted to build.&lt;/p&gt;

&lt;h2&gt;
  
  
  But why is it hated so much by the authorities?
&lt;/h2&gt;

&lt;p&gt;Governments first thought Bitcoin and early cryptocurrencies were anonymous too, as we did. But soon it became obvious that this anonymity shatters as soon as you spend your money in the real world.&lt;/p&gt;

&lt;p&gt;Imagine you get some Bitcoin somehow, and you try to buy a car with it. Everyone can trace that transaction, get to the person who sold you the car, and identify you. With the help of AI, this can be further simplified and made easier to track.&lt;/p&gt;

&lt;p&gt;But Monero is different. Monero is untraceable, meaning they don’t know who spends money. They cannot collect taxes, and they hate it when they cannot figure out what everyone in society is doing.&lt;/p&gt;

&lt;p&gt;But despite all the efforts to criminalize Monero and delist it from exchanges, it has kept its value, continuing to increase over time because, like cash, it is actually used in transactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is cash and what was Satoshi’s promise?
&lt;/h2&gt;

&lt;p&gt;Satoshi’s whitepaper introduced Bitcoin as &lt;strong&gt;“Bitcoin: A Peer-to-Peer Electronic Cash System”&lt;/strong&gt; , but Bitcoin never became electronic cash. Not even Bitcoin Cash managed to be electronic cash.&lt;/p&gt;

&lt;p&gt;But what is cash? Cash is a form of money that allows for direct &lt;strong&gt;peer-to-peer transactions&lt;/strong&gt; without intermediaries. When you pay for something with cash, you don’t know where the cash originally came from. You just know you got it, and you will give it to someone else in exchange. No matter how many times these green papers circulate among people, they won’t be linked to anyone. When you hand over cash, the transaction is &lt;strong&gt;irreversible&lt;/strong&gt; and requires no third-party verification.&lt;/p&gt;

&lt;p&gt;Cash is &lt;strong&gt;Permissionless&lt;/strong&gt; , meaning no approval is needed to use it. Bitcoin and other public ledger cryptos are that to some extent.&lt;/p&gt;

&lt;p&gt;Cash is &lt;strong&gt;Censorship-Resistant&lt;/strong&gt; , meaning it cannot be blocked by banks or governments. Bitcoin is that as well to some extent, but there can be censorship when exchanging on centralized exchanges (CEX).&lt;/p&gt;

&lt;p&gt;Cash is &lt;strong&gt;Untraceable&lt;/strong&gt; , meaning transactions are not publicly recorded. Bitcoin and most cryptos are not that; they’re exactly the opposite.&lt;/p&gt;

&lt;p&gt;Cash has &lt;strong&gt;No Counterparty Risk&lt;/strong&gt; , meaning unlike digital bank balances, which depend on a central authority, cash does not. Neither Bitcoin nor most cryptos do.&lt;/p&gt;

&lt;p&gt;Self-Custodial: When you have cash, it’s in your wallet. You’re holding it. Bitcoin and other cryptos are like that as well.&lt;/p&gt;

&lt;p&gt;Based on all these points, Bitcoin and other public ledger cryptocurrencies have most of the characteristics of cash except one thing: &lt;strong&gt;Untraceability&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monero found the missing piece of the puzzle
&lt;/h2&gt;

&lt;p&gt;Monero introduced a new way that fixed the only missing piece of the “Electronic Cash” system that Satoshi wanted to build. Monero made a &lt;strong&gt;untraceable cryptocurrency&lt;/strong&gt; while keeping all of cash’s characteristics.&lt;/p&gt;

&lt;p&gt;To achieve this, Monero introduced a few new technologies to the crypto space:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Ring Signatures: Sender Anonymity&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Monero transactions do not directly reveal the sender. Instead, they use &lt;strong&gt;ring signatures&lt;/strong&gt; , which mix a real transaction input with decoys.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;ring signature&lt;/strong&gt; allows a signer to create a signature that proves ownership of a private key &lt;strong&gt;without revealing which key in the set was used&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In a ring signature, the user is given a &lt;strong&gt;set of public keys&lt;/strong&gt; $P1, P2, …, Pn$. The signer has the private key $sk$ corresponding to one of these.&lt;/p&gt;

&lt;p&gt;The signature is constructed so that an observer cannot determine which private key was used, only that one of the keys in the ring signed the message.&lt;/p&gt;

&lt;p&gt;Monero used to use &lt;strong&gt;Linkable Spontaneous Anonymous Group (LSAG) Signatures&lt;/strong&gt; for ring signatures, in which:&lt;/p&gt;

&lt;p&gt;Each &lt;strong&gt;participant’s public key&lt;/strong&gt; contributes to a &lt;strong&gt;key image&lt;/strong&gt; $I$, which is derived as: I=xH(P)I=xH(P) where $x$ is the private key, and $H(P)$ is a cryptographic hash of the public key.&lt;/p&gt;

&lt;p&gt;Then, the signer constructs a ring $(P1, P2, …, Pn)$ such that: $σ=(I,c1,r1,…,rn)σ=(I,c1, r1, …, rn)$&lt;/p&gt;

&lt;p&gt;where $c1$ is a random challenge, and $r_i$ are random values that make the signature verifiable without revealing the real signer.&lt;/p&gt;

&lt;p&gt;A verifier can check that the &lt;strong&gt;key image $I$&lt;/strong&gt; hasn’t been used before, preventing double spending.&lt;/p&gt;

&lt;p&gt;In 2020, Monero introduced &lt;strong&gt;&lt;a href="https://eprint.iacr.org/2019/654.pdf" rel="noopener noreferrer"&gt;Concise Linkable Spontaneous Anonymous Group Signatures (CLSAG)&lt;/a&gt;&lt;/strong&gt;, which does this much more efficiently.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With CLSAG, users see a 20% improvement in signature verification, and at least a 10% overall improvement for typical transactions. For example, a typical Monero transaction (2 inputs and 2 outputs) that usually weighs 2.5kB now takes only 1.9kB of blockchain space with CLSAG, a ~25% improvement.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;2. RingCT: Amount Privacy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Bitcoin transactions openly show input and output amounts. Monero hides these with &lt;strong&gt;RingCT&lt;/strong&gt; (Ring Confidential Transactions).&lt;/p&gt;

&lt;p&gt;RingCT is based on &lt;strong&gt;Pedersen Commitments&lt;/strong&gt; , which allow a sender to commit to a value without revealing it.&lt;/p&gt;

&lt;p&gt;A commitment to a value $v$ is:&lt;/p&gt;

&lt;p&gt;$C=vG+rHC=vG+rH$&lt;/p&gt;

&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$G, H$ are generator points on an elliptic curve.&lt;/li&gt;
&lt;li&gt;$r$ is a random blinding factor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Each input and output amount is committed&lt;/strong&gt; using Pedersen Commitments. &lt;strong&gt;Bulletproofs&lt;/strong&gt; are used to prove that the committed values &lt;strong&gt;sum to zero&lt;/strong&gt; (to ensure no coins are created out of thin air) without revealing them. &lt;strong&gt;Decoy amounts&lt;/strong&gt; are included to make it impossible to determine the real transaction amounts.&lt;/p&gt;

&lt;p&gt;Additionally, a Bulletproofs+ upgrade in 2022 &lt;strong&gt;reduced transaction size&lt;/strong&gt; by 5-7x compared to original Bulletproofs, making Monero even faster.&lt;/p&gt;

&lt;p&gt;Later on, cryptos like ZCash came along that use Zero-Knowledge Proofs (ZKPs) like ZK-Snarks to do this exact thing, and they are superior. But the problem with ZCash is that it is not the default; in Monero, all transactions are private, whereas in ZCash, some transactions can be private.&lt;/p&gt;

&lt;p&gt;In ZKPs, someone can prove that they possess information (in the case of crypto, the amount of money they want to transfer) without revealing anything about the information itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. Stealth Addresses: Receiver Privacy&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Bitcoin addresses are static, meaning you can see all transactions going to a given address. Monero uses &lt;strong&gt;one-time stealth addresses&lt;/strong&gt; that unlink the receiver from the transaction.&lt;/p&gt;

&lt;p&gt;Each Monero wallet has a &lt;strong&gt;public view key $v$ and a public spend key $s$&lt;/strong&gt;. When sending a transaction, the sender generates a &lt;strong&gt;one-time public key $P$&lt;/strong&gt; as follows:&lt;/p&gt;

&lt;p&gt;$P=H(rV)G+SP=H(rV)G+S$&lt;/p&gt;

&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$r$ is a random scalar chosen by the sender.&lt;/li&gt;
&lt;li&gt;$V$ is the recipient’s &lt;strong&gt;public view key&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;$S$ is the recipient’s &lt;strong&gt;public spend key&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;$G$ is the elliptic curve generator.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The recipient can compute the corresponding &lt;strong&gt;private key&lt;/strong&gt; to spend the funds:&lt;/p&gt;

&lt;p&gt;$P′=H(rv)G+sP′=H(rv)G+s$&lt;/p&gt;

&lt;p&gt;This ensures that &lt;strong&gt;only the recipient can identify and spend the transaction&lt;/strong&gt; , but an outsider cannot link it to a wallet.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;4. Dandelion++: Network-Level Privacy&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Even if transactions are private on-chain, Bitcoin and other cryptocurrencies &lt;strong&gt;leak metadata&lt;/strong&gt; through the P2P network when broadcasting transactions. Monero mitigates this with &lt;strong&gt;Dandelion++&lt;/strong&gt; , which obfuscates transaction propagation.&lt;/p&gt;

&lt;p&gt;In Dandelion++, a node first &lt;strong&gt;selects a “stem” path&lt;/strong&gt; (a small subset of nodes) to pass the transaction privately. Then, at a random step, the transaction &lt;strong&gt;“fluffs”&lt;/strong&gt; (broadcasts) to the entire network. This makes it difficult to determine the &lt;strong&gt;original source&lt;/strong&gt; of the transaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monero makes a more decentralized cryptocurrency than Bitcoin
&lt;/h2&gt;

&lt;p&gt;As mining becomes more competitive, larger entities with access to specialized hardware (ASICs) and cheap electricity can dominate the mining process. This leads to centralization, where a few large mining pools control the majority of the network’s hash power, potentially threatening decentralization and security.&lt;/p&gt;

&lt;p&gt;Monero introduces a new PoW algorithm called RandomX to be ASIC-resistant by making the PoW memory-hard, meaning it requires RAM to compute hashes, which cannot be specialized like SHA hashes. RAM is expensive to obtain.&lt;/p&gt;

&lt;p&gt;Also, unlike Bitcoin’s &lt;strong&gt;1 MB limit&lt;/strong&gt; , Monero has an &lt;strong&gt;adaptive block size&lt;/strong&gt; to prevent congestion. And unlike Ethereum and Solana, Monero has &lt;strong&gt;no pre-mined coins, no corporate backing, and no central leadership&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;With all that being said, we all love Bitcoin, Ethereum, and Solana. They’re great technologies, and even the worst of them is still superior to the best of banking systems.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A Very Technical Look at BitMessage: Learning From a Dead Project</title>
      <dc:creator>Zola Gonano</dc:creator>
      <pubDate>Sun, 25 Aug 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/zolagonano/a-very-technical-look-at-bitmessage-learning-from-a-dead-project-1j6o</link>
      <guid>https://dev.to/zolagonano/a-very-technical-look-at-bitmessage-learning-from-a-dead-project-1j6o</guid>
      <description>&lt;p&gt;There are a lot of cool projects that unfortunately have been abandoned or unmaintained for years, but that doesn’t mean they added no value. Studying what they’ve done, taking their unique ways of doing certain things and their problems can lead us to build something better. Bitmessage is one of those projects; it is an email-like service but fully peer-to-peer and decentralized, built upon the same principles. It used innovations of other projects like Bitcoin to build a new thing.&lt;/p&gt;

&lt;p&gt;But sadly, Bitmessage didn’t seem to make it. Though it was a cool project, it never got independently audited and its latest release was in 2018. There aren’t many peers left on the network and its client has some known and critical vulnerabilities like remote code execution, but none of this means it served no good. There have been freedom-loving and privacy-caring people behind it, dedicating their time, money, and energy to build something valuable.&lt;/p&gt;

&lt;p&gt;In this post, I wanted to take a technical look at this network, on how it worked in depth (like the &lt;a href="//./a-very-technical-look-at-zeronet"&gt;post I did on Zeronet&lt;/a&gt;) in a very simple and easy-to-understand way.&lt;/p&gt;

&lt;h2&gt;
  
  
  What did Bitmessage do
&lt;/h2&gt;

&lt;p&gt;Bitmessage was supposed to be like a truly &lt;a href="https://en.wikipedia.org/wiki/Peer-to-peer" rel="noopener noreferrer"&gt;P2P&lt;/a&gt; email system. You would send a message on the network, it would have been encrypted for the receiver and spread across the peers until the receiver got it. Everyone would get the message, but only the receiver would open it. That causes some issues, though. We’re relying too much on the encryption scheme and its implementation. If a flaw in the scheme was found, the whole network would become insecure until a new version was released, and then it would break compatibility with older versions. Here, a bad actor could log all messages flowing on the network and wait for the day that a vulnerability in encryption was found, then they would open all messages. Networks like &lt;a href="https://en.wikipedia.org/wiki/Jami_(software)" rel="noopener noreferrer"&gt;Jami&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Tox_(protocol)" rel="noopener noreferrer"&gt;Tox&lt;/a&gt; do this differently; they find the receiver and then directly communicate with them, which fixes that problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mailing Lists
&lt;/h3&gt;

&lt;p&gt;One of the interesting features of Bitmessage was its mailing list feature, which is a broadcast address that, when someone sends a message to it, a copy of the message gets sent to everyone in the subscription list of this address.&lt;/p&gt;

&lt;p&gt;But the downside is that everyone can send a message to a mailing list in Bitmessage without even being a member of it, and that is the recipe for spam.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subscriptions
&lt;/h3&gt;

&lt;p&gt;Subscriptions in Bitmessage allow users to receive encrypted broadcasts from a subscribed address. The way that this encryption happens is not very secure. A broadcast message is encrypted with a key, which can be derived from the sender’s address. Once a broadcast is decrypted, the sender’s address is known, and after that, every broadcast ever sent and every broadcast that will be sent in the future can be decrypted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Private Messages
&lt;/h3&gt;

&lt;p&gt;Private messages in Bitmessage are those messages that have only one recipient. All private messages are encrypted using the receiver’s public key (and the receiver’s public key is the receiver’s address), and when the receiver successfully decrypts the sender’s message, they send an ACK message telling the sender that the message has been successfully received and decrypted. If the ACK message isn’t received within 2.5 days, the message expires and the sender will have to do the Proof-of-Work again.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Explain the Proof of Work and how it works in Bitmessage here&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  BitMessage’s Encryption
&lt;/h2&gt;

&lt;p&gt;Bitmessage used an &lt;a href="https://en.wikipedia.org/wiki/Integrated_Encryption_Scheme" rel="noopener noreferrer"&gt;ECIES (Elliptic Curve Integrated Encryption Scheme)&lt;/a&gt; to encrypt the payloads of the messages and broadcast objects.&lt;/p&gt;

&lt;p&gt;This scheme uses &lt;a href="https://en.wikipedia.org/wiki/Elliptic-curve_Diffie%E2%80%93Hellman" rel="noopener noreferrer"&gt;ECDH (Elliptic Curve Diffie-Hellman)&lt;/a&gt; to generate a shared secret, which is then used to encrypt the data using AES-256 (Advanced Encryption Standard) in CBC mode.&lt;/p&gt;

&lt;p&gt;This scheme, by today’s standards, isn’t that secure. The CBC mode adds a lot of complications and insecurities because it doesn’t have authentication of messages, and self-implementation of them leaves room for many errors and mistakes. A better scheme by today’s standards would use X25519 to exchange the keys and generate the shared secrets. &lt;a href="https://en.wikipedia.org/wiki/Curve25519" rel="noopener noreferrer"&gt;X25519&lt;/a&gt; is based on Curve25519, which is faster and more secure than ECDH.&lt;/p&gt;

&lt;p&gt;Also, there are other new methods for exchanging keys besides elliptic curves and &lt;a href="https://en.wikipedia.org/wiki/RSA_(cryptosystem)" rel="noopener noreferrer"&gt;RSA&lt;/a&gt;, like &lt;a href="https://en.wikipedia.org/wiki/Key_encapsulation_mechanism" rel="noopener noreferrer"&gt;Key Encapsulation Mechanisms (KEMs)&lt;/a&gt;, which are supposed to reduce the complexity of the key exchange methods.&lt;/p&gt;

&lt;p&gt;The encryption process in Bitmessage is as follows (taken from Bitmessage’s wiki):&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;The destination public key is called K.&lt;/li&gt;
&lt;li&gt;Generate 16 random bytes using a secure random number generator. Call them IV.&lt;/li&gt;
&lt;li&gt;Generate a new random EC key pair with the private key called r and the public key called R.&lt;/li&gt;
&lt;li&gt;Do an EC point multiply with public key K and private key r. This gives you public key P.&lt;/li&gt;
&lt;li&gt;Use the X component of public key P and calculate the SHA512 hash H.&lt;/li&gt;
&lt;li&gt;The first 32 bytes of H are called key_e and the last 32 bytes are called key_m.&lt;/li&gt;
&lt;li&gt;Pad the input text to a multiple of 16 bytes, in accordance with PKCS7.&lt;/li&gt;
&lt;li&gt;Encrypt the data with AES-256-CBC, using IV as the initialization vector, key_e as the encryption key, and the padded input text as the payload. Call the output ciphertext.&lt;/li&gt;
&lt;li&gt;Calculate a 32-byte MAC with HMAC-SHA256, using key_m as salt and IV + R + ciphertext as data. Call the output MAC.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The resulting data is: IV + R + ciphertext + MAC&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This could be improved in many ways, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using a safe KDF like HKDF to derive the keys instead of using a hash like SHA512.&lt;/li&gt;
&lt;li&gt;Using an &lt;a href="https://en.wikipedia.org/wiki/Authenticated_encryption#Authenticated_encryption_with_associated_data_(AEAD)" rel="noopener noreferrer"&gt;AEAD&lt;/a&gt; to ensure the integrity of the encrypted message instead of calculating the MAC separately and increasing the risk of implementation errors.&lt;/li&gt;
&lt;li&gt;Using a KEM to exchange the keys would simplify the exchange process while increasing security by reducing the risk of key interception or leakage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Plausible Deniability
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Plausible_deniability" rel="noopener noreferrer"&gt;Plausible deniability&lt;/a&gt; is when someone can say they didn’t know about something bad that happened, and there is no clear proof to show they did.&lt;/p&gt;

&lt;p&gt;Plausible deniability is a big part of Bitmessage. In Bitmessage, the messages are not only encrypted but also signed with the sender’s address to prevent strangers from claiming to be a specific person. However, this also causes problems: if a message is decrypted and the signature is verified, the content of the message can be used against the sender. But these actions can be taken for plausible deniability in Bitmessage:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deleting: Address blocks can be deleted from keys.dat so they are no longer used to send or receive messages.&lt;/li&gt;
&lt;li&gt;Publication: Another thing that can be done is to publicly share the address block in a crowded mailing list. This way, users can pretend that they never used that address at all, and there will be no proof that they actually did, or someone else who had access to that address block. But this comes with some consequences: 

&lt;ol&gt;
&lt;li&gt;Everyone can claim that they’re the true owner of the address, impersonating the address.&lt;/li&gt;
&lt;li&gt;Every message that was sent using or to the public address can be decrypted by everyone. While the messages delete after 2.5 days, there might always be a backup copy of the messages.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  BitMessage’s Proof of Work
&lt;/h2&gt;

&lt;p&gt;To prevent or at least reduce the spam on the network, Bitmessage used proof of work to address this problem.&lt;/p&gt;

&lt;p&gt;Proof of work is a costly task for a computer to perform, for example, a calculation that requires time and effort (for a computer) to do, and it is verifiable by other people that this calculation or work is done correctly and there is no cheating involved.&lt;/p&gt;

&lt;p&gt;Bitmessage uses a double round of &lt;a href="http://en.wikipedia.org/wiki/SHA-2" rel="noopener noreferrer"&gt;SHA-512&lt;/a&gt; for its proof of work, meaning it hashes a message twice with SHA-512. It is not very costly by modern standards, and in my opinion, it doesn’t prevent spam that much. It could have been improved by using a hash algorithm that requires memory to perform the hashing, usually a &lt;a href="https://en.wikipedia.org/wiki/Key_derivation_function" rel="noopener noreferrer"&gt;key derivation function (KDF)&lt;/a&gt; like &lt;a href="https://en.wikipedia.org/wiki/Argon2" rel="noopener noreferrer"&gt;Argon2id&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/Scrypt" rel="noopener noreferrer"&gt;scrypt&lt;/a&gt;. These algorithms not only require CPU/GPU power but also take up RAM, and RAM is much more expensive than CPU power.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hello
9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043 (first round of SHA-512)
0592a10584ffabf96539f3d780d776828c67da1ab5b169e9e8aed838aaecc9ed36d49ff1423c55f019e050c66c6324f53588be88894fef4dcffdb74b98e2b200 (second round of SHA-512)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  BitMessage’s Addresses
&lt;/h2&gt;

&lt;p&gt;In Bitmessage, all addresses are Base58 encoded hashes of the public key. The hash algorithm for address generation is different from its PoW algorithm; for addresses, Bitmessage uses &lt;a href="http://en.wikipedia.org/wiki/RIPEMD" rel="noopener noreferrer"&gt;RIPEMD-160&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;For example, a Bitmessage address looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BM-BcbRqcFFSQUUmXFKsPJgVQPSiFA3Xash

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All Bitmessage addresses start with the characters “BM-“ to indicate they are part of the protocol. This is a good idea because when a network applies such a separator in the addresses, it becomes easier to identify newer versions of addresses. For example, if in a new version of Bitmessage they wanted to use a different hash or public key algorithm, they could have changed the address prefix to &lt;code&gt;BMV2-&lt;/code&gt;, indicating it is version 2 of the address. This would let users know if they need a newer client and would prevent sending messages from unsupported clients to newer versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  BitMessage’s Protocol
&lt;/h2&gt;

&lt;h3&gt;
  
  
  BitMessage’s Message Encoding
&lt;/h3&gt;

&lt;p&gt;BitMessage’s protocol uses a custom encoding system for its messages that are sent over the network. It consists of several parts:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. &lt;strong&gt;Message Header&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Magic (4 bytes)&lt;/strong&gt;: Identifies the network. It helps the protocol to recognize the beginning of a message. Known magic values include &lt;code&gt;0xE9BEB4D9&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Command (12 bytes)&lt;/strong&gt;: ASCII string identifying the type of message. It’s padded with NULL bytes (&lt;code&gt;\x00&lt;/code&gt;) to ensure a length of 12 bytes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Length (4 bytes)&lt;/strong&gt;: Specifies the length of the payload. It’s an unsigned 32-bit integer, meaning it can describe a payload up to &lt;code&gt;4,294,967,295&lt;/code&gt; bytes, though restrictions limit it to 1,600,003 bytes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checksum (4 bytes)&lt;/strong&gt;: First 4 bytes of the SHA-512 hash of the payload to ensure data integrity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Message Payload (variable)&lt;/strong&gt;: The actual data being transmitted, which could vary based on the message type.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. &lt;strong&gt;Variable Length Integer (VarInt)&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1 byte&lt;/strong&gt; : For values less than &lt;code&gt;0xFD&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3 bytes&lt;/strong&gt; : Starts with &lt;code&gt;0xFD&lt;/code&gt; followed by a &lt;code&gt;uint16_t&lt;/code&gt; value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5 bytes&lt;/strong&gt; : Starts with &lt;code&gt;0xFE&lt;/code&gt; followed by a &lt;code&gt;uint32_t&lt;/code&gt; value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;9 bytes&lt;/strong&gt; : Starts with &lt;code&gt;0xFF&lt;/code&gt; followed by a &lt;code&gt;uint64_t&lt;/code&gt; value.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. &lt;strong&gt;Variable Length String&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Prefixed with a VarInt indicating the length of the string, followed by the string itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. &lt;strong&gt;Variable Length List of Integers&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Starts with a VarInt specifying the number of integers, followed by the integers themselves, each encoded as VarInts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  5. &lt;strong&gt;Network Address Structure&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time (8 bytes)&lt;/strong&gt;: Timestamp.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stream (4 bytes)&lt;/strong&gt;: Stream number for the node.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Services (8 bytes)&lt;/strong&gt;: Node services bitfield.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IPv6/4 (16 bytes)&lt;/strong&gt;: IPv6 address (or IPv4 mapped into IPv6).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port (2 bytes)&lt;/strong&gt;: Network port.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  6. &lt;strong&gt;Inventory Vectors&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Used to announce or request objects within the network. Each entry includes a 32-byte hash representing the object.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  7. &lt;strong&gt;Encrypted Payload&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IV (16 bytes)&lt;/strong&gt;: Initialization vector for AES-256-CBC encryption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Curve Type (2 bytes)&lt;/strong&gt;: Identifies the elliptic curve used (e.g., 0x02CA for secp256k1).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public Key Components (X and Y)&lt;/strong&gt;: Lengths and values of the X and Y coordinates of the elliptic curve public key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ciphertext&lt;/strong&gt; : The actual encrypted data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MAC (32 bytes)&lt;/strong&gt;: Message Authentication Code using HMAC-SHA256.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  8. &lt;strong&gt;Unencrypted Message Data&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Various fields including version, address version, stream number, public keys, and a bitfield indicating node behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nonce Trials per Byte and Extra Bytes&lt;/strong&gt; : Difficulty settings for Proof of Work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RIPE Hash (20 bytes)&lt;/strong&gt;: Represents the receiver’s public key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Message Encoding&lt;/strong&gt; : Specifies how the message is formatted (e.g., UTF-8).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signature&lt;/strong&gt; : ECDSA signature covering specific parts of the message.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  9. &lt;strong&gt;Message Types&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Version Message&lt;/strong&gt; : Announces a node’s protocol version and capabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verack Message&lt;/strong&gt; : Acknowledges the reception of a version message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Addr Message&lt;/strong&gt; : Provides information about known nodes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inv and Getdata Messages&lt;/strong&gt; : Handle the exchange of object information and requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Object Message&lt;/strong&gt; : Carries content that propagates across the network, such as public keys, messages, or broadcasts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  10. &lt;strong&gt;Pubkey and Broadcast Structures&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pubkey&lt;/strong&gt; : Contains public keys for signing and encryption, behavior bitfields, and optionally encrypted data (in version 4 and above).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broadcast&lt;/strong&gt; : Used for sending messages to subscribers, with version 4 and 5 utilizing tags and encryption for additional security.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There could be some improvement to this encoding format. For example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The protocol uses a variable-length encoding that allows smaller numbers to be represented with fewer bytes. However, the current scheme (e.g., using 0xFD, 0xFE, and 0xFF prefixes) might be wasteful for certain number ranges. A more efficient variable-length encoding scheme, such as &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/LEB128" rel="noopener noreferrer"&gt;LEB128 (Little Endian Base 128)&lt;/a&gt;&lt;/strong&gt;, which is widely used in formats like WebAssembly, encodes integers using a variable number of bytes, with no need for extra prefix bytes, making it more space-efficient for both small and large numbers.&lt;/li&gt;
&lt;li&gt;The protocol stores full 128-bit IPv6 addresses, which can be wasteful, especially for addresses with long sequences of zeros. It could use &lt;strong&gt;address compression&lt;/strong&gt; techniques, similar to how IPv6 addresses are represented textually (e.g., using &lt;code&gt;::&lt;/code&gt; to replace sequences of zeros). Alternatively, apply &lt;strong&gt;prefix compression&lt;/strong&gt; if many addresses share a common prefix.&lt;/li&gt;
&lt;li&gt;Commands are encoded as fixed-length, 12-byte ASCII strings, which is simple but inefficient and inflexible. Using a &lt;strong&gt;variable-length encoding&lt;/strong&gt; for commands, storing the command length as a prefix, followed by the command string, would reduce the size of the message when the command is shorter than 12 bytes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  BitMessage’s Message Types
&lt;/h3&gt;

&lt;p&gt;Message types refer to the different kinds of messages that nodes (computers) can send and receive in a peer-to-peer network. These messages are used for various purposes, including establishing connections, sharing information about nodes, requesting and receiving data, and managing the objects (like messages or keys) in the network.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Version&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What It Is&lt;/strong&gt; : When two nodes first connect, they need to agree on the version of the protocol they are using.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Contains&lt;/strong&gt; : 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Version&lt;/strong&gt; : Tells which version of the protocol the node is using (should be version 3).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Services&lt;/strong&gt; : Lists the features the node supports (like network connectivity or security options).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timestamp&lt;/strong&gt; : The current time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Addresses&lt;/strong&gt; : The network addresses of the node sending and receiving the message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nonce&lt;/strong&gt; : A random number used to check if the connection is with itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Agent&lt;/strong&gt; : Information about the software being used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stream Numbers&lt;/strong&gt; : The streams of messages the node is interested in.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Verack&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What It Is&lt;/strong&gt; : A simple acknowledgment message sent in reply to the &lt;code&gt;version&lt;/code&gt; message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Contains&lt;/strong&gt; : Just a header with the command “verack” (short for “version acknowledgment”).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt; : Signals that the connection setup is complete and both nodes are ready to communicate. After this, they can start a secure connection if both support &lt;a href="https://dev.toSSL/TLS"&gt;SSL/TLS&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Addr&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What It Is&lt;/strong&gt; : A message that provides information about other nodes in the network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Contains&lt;/strong&gt; : 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Count&lt;/strong&gt; : The number of addresses being shared.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Address List&lt;/strong&gt; : The addresses of other nodes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Inv&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What It Is&lt;/strong&gt; : A message used to announce that a node knows about certain objects (like messages or public keys).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Contains&lt;/strong&gt; : 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Count&lt;/strong&gt; : The number of objects being announced.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inventory&lt;/strong&gt; : Details of each object.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;Getdata&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What It Is&lt;/strong&gt; : A request to get the actual data of specific objects after they have been announced by &lt;code&gt;inv&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Contains&lt;/strong&gt; : 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Count&lt;/strong&gt; : The number of objects being requested.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inventory&lt;/strong&gt; : Details of each object requested.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. &lt;strong&gt;Object&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What It Is&lt;/strong&gt; : This is the actual data message that gets shared across the network. It’s the primary way to send information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Contains&lt;/strong&gt; : 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nonce&lt;/strong&gt; : A random number used to ensure the message has been properly processed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expires Time&lt;/strong&gt; : When the object will no longer be valid.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Object Type&lt;/strong&gt; : Tells what kind of message it is (like a request for a public key, a public key, a message, or a broadcast).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version&lt;/strong&gt; : The version of the object format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stream Number&lt;/strong&gt; : The stream the object is related to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Object Payload&lt;/strong&gt; : The actual content of the message, which varies depending on the type of object.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The message types could have been improved by implementing these methods:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A more detailed protocol negotiation mechanism to handle different versions and backward compatibility better.&lt;/li&gt;
&lt;li&gt;Including additional metadata about the node’s capabilities or preferences (e.g., supported encryption algorithms or message types) to optimize the interactions.&lt;/li&gt;
&lt;li&gt;Ensuring SSL/TLS handshakes are mandatory and verified before proceeding with further messages.&lt;/li&gt;
&lt;li&gt;Implementing mechanisms to dynamically update and validate node addresses to ensure that outdated or invalid addresses are removed from the list.&lt;/li&gt;
&lt;li&gt;Using methods to anonymize node addresses or using temporary addresses that change periodically to increase privacy.&lt;/li&gt;
&lt;li&gt;Supporting batch requests to retrieve multiple objects in a single &lt;code&gt;getdata&lt;/code&gt; message, reducing overhead and improving efficiency.&lt;/li&gt;
&lt;li&gt;Implementing priority mechanisms to request the most critical data first and supporting caching to avoid redundant requests for the same data.&lt;/li&gt;
&lt;li&gt;Using compression algorithms to reduce the size of &lt;code&gt;object&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;messages, especially for large payloads.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Implementing version control within &lt;code&gt;object&lt;/code&gt; messages to handle changes in the object structure or content format over time.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  BitMessage’s Object Types
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Object types&lt;/strong&gt; are specific data structures used to perform various operations within the network. Each object type serves a particular purpose, such as requesting a public key, sending messages, broadcasting information to multiple recipients, or transmitting a public key. These objects are the building blocks of the protocol, enabling nodes (participants in the network) to communicate, authenticate, and exchange information securely and efficiently.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. &lt;strong&gt;&lt;code&gt;getpubkey&lt;/code&gt; Object&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt; : This object is used by a node to request a public key from another node when it has the hash of the public key (from an address) but not the key itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fields&lt;/strong&gt; : 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ripe&lt;/code&gt; (20 bytes)&lt;/strong&gt;: This is the RIPEMD-160 hash of the public key. It is included only when the address version is 3 or below.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;tag&lt;/code&gt; (32 bytes)&lt;/strong&gt;: This is derived from the address version, stream number, and ripe hash. It’s included when the address version is 4 or above, ensuring that the requesting node can correctly identify the public key.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. &lt;strong&gt;&lt;code&gt;pubkey&lt;/code&gt; Object&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt; : The &lt;code&gt;pubkey&lt;/code&gt; object contains the public key information used for cryptographic operations like signing and encryption. The format of this object evolves across different protocol versions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version 2&lt;/strong&gt; : 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;behavior bitfield&lt;/code&gt; (4 bytes)&lt;/strong&gt;: Indicates the behaviors and features supported by the node. This is a set of flags that tell other nodes what to expect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;public signing key&lt;/code&gt; (64 bytes)&lt;/strong&gt;: The ECC public key used for signing messages, provided in an uncompressed format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;public encryption key&lt;/code&gt; (64 bytes)&lt;/strong&gt;: The ECC public key used for encrypting messages, also in an uncompressed format.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version 3&lt;/strong&gt; : Builds upon version 2 by adding: 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;nonce_trials_per_byte&lt;/code&gt; (variable length)&lt;/strong&gt;: Defines how hard the Proof of Work (PoW) needs to be for messages accepted by the node. A higher value increases the difficulty.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;extra_bytes&lt;/code&gt; (variable length)&lt;/strong&gt;: Adds to the message length to increase the difficulty of sending small messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;sig_length&lt;/code&gt; (variable length)&lt;/strong&gt;: Length of the signature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;signature&lt;/code&gt; (variable length)&lt;/strong&gt;: The ECDSA signature that covers the object header and data.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version 4&lt;/strong&gt; : In version 4, most of the pubkey data is encrypted: 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;tag&lt;/code&gt; (32 bytes)&lt;/strong&gt;: Helps in identifying the correct pubkey for decryption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;encrypted&lt;/code&gt; (variable length)&lt;/strong&gt;: Contains encrypted data, which includes fields like the behavior bitfield, public signing key, and public encryption key. This encryption is intended to prevent spam or abuse of the public key.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. &lt;strong&gt;&lt;code&gt;msg&lt;/code&gt; Object&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt; : The &lt;code&gt;msg&lt;/code&gt; object is used for sending person-to-person messages within the network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fields&lt;/strong&gt; : 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;encrypted&lt;/code&gt; (variable length)&lt;/strong&gt;: The message data is encrypted to ensure confidentiality. The details of this encryption are handled similarly to how pubkeys are encrypted, ensuring only the intended recipient can read the message.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. &lt;strong&gt;&lt;code&gt;broadcast&lt;/code&gt; Object&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt; : The &lt;code&gt;broadcast&lt;/code&gt; object allows a user to send messages to all subscribers of an address. This is typically used for announcements or public messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fields&lt;/strong&gt; : 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;tag&lt;/code&gt; (32 bytes)&lt;/strong&gt;: Similar to the &lt;code&gt;pubkey&lt;/code&gt; object, it helps identify the broadcast messages that are relevant to a particular address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;encrypted&lt;/code&gt; (variable length)&lt;/strong&gt;: Contains the encrypted broadcast data. Like in the &lt;code&gt;msg&lt;/code&gt; object, the encryption ensures that only the intended recipients can decrypt and view the broadcast.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Encryption Mechanism&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For Version 4+ Objects&lt;/strong&gt; : 

&lt;ul&gt;
&lt;li&gt;The encryption is done using a key derived from the double SHA-512 hash of the address version, stream number, and the ripe hash of the address. This ensures that only nodes that know the address can decrypt the pubkey or broadcast message, adding a layer of privacy and security.&lt;/li&gt;
&lt;li&gt;The process involves creating a public and private key pair from the first 32 bytes of the hash, which is then used for encrypting the pubkey data.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Understanding Specific Fields&lt;/strong&gt; :
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;behavior bitfield&lt;/code&gt;&lt;/strong&gt; : This is a bitmask that specifies what behaviors the node supports. Each bit in the 32-bit field might represent a different capability or feature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;public signing key&lt;/code&gt; and &lt;code&gt;public encryption key&lt;/code&gt;&lt;/strong&gt; : These are standard ECC (Elliptic Curve Cryptography) keys used in the cryptographic processes of signing messages to verify authenticity and encrypting messages to ensure privacy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;nonce_trials_per_byte&lt;/code&gt; and &lt;code&gt;extra_bytes&lt;/code&gt;&lt;/strong&gt; : These are parameters related to the Proof of Work mechanism, which is used to prevent spam by making it computationally expensive to send messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;tag&lt;/code&gt;&lt;/strong&gt; : This is a unique identifier derived from the address data, allowing nodes to quickly determine whether a particular piece of data is relevant to them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Object types also have some issues that could have been addressed and improved:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Instead of requesting public keys using a hash, using a more direct method to find and retrieve keys quickly.&lt;/li&gt;
&lt;li&gt;Allowing the &lt;code&gt;behavior bitfield&lt;/code&gt; and other metadata to be dynamically updated rather than fixed, so nodes can negotiate features dynamically.&lt;/li&gt;
&lt;li&gt;Implementing data compression to reduce the size of encrypted messages and improve transmission efficiency.&lt;/li&gt;
&lt;li&gt;Improving the tagging mechanism to handle larger and more complex broadcasts efficiently. For instance, using hierarchical tagging to manage different types of broadcasts.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  BitMessage’s Files
&lt;/h2&gt;

&lt;p&gt;BitMessage uses several &lt;code&gt;.dat&lt;/code&gt; files to store crucial data for the client to work:&lt;/p&gt;

&lt;h3&gt;
  
  
  Knownnodes.dat
&lt;/h3&gt;

&lt;p&gt;BitMessage uses a file named &lt;code&gt;knownnodes.dat&lt;/code&gt; to store the IP addresses and ports of the nodes that run BitMessage. These nodes help bootstrap the connection to the network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keys.dat
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;keys.dat&lt;/code&gt;&lt;/strong&gt; is a file used by BitMessage to store important settings and address information.&lt;/p&gt;

&lt;p&gt;This file is the most important file in BitMessage as it contains all your keys; if you lose this file, you lose your BitMessage identity (which is your private/public keys).&lt;/p&gt;

&lt;h4&gt;
  
  
  Configuration Sections
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[DEFAULT] and [bitmessagesettings]&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Address Sections&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  messages.dat
&lt;/h4&gt;

&lt;p&gt;All messages in BitMessage are stored in a &lt;a href="https://en.wikipedia.org/wiki/SQLite" rel="noopener noreferrer"&gt;SQLite&lt;/a&gt; database named &lt;code&gt;messages.dat&lt;/code&gt;, along with &lt;code&gt;keys.dat&lt;/code&gt; and &lt;code&gt;knownnodes.dat&lt;/code&gt; in the same directory.&lt;/p&gt;

&lt;p&gt;This database uses different tables for different messages, for example, messages that were retrieved and decrypted, sent messages, and also some app configurations:&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;code&gt;inbox&lt;/code&gt; Table:
&lt;/h5&gt;

&lt;p&gt;This table contains messages that were retrieved and were successfully decrypted. It also stores trash messages as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE inbox (
    msgid blob,
    toaddress text,
    fromaddress text,
    subject text,
    received text,
    message text,
    folder text,
    encodingtype int,
    read bool,
UNIQUE(msgid) ON CONFLICT REPLACE)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  &lt;code&gt;sent&lt;/code&gt; Table:
&lt;/h5&gt;

&lt;p&gt;Similar to the &lt;code&gt;inbox&lt;/code&gt; table, it contains only the sent messages, including the trashed sent messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE sent (
    msgid blob,
    toaddress text,
    toripe blob,
    fromaddress text,
    subject text,
    message text,
    ackdata blob,
    lastactiontime integer,
    status text,
    pubkeyretrynumber integer,
    msgretrynumber integer,
    folder text,
    encodingtype int
)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  &lt;code&gt;subscription&lt;/code&gt; Table:
&lt;/h5&gt;

&lt;p&gt;This table contains all the addresses that the user has subscribed to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE subscriptions (
    label text,
    address text,
    enabled bool
)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  &lt;code&gt;addressbook&lt;/code&gt; Table:
&lt;/h5&gt;

&lt;p&gt;This table contains all of the addresses that a user has communicated with in the past, like a phonebook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE addressbook (
    label text,
    address text
)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  &lt;code&gt;blacklist&lt;/code&gt; Table:
&lt;/h5&gt;

&lt;p&gt;As the name suggests, any address that a user blocks will be stored in this table to prevent future communications with these addresses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE blacklist (
    label text,
    address text,
    enabled bool
)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  &lt;code&gt;whitelist&lt;/code&gt; Table:
&lt;/h5&gt;

&lt;p&gt;This table, similar to the &lt;code&gt;blacklist&lt;/code&gt; table, contains addresses that are whitelisted. If the field &lt;code&gt;enabled&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt;, the user can only communicate with the addresses in this table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE whitelist (
    label text,
    address text,
    enabled bool
)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  &lt;code&gt;knownnodes&lt;/code&gt; Table:
&lt;/h5&gt;

&lt;p&gt;This table was created for future use and to replace the &lt;code&gt;knownnodes.dat&lt;/code&gt; file, which never happened as the project’s development halted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE knownnodes (
    timelastseen int,
    stream int,
    services blob,
    host blob,
    port blob,
UNIQUE(host, stream, port) ON CONFLICT REPLACE)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  &lt;code&gt;settings&lt;/code&gt; Table:
&lt;/h5&gt;

&lt;p&gt;This table, like the &lt;code&gt;knownnodes&lt;/code&gt; table, is not implemented and was set to replace the &lt;code&gt;keys.dat&lt;/code&gt; file configurations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE settings (
    key blob,
    value blob,
UNIQUE(key) ON CONFLICT REPLACE)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  &lt;code&gt;pubkeys&lt;/code&gt; Table:
&lt;/h5&gt;

&lt;p&gt;This table contains public keys created by the user and the time they were last transmitted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE pubkeys (
    hash blob,
    transmitdata blob,
    time int,
    usedpersonally text,
UNIQUE(hash) ON CONFLICT REPLACE)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;This approach has some obvious flaws that could have been addressed relatively easily:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There is no need for storing sent and retrieved messages in separate tables; it could have been done by adding a field to a table called &lt;code&gt;messages&lt;/code&gt; to determine whether it is sent or received. This would reduce a table in the database, making it smaller, especially as it is a file-based database.&lt;/li&gt;
&lt;li&gt;Whitelists and blacklists could have been implemented into the &lt;code&gt;addressbook&lt;/code&gt; table by adding a field to determine whether the address is whitelisted or blacklisted.&lt;/li&gt;
&lt;li&gt;Using a file-based database for storing messages is not a good idea as they will get huge and will slow down as they grow more and more with usage.&lt;/li&gt;
&lt;li&gt;Configurations such as &lt;code&gt;knownnodes&lt;/code&gt; aren’t related to the messages and should have a separate database, perhaps named &lt;code&gt;configs.dat&lt;/code&gt;, to store all configurations and settings.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  BitMessage’s Stream
&lt;/h2&gt;

&lt;p&gt;In BitMessage, a stream is like a separate channel or group where nodes can communicate. Streams in BitMessage help organize and manage communication between nodes. They allow for more efficient data handling and keep the network organized.&lt;/p&gt;

&lt;p&gt;This feature was never fully implemented to have multiple streams, and all addresses use stream 1 in BitMessage. When you create a new address, you usually set it up with a stream number, which is stream 1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stream 1&lt;/strong&gt; is the main or “master” stream. It’s the primary stream and, at the moment, the only one.&lt;/p&gt;

&lt;p&gt;If you have addresses in different streams, your BitMessage client will connect to other clients in those specific streams only. This helps manage and reduce the amount of data your client handles. Your client will also connect to streams of addresses you are subscribed to.&lt;/p&gt;

&lt;p&gt;Clients occasionally connect to Stream 1 to let other clients know that they exist. This helps clients find out which stream a particular address is using.&lt;/p&gt;

&lt;p&gt;If there are too many messages in a stream, clients can create new streams (child streams) to manage the load. This helps keep the traffic and messages more manageable.&lt;/p&gt;

&lt;p&gt;When someone wants to send you a private message, they need to find your stream and send it there. You then need to connect back to their stream to acknowledge receipt of the message.&lt;/p&gt;

&lt;p&gt;Broadcasts are sent to the stream associated with the sending address. If you want to receive broadcasts from another stream, you have to connect to that stream and check for messages manually. For example, if you’re in Stream 5 and want updates from Stream 1, you need to connect to Stream 1 to get those updates.&lt;/p&gt;

&lt;p&gt;This feature, if implemented fully, could benefit a searching system where users could look up different stream numbers and connect to those. It could also allow users to prioritize streams based on their preferences. This way, important streams receive more attention and resources, while less critical ones are managed with lower priority.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good BitMessage’s Proposals
&lt;/h2&gt;

&lt;p&gt;BitMessage had some good proposals to implement. If development had continued, it could have made a solid network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scalability through Prefix Filtering
&lt;/h3&gt;

&lt;p&gt;This proposal was to make BitMessage more scalable. The proposal is still a work in progress, and some details are yet to be finalized. It suggests a new system to handle how messages are routed through the network to manage increasing traffic efficiently.&lt;/p&gt;

&lt;p&gt;In this proposal, every BitMessage address and network node will have a ‘prefix’ and a ‘prefix length.’ These determine how messages are routed and how much traffic each node handles.&lt;/p&gt;

&lt;p&gt;Nodes will be grouped into overlapping ‘streams’ based on these prefixes. As the network grows or shrinks, both addresses and nodes will move between streams to balance traffic and privacy.&lt;/p&gt;

&lt;p&gt;Each message or object has a ‘prefix nonce’ that determines which streams it will travel through. Objects are processed in their stream and all higher streams in the same branch.&lt;/p&gt;

&lt;h4&gt;
  
  
  Stream Structure
&lt;/h4&gt;

&lt;p&gt;The diagram below shows the proposed structure of streams in BitMessage:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgnfy92702x6e1dv5jsgf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgnfy92702x6e1dv5jsgf.png" alt="Prefix Filter Streams Hierarchy.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the diagram above, the small, light blue circles represent groups of nodes, and the large colored circles represent streams.&lt;/p&gt;

&lt;h4&gt;
  
  
  Node Connections
&lt;/h4&gt;

&lt;p&gt;The diagram below shows the connections between nodes and clients under the proposed system:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya707h0y2if70kswmgfe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya707h0y2if70kswmgfe.png" alt="Node Connections Diagram.png" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nodes and clients are labeled with a stream number, which corresponds to the stream numbers in the stream structure diagram above.&lt;/p&gt;

&lt;h4&gt;
  
  
  Proposed Changes
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Prefixes:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Prefixes route messages through the network. Instead of creating or deleting streams, this proposal uses a vast number of possible prefix values to manage traffic and privacy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node and Address Management:&lt;/strong&gt; Nodes and addresses are assigned a prefix and prefix length to control their traffic handling and balance anonymity with efficiency.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Address and Object Structure:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Address Prefix:&lt;/strong&gt; The prefix is part of the address and does not change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefix Length:&lt;/strong&gt; Determines the address’s stream. Changing the prefix length adjusts the stream assignment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Object Structure:&lt;/strong&gt; Messages (objects) include a ‘prefix nonce’ to dictate their propagation path.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stream Hierarchy:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The proposed system involves a multi-level hierarchy of streams. Each stream level handles different traffic volumes and maintains connections accordingly.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  How Objects Travel and Nodes Connect
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Node Connections:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Nodes connect to multiple nodes in their stream and nearby streams. They also connect directly to other streams as needed.&lt;/li&gt;
&lt;li&gt;Higher-capacity nodes maintain more connections.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Message Propagation:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Nodes process messages in their stream and all higher streams within the same branch.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Client Procedures
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Creating an Address:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The client requests information on current traffic levels, sets a prefix length for the address, and generates a pubkey with this prefix length.&lt;/li&gt;
&lt;li&gt;The client then sends the pubkey to the appropriate nodes based on the stream.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieving a Pubkey:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The client queries nodes in the stream to get the pubkey for an address, matching the address’s prefix value.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sending a Message:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The client sets the prefix nonce of the message to match the destination address’s prefix and sends it to the relevant stream.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieving Messages:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The client connects to nodes in the address’s stream or higher streams to request and process messages.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broadcasts:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Broadcasts are sent to all nodes in the destination address’s stream. Clients need to periodically check these streams for new broadcasts from subscribed addresses.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Argothas Architecture
&lt;/h3&gt;

&lt;p&gt;The goal of this proposal is to make it more scalable and user-friendly while preserving privacy. It is worth mentioning that this system sacrifices some aspects of complete anonymity and the ability to check for new messages instantly.&lt;/p&gt;

&lt;p&gt;The new system involves several types of servers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Data Server (DS):&lt;/strong&gt; Stores actual messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Introduction Server (IS):&lt;/strong&gt; Handles notification messages that inform users about new messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Directory Server (DIR):&lt;/strong&gt; Keeps track of all available servers so clients can find them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;New Address Process&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create Address:&lt;/strong&gt; The user creates a new BitMessage address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find Servers:&lt;/strong&gt; The user connects to a DIR to get a list of available ISs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Select ISs:&lt;/strong&gt; The user chooses some ISs to use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Access:&lt;/strong&gt; The user contacts the chosen ISs to request access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store Details:&lt;/strong&gt; If accepted, the IS’s details are stored with the user’s address.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why Multiple ISs?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redundancy:&lt;/strong&gt; If one IS is down, the user can still receive messages through others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy:&lt;/strong&gt; Using multiple ISs prevents any single IS from seeing all messages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;New Message Process&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create Message:&lt;/strong&gt; The sender prepares the message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose DS:&lt;/strong&gt; The sender picks a Data Server to store the message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store Message:&lt;/strong&gt; The DS responds with a minimum proof-of-work value (security measure).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send Message:&lt;/strong&gt; The sender uploads the message to the DS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prepare Notification:&lt;/strong&gt; The sender creates a notification message (NMESSAGE) with instructions for the recipient.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send Notification:&lt;/strong&gt; The sender sends the NMESSAGE to the chosen ISs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Receiving Messages&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check ISs:&lt;/strong&gt; The recipient periodically connects to their ISs to check for new notifications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieve Messages:&lt;/strong&gt; The recipient decrypts the notification and then fetches the actual messages from the Data Servers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Onion Routing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Onion routing (like TOR) hides users’ IP addresses to prevent tracking and linking of messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Question:&lt;/strong&gt; Should BitMessage clients use TOR, or should the BitMessage network create its own onion routing system?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This proposal suggests a more scalable and private architecture for BitMessage. It involves using multiple servers to handle different types of data, distributing trust among them, and incorporating onion routing for enhanced privacy.&lt;/p&gt;




&lt;p&gt;Bitmessage was a great project aiming to create a fully decentralized and private messaging system. Even though it’s no longer maintained and has some serious issues, it was an appreciable effort in its time.&lt;/p&gt;

&lt;p&gt;By studying Bitmessage and other similar projects, we can learn valuable lessons from their innovations and challenges. These lessons help us understand what worked, what didn’t, and how we can build even better systems in the future.&lt;/p&gt;

&lt;p&gt;The dedication of the people behind Bitmessage, who worked hard to create something meaningful, is a testament to their commitment to privacy and freedom. Their work, despite its limitations, has paved the way for new ideas and improvements in the world of decentralized communication.&lt;/p&gt;

&lt;p&gt;While Bitmessage may be a project of the past, its impact continues to guide future developments in privacy-focused and decentralized technologies.&lt;/p&gt;

&lt;p&gt;As always, this post is freely available on my &lt;a href="https://github.com/zolagonano" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. If you see any problems or have any suggestions, please make a pull request or open an issue.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
