<?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: Alex Antsiferov</title>
    <description>The latest articles on DEV Community by Alex Antsiferov (@wetterkrank).</description>
    <link>https://dev.to/wetterkrank</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F559394%2F55fb4a85-8abc-432d-986c-a509e3363dcc.jpg</url>
      <title>DEV Community: Alex Antsiferov</title>
      <link>https://dev.to/wetterkrank</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wetterkrank"/>
    <language>en</language>
    <item>
      <title>VS Code devcontainer with an external terminal: SSH agent howto</title>
      <dc:creator>Alex Antsiferov</dc:creator>
      <pubDate>Sat, 31 Jan 2026 14:52:48 +0000</pubDate>
      <link>https://dev.to/wetterkrank/ssh-agent-in-a-vs-code-devcontainer-with-a-external-terminal-49ah</link>
      <guid>https://dev.to/wetterkrank/ssh-agent-in-a-vs-code-devcontainer-with-a-external-terminal-49ah</guid>
      <description>&lt;p&gt;I'm not a huge fan of VS Code's internal terminal.&lt;/p&gt;

&lt;p&gt;I keep bumping into its various bugs, like disappearing characters, cmd-V pasting into the wrong window, or the terminal getting closed when switching branches (with &lt;code&gt;scm.workingSets&lt;/code&gt; feature enabled).&lt;br&gt;
And really, I find a terminal an unnecessary bloat for a code editor.&lt;/p&gt;

&lt;p&gt;That said, we do use VS Code and devcontainers at work. &lt;br&gt;
I won't get into the advantages and disadvantages, let's say it's a given.&lt;/p&gt;

&lt;p&gt;In order to use an &lt;strong&gt;external terminal&lt;/strong&gt; with a devcontainer we need something like this, works just fine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CONTAINER_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.ID}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Names}}"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"my-container-name"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; /workspaces/my-project &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONTAINER_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; /bin/zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there's one thing I couldn't get to work in an external terminal -- the SSH agent. Not being able to &lt;code&gt;git pull&lt;/code&gt; is annoying!&lt;/p&gt;

&lt;p&gt;VS Code mounts the host SSH agent's socket to the container. It also sets SSH_AUTH_SOCK var, but only for the internal terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vscode ➜ /workspaces/my-project (main) $ echo $SSH_AUTH_SOCK
/tmp/vscode-ssh-auth-e85d0936-746e-465d-8382-cbf0bb69b476.sock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So here's the workaround: we find it in &lt;code&gt;/tmp&lt;/code&gt; and set &lt;code&gt;SSH_AUTH_SOCK&lt;/code&gt; in our &lt;code&gt;docker exec&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CONTAINER_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.ID}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Names}}"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"my-container-name"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-w&lt;/span&gt; /workspaces/my-project &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONTAINER_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  sh &lt;span class="nt"&gt;-lc&lt;/span&gt; &lt;span class="s1"&gt;'
    for sock in /tmp/vscode-ssh-auth-*.sock; do
      [ -S "$sock" ] || continue
      SSH_AUTH_SOCK="$sock" ssh-add -l &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; {
        export SSH_AUTH_SOCK="$sock"
        echo "Using SSH_AUTH_SOCK=$SSH_AUTH_SOCK"
        break
      }
    done

    if [ -z "$SSH_AUTH_SOCK" ]; then
      echo "No working VS Code SSH agent socket found"
    fi

    exec /bin/zsh
  '&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A bit hacky, but it does the job! Enjoy working from your favorite terminal!&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>devcontainers</category>
      <category>terminal</category>
      <category>ssh</category>
    </item>
    <item>
      <title>On comparing time intervals</title>
      <dc:creator>Alex Antsiferov</dc:creator>
      <pubDate>Sun, 09 Feb 2025 15:21:55 +0000</pubDate>
      <link>https://dev.to/wetterkrank/on-comparing-time-intervals-44on</link>
      <guid>https://dev.to/wetterkrank/on-comparing-time-intervals-44on</guid>
      <description>&lt;p&gt;Here's an edge case to keep in mind when querying the database for something that has duration (for example, events) using a &lt;code&gt;from..to&lt;/code&gt; date/time filter: long events can start &lt;strong&gt;before&lt;/strong&gt; &lt;code&gt;from&lt;/code&gt; and end &lt;strong&gt;after&lt;/strong&gt; &lt;code&gt;to&lt;/code&gt;.&lt;br&gt;
So if we check only that the &lt;code&gt;start&lt;/code&gt; or &lt;code&gt;end&lt;/code&gt; time of the event fall within the interval, we can miss an event, even though it overlaps ("covers") the required interval.&lt;/p&gt;

</description>
      <category>edgecases</category>
      <category>softcs</category>
    </item>
    <item>
      <title>Tracing a Ruby method call in production</title>
      <dc:creator>Alex Antsiferov</dc:creator>
      <pubDate>Sun, 05 Jan 2025 12:08:39 +0000</pubDate>
      <link>https://dev.to/wetterkrank/tracing-a-method-call-1dgd</link>
      <guid>https://dev.to/wetterkrank/tracing-a-method-call-1dgd</guid>
      <description>&lt;p&gt;Sometimes, we can't reproduce the issue in development and need to trace a method call in production console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;trace&lt;/span&gt;
  &lt;span class="n"&gt;trace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TracePoint&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="ss"&gt;:call&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; 
    &lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineno&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method_id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enable&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt;
  &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disable&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;trace&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;some_method&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In most cases, we want to see our own code only, so let's exclude libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;trace&lt;/span&gt;
  &lt;span class="n"&gt;trace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TracePoint&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="ss"&gt;:call&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineno&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method_id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="sr"&gt;/gems|ruby/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enable&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt;
  &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disable&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are also &lt;a href="https://ruby-doc.org/core-3.0.3/TracePoint.html#class-TracePoint-label-Events" rel="noopener noreferrer"&gt;other events to trace&lt;/a&gt;, for example, &lt;code&gt;:raise&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Rails: passing data to the browser with &lt;meta&gt;</title>
      <dc:creator>Alex Antsiferov</dc:creator>
      <pubDate>Sun, 05 Jan 2025 11:56:37 +0000</pubDate>
      <link>https://dev.to/wetterkrank/passing-data-to-the-browser-with-3i95</link>
      <guid>https://dev.to/wetterkrank/passing-data-to-the-browser-with-3i95</guid>
      <description>&lt;p&gt;I'll be posting code snippets like this -- solutions to little problems that I come across in everyday work.&lt;/p&gt;

&lt;p&gt;This one is one of the simplest ways to share some structured data with the front end using &lt;a href="https://apidock.com/rails/ActionView/Helpers/CaptureHelper/content_for" rel="noopener noreferrer"&gt;content_for&lt;/a&gt; helper and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta" rel="noopener noreferrer"&gt;meta&lt;/a&gt; element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .erb&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% content_for &lt;/span&gt;&lt;span class="ss"&gt;:head&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="sx"&gt;%&amp;gt;
  &amp;lt;meta name="project-prices" content=&amp;lt;%=prices.to_json%&amp;gt;&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// .js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[name="project-prices"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



</description>
      <category>rails</category>
      <category>javascript</category>
      <category>snippet</category>
    </item>
    <item>
      <title>Moving Mongo Out of the Container | MongoDB Atlas Hackathon 2022 on DEV</title>
      <dc:creator>Alex Antsiferov</dc:creator>
      <pubDate>Wed, 07 Dec 2022 15:36:39 +0000</pubDate>
      <link>https://dev.to/wetterkrank/moving-out-of-the-container-l4a</link>
      <guid>https://dev.to/wetterkrank/moving-out-of-the-container-l4a</guid>
      <description>&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;I have a pet project that I started some time ago, while studying programming. It's a Telegram bot for people learning German, called Dasbot.&lt;br&gt;
I'm pretty proud of its daily audience of a few hundred users who have collectively answered more than 300k quiz questions 😎, but I must confess: until now its database has been residing in a Docker container 📦. Like, not even on a mounted volume 🤦‍♂️.&lt;br&gt;
This hackaton motivated me to amend this gruesome mistake.&lt;/p&gt;

&lt;p&gt;Also, now that I know about change streams, I can display  some real time stats on the bot's web page, yay!&lt;/p&gt;
&lt;h3&gt;
  
  
  Category Submission:
&lt;/h3&gt;

&lt;p&gt;No idea! Just wanted to share some life lessons :)&lt;/p&gt;
&lt;h3&gt;
  
  
  App Link
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dasbot.yak.supplies" rel="noopener noreferrer"&gt;https://dasbot.yak.supplies&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You're welcome to use the bot and answer its questions! (Especially if you're struggling with German like I do). If it annoys you, just ban it 😊&lt;/p&gt;
&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&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%2F76bbmhhhkz3oou6iafey.gif" 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%2F76bbmhhhkz3oou6iafey.gif" alt="Dasbot stats page" width="990" height="760"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Description
&lt;/h3&gt;

&lt;p&gt;German language is difficult! Especially terrible are its grammatical genders which defy any logic, so you just have to memorize them.&lt;br&gt;
Dasbot actually helps you do this, with a simple spaced repetition algorithm.&lt;/p&gt;

&lt;p&gt;It's written in Python, because I was studying Python at that time.&lt;br&gt;
And it's using MongoDB for database, because I didn't need much structure in my documents.&lt;br&gt;
(There should be a photo of my desk here, covered with all the bureaucratic papers they send you twice a day here in Germany 📩).&lt;/p&gt;

&lt;p&gt;In the database I keep everyone's scores neeeded for the repetition system. I also collect stats (user, word, answer, time) -- there could be some useful insights in there.&lt;/p&gt;
&lt;h3&gt;
  
  
  Link to Source Code
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/wetterkrank/dasbot" rel="noopener noreferrer"&gt;https://github.com/wetterkrank/dasbot&lt;/a&gt; -- main app&lt;br&gt;
&lt;a href="https://github.com/wetterkrank/dasbot-docs-live" rel="noopener noreferrer"&gt;https://github.com/wetterkrank/dasbot-docs-live&lt;/a&gt; -- web app with the new /stats page&lt;/p&gt;
&lt;h3&gt;
  
  
  Permissive License
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/wetterkrank/dasbot-docs-live/blob/master/README.md" rel="noopener noreferrer"&gt;MIT License&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;So, I used Docker.&lt;br&gt;
It's a great tool! And I guess it's ok for a study project to spawn a database in a container. But when you do it in "production", you start collecting some gotchas. Here's a couple of mine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;mongo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0:27017:27017"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;-- this was a part of my &lt;code&gt;docker-compose.yml&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;After the launch, everything worked fine for a few days, and then I found my database empty! &lt;br&gt;
I checked the Mongo logs and found some &lt;code&gt;dropDatabase&lt;/code&gt; calls coming from unknown IPs. Hacked! 🪓 But how!? I knew my &lt;code&gt;ufw&lt;/code&gt; rules by heart! What I didn't know is that Docker keeps its own &lt;code&gt;iptables&lt;/code&gt; and will not be trammelled by a mere firewall.&lt;br&gt;
So when you expose the port using &lt;code&gt;0.0.0.0&lt;/code&gt;, you share it with the world full of people with port scanners.&lt;/p&gt;

&lt;p&gt;Fast forward to this November. I just updated a config setting and decided to restart the containers manually.&lt;br&gt;
Then I pinged the bot and was slightly surprised that it didn't recognise me. So I looked at the db collections... interesting... 0 documents... 😰&lt;br&gt;
After scrolling up the shell history, I noticed that I typed &lt;code&gt;docker-compose down&lt;/code&gt; instead of &lt;code&gt;docker-compose stop&lt;/code&gt;. Here goes my data! Luckily, I had a backup 😅. &lt;/p&gt;
&lt;h3&gt;
  
  
  How I built it
&lt;/h3&gt;

&lt;p&gt;As for the &lt;strong&gt;moving to Atlas&lt;/strong&gt; part: this was simple!&lt;br&gt;
I would have loved to use the &lt;a href="https://www.mongodb.com/cloud/atlas/migrate" rel="noopener noreferrer"&gt;live migration service&lt;/a&gt; but I decided to start with M0 cluster so didn't have the opportunity and just used &lt;code&gt;mongorestore&lt;/code&gt; instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DB_CONTAINER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"dasbot_db"&lt;/span&gt;
&lt;span class="nv"&gt;RESTORE_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mongodb+srv://&lt;/span&gt;&lt;span class="nv"&gt;$DB_USERNAME&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$DB_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;@mydb.smth.mongodb.net/"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Piping mongodump to mongorestore with Atlas as destination..."&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nv"&gt;$DB_CONTAINER&lt;/span&gt; mongodump &lt;span class="nt"&gt;--db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dasbot &lt;span class="nt"&gt;--archive&lt;/span&gt; | mongorestore &lt;span class="nt"&gt;--archive&lt;/span&gt; &lt;span class="nt"&gt;--drop&lt;/span&gt; &lt;span class="nt"&gt;--uri&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RESTORE_URI&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One notable hiccup was the speed of &lt;code&gt;mongorestore&lt;/code&gt; -- a pitiful 50Mb of data took several minutes to load! However, increasing the number of workers (&lt;code&gt;numInsertionWorkersPerCollection&lt;/code&gt;) helped. &lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;change streams (real time stats)&lt;/strong&gt; exercise I had to refresh my knowledge of aggregation pipelines and write some JS code. I already mentioned &lt;code&gt;stats&lt;/code&gt; collection above, it can be used to build all kinds of reports.&lt;/p&gt;

&lt;p&gt;So I've added a couple of triggers which are responsible for aggregating this data and publishing the updates to a separate database, and an Atlas app that lets users access this database anonymously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Scheduled to run twice per day&lt;/span&gt;
&lt;span class="c1"&gt;// Updates correct / incorrect counters in answers_total&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mongodb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DasbotData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mongodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dasbot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stats&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pipeline&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="na"&gt;$group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$cond&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$eq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$correct&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;correct&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;incorrect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$sum&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;$out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dasbot-meta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;coll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;answers_total&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pipeline&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This runs on every `stats` insert and updates the aggregated results&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changeEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DasbotData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dasbot-meta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;answers_total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;answers_total&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fullDocument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;changeEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullDocument&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fullDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;correct&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;correct&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;incorrect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;upsert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nx"&gt;answers_total&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$inc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;count&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// { _id:, value: }&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To display the data, I made &lt;a href="https://github.com/wetterkrank/dasbot-docs-live" rel="noopener noreferrer"&gt;a simple React app&lt;/a&gt; that uses the Realm Web SDK. Now, when someone answers the bot's question, you can immediately see it ⚡.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Resources/Info
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.mongodb.com/developer/products/mongodb/real-time-data-javascript/?utm_campaign=dev_hackathon&amp;amp;utm_source=devto&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;This tutorial&lt;/a&gt; was quite handy!&lt;/p&gt;

</description>
      <category>atlashackathon22</category>
      <category>mongodb</category>
      <category>docker</category>
    </item>
    <item>
      <title>Ruby 3.1.x and OpenSSL::Digest::DigestError</title>
      <dc:creator>Alex Antsiferov</dc:creator>
      <pubDate>Tue, 15 Nov 2022 23:02:58 +0000</pubDate>
      <link>https://dev.to/wetterkrank/openssldigestdigesterror-when-using-md4-3o0i</link>
      <guid>https://dev.to/wetterkrank/openssldigestdigesterror-when-using-md4-3o0i</guid>
      <description>&lt;p&gt;Came across this issue after upgrading Ruby to 3.1.0 in one of the apps I'm working on.&lt;/p&gt;

&lt;p&gt;I use &lt;code&gt;rbenv&lt;/code&gt; to manage Ruby versions, and it turns out they recently &lt;a href="https://github.com/rbenv/ruby-build/pull/2000" rel="noopener noreferrer"&gt;started building Ruby with OpenSSL 3.0 instead of OpenSSL 1.1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;OpenSSL 3.0, in turn, &lt;a href="https://www.openssl.org/docs/man3.0/man7/migration_guide.html#Legacy-Algorithms" rel="noopener noreferrer"&gt;has retired a number of algorithms&lt;/a&gt; (including MD4 function, which was used in that app).&lt;/p&gt;

&lt;p&gt;And hello! After the update, we call &lt;code&gt;OpenSSL::Digest::MD4.hexdigest()&lt;/code&gt; and get this:&lt;br&gt;
&lt;code&gt;OpenSSL::Digest::DigestError, message: Digest initialization failed: initialization error&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So, if you're using &lt;code&gt;rbenv&lt;/code&gt; to update Ruby to 3.1.x and your code uses one of the retired algorithms, there's a chance you'll come across a similar issue.&lt;/p&gt;

&lt;p&gt;A temporary workaround if you can't get rid of legacy code right away:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;install Ruby with OpenSSL 1.1 (note I'm using brew here): &lt;code&gt;export RUBY_CONFIGURE_OPTS="--with-openssl-dir=$(brew --prefix openssl@1.1)" &amp;amp;&amp;amp; rbenv install 3.1.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;as an alternative, look into re-enabling MD4 in OpenSSL (&lt;a href="https://www.openssl.org/docs/man3.0/man7/openssl_user_macros.html" rel="noopener noreferrer"&gt;this looks useful&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another alternative would be to use an &lt;a href="https://rosettacode.org/wiki/MD4#Ruby" rel="noopener noreferrer"&gt;MD4 implementation in pure Ruby&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>openssl</category>
      <category>rbenv</category>
    </item>
    <item>
      <title>unexpected EF BB BF</title>
      <dc:creator>Alex Antsiferov</dc:creator>
      <pubDate>Sat, 26 Feb 2022 22:58:23 +0000</pubDate>
      <link>https://dev.to/wetterkrank/unexpected-ef-bb-bf-5fi5</link>
      <guid>https://dev.to/wetterkrank/unexpected-ef-bb-bf-5fi5</guid>
      <description>&lt;p&gt;Got this funny little hiccup while setting up a remote Ubuntu server from a Windows machine.&lt;/p&gt;

&lt;p&gt;As soon as I created the user account, I decided to add my public key to &lt;code&gt;authorized_keys&lt;/code&gt; so I could log in without password. Since Windows doesn't have &lt;code&gt;ssh-copy-id&lt;/code&gt;, I ran this nifty command in PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type $env:USERPROFILE\.ssh\id_rsa.pub | ssh {SERVER} "cat &amp;gt;&amp;gt; .ssh/authorized_keys"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My public key was copied to &lt;code&gt;authorized_keys&lt;/code&gt;, perfect! But on the next login I still got the password prompt. What the..? &lt;/p&gt;

&lt;p&gt;I ran &lt;code&gt;ssh&lt;/code&gt; with &lt;code&gt;-vv&lt;/code&gt; key to get more info, rechecked everything twice, and finally noticed that the newly added key had some extra characters.&lt;br&gt;
And no, these were not CRLF. This time it was a mysterious EF BB BF sequence also known as &lt;a href="https://en.wikipedia.org/wiki/Byte_order_mark" rel="noopener noreferrer"&gt;BOM&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; 00000000: efbb bf73 7368 2d72 7361 2041 4141 4142  ...ssh-rsa AAAAB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After I removed them, &lt;code&gt;ssh&lt;/code&gt; recognized my key and everything worked as expected. Don't I just love this stuff!&lt;/p&gt;

&lt;p&gt;A couple more useful commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;xxd myfile # hex dump myfile
diff -y &amp;lt;(xxd myfile1.bin) &amp;lt;(xxd myfile2.bin) # compare binaries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ssh</category>
      <category>bom</category>
      <category>utf8</category>
    </item>
    <item>
      <title>Building LineageOS in WSL2</title>
      <dc:creator>Alex Antsiferov</dc:creator>
      <pubDate>Wed, 03 Nov 2021 20:49:58 +0000</pubDate>
      <link>https://dev.to/wetterkrank/building-lineageos-in-wsl-51jg</link>
      <guid>https://dev.to/wetterkrank/building-lineageos-in-wsl-51jg</guid>
      <description>&lt;p&gt;A few days ago, I needed an Android phone for some experiments, so I fished out an old Nexus 5 "Hammerhead" from a drawer where I keep the assorted lengths of wires. &lt;/p&gt;

&lt;p&gt;However, it had a pretty outdated Android 6 OS, and some apps refused to install. So I decided to get it a slightly newer LineageOS 14.1 (based on Android 7). Feeling adventurous, I decided to build one myself instead of downloading a ready build (from god-knows-where, by the way).&lt;/p&gt;

&lt;p&gt;I used &lt;a href="https://wiki.lineageos.org/devices/hammerhead/install" rel="noopener noreferrer"&gt;this guide&lt;/a&gt;. Overall, it was surprisingly comprehensive. Still, I encountered a few obstacles, writing them down here for reference.&lt;/p&gt;

&lt;h3&gt;
  
  
  Notable hiccups
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Preparing tools&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;libwxgtk3.0-dev was renamed to libwxgtk3.0-gtk3-dev in Ubuntu 20.04&lt;/li&gt;
&lt;li&gt;to access USB devices in WSL, I had to run &lt;code&gt;adb&lt;/code&gt; in tcp bridge mode (see &lt;a href="https://github.com/microsoft/WSL/issues/4619#issuecomment-596990676" rel="noopener noreferrer"&gt;https://github.com/microsoft/WSL/issues/4619#issuecomment-596990676&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Getting the source files in place&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I'm using zsh, and it doesn't normally execute .profile, so my &lt;code&gt;$HOME/bin&lt;/code&gt; wasn't included in the path -- &lt;code&gt;repo&lt;/code&gt; didn't run. Being lazy, I just dropped it in the sources folder, and was punished on one of the next steps: failure to execute &lt;code&gt;repo&lt;/code&gt; was breaking the &lt;code&gt;breakfast&lt;/code&gt; pipeline. Luckily, the error was logged so I figured it out and fixed properly.&lt;/li&gt;
&lt;li&gt;Proprietary blobs for the device: this seemed like a Catch-22 situation; then I realised that I could get those from TheMuppets (&lt;a href="https://github.com/TheMuppets/proprietary_vendor_lge/tree/cm-14.1" rel="noopener noreferrer"&gt;https://github.com/TheMuppets/proprietary_vendor_lge/tree/cm-14.1&lt;/a&gt;); note the branch in the path.&lt;/li&gt;
&lt;li&gt;TheMuppets repo must be included in the .repo/local_manifests/roomservice.xml like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;manifest&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;project&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"LineageOS/android_device_lge_hammerhead"&lt;/span&gt; &lt;span class="na"&gt;path=&lt;/span&gt;&lt;span class="s"&gt;"device/lge/hammerhead"&lt;/span&gt; &lt;span class="na"&gt;remote=&lt;/span&gt;&lt;span class="s"&gt;"github"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;project&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"LineageOS/android_kernel_lge_hammerhead"&lt;/span&gt; &lt;span class="na"&gt;path=&lt;/span&gt;&lt;span class="s"&gt;"kernel/lge/hammerhead"&lt;/span&gt; &lt;span class="na"&gt;remote=&lt;/span&gt;&lt;span class="s"&gt;"github"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;project&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"TheMuppets/proprietary_vendor_lge"&lt;/span&gt; &lt;span class="na"&gt;path=&lt;/span&gt;&lt;span class="s"&gt;"vendor/lge"&lt;/span&gt; &lt;span class="na"&gt;remote=&lt;/span&gt;&lt;span class="s"&gt;"github"&lt;/span&gt; &lt;span class="na"&gt;revision=&lt;/span&gt;&lt;span class="s"&gt;"cm-14.1"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/manifest&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;After rerunning &lt;code&gt;repo sync&lt;/code&gt; the blobs were added to the source files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Building&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The build process was inexplicably failing somewhere around 2%. I ran &lt;code&gt;jack-diagnose&lt;/code&gt; tool from &lt;code&gt;prebuilts/sdk/tools&lt;/code&gt;, but it wasn't detecting any issues. Neither the jack-server logs had anything of note in them.&lt;/li&gt;
&lt;li&gt;I suspected that jack-server was quietly crashing due to insufficient memory, and did several things at once: 

&lt;ul&gt;
&lt;li&gt;increased WSL memory limit from 6Gb to 8Gb (my laptop has 16Gb total), &lt;/li&gt;
&lt;li&gt;in &lt;code&gt;~/.jack-server/config.properties&lt;/code&gt;, set &lt;code&gt;jack.server.max-service&lt;/code&gt; to 1 instead of 4, &lt;/li&gt;
&lt;li&gt;set &lt;code&gt;-Xmx5G&lt;/code&gt; instead of &lt;code&gt;-Xmx4G&lt;/code&gt; in &lt;code&gt;ANDROID_JACK_VM_ARGS&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;and upgraded jack-server package as suggested &lt;a href="https://forum.xda-developers.com/t/discussion-how-to-fix-jack-server-failing-to-build-with-error-try-jack-diagnose.3575179/#post-71485731" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Not sure which exact part was crucial, but after doing this the build went on smoothly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Loading the firmware&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The TWRP recovery wouldn't start from the bootloader, so I had to use this command: &lt;code&gt;fastboot boot &amp;lt;recovery.img&amp;gt;&lt;/code&gt; instead.&lt;/li&gt;
&lt;li&gt;I had to install the "pico" (smallest) Google Apps package along the OS ROM, otherwise it wouldn't fit in without repartitioning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Finally&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The first boot of LineageOS took a while, and I had to reboot it once when it got stuck at "looking for updates" step. &lt;br&gt;
After the initial setup, it has been working pretty well! On with the experiments!&lt;/p&gt;

</description>
      <category>wsl</category>
      <category>android</category>
      <category>lineageos</category>
    </item>
    <item>
      <title>Setting up incremental backups for WSL2</title>
      <dc:creator>Alex Antsiferov</dc:creator>
      <pubDate>Sat, 10 Apr 2021 22:50:16 +0000</pubDate>
      <link>https://dev.to/wetterkrank/setting-up-automated-backups-for-wsl2-2mh3</link>
      <guid>https://dev.to/wetterkrank/setting-up-automated-backups-for-wsl2-2mh3</guid>
      <description>&lt;p&gt;Since I've started using WSL, most of my dev projects have migrated there.&lt;br&gt;
And while the code is mostly stored on GitHub, there's still a question of backing up the rest of the files (dotfiles, auxiliary scripts, data etc). Basically, I want to run daily incremental backups of my home dir, preferably encryped.&lt;/p&gt;

&lt;p&gt;In Windows I have a nice open-source tool called Duplicati which makes daily incremental encrypted backups to a NAS. So, I was hoping to use it for WSL, too.&lt;/p&gt;
&lt;h3&gt;
  
  
  Fail: The Windows way
&lt;/h3&gt;

&lt;p&gt;As the first shot, I tried accessing WSL files from Duplicati running in Windows, and added this path to my backup: &lt;code&gt;\\wsl$\Ubuntu\home&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The performance was horrendous, just as I expected. ⛔&lt;br&gt;
(Maybe I should have excluded all those &lt;code&gt;node_modules&lt;/code&gt; after all... 🤔) &lt;/p&gt;

&lt;p&gt;The next experiment was grabbing the whole WSL file system image file. It's located at &lt;code&gt;%LOCALAPPDATA%\Packages\ CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\ext4.vhdx&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;However, at some point WSL wouldn't start; I guess the file got locked by the backup process. Bad idea! Having one big file isn't good for incremental backups anyway, so I dropped this plan. ⛔&lt;/p&gt;

&lt;p&gt;The "official" method with &lt;code&gt;wsl.exe –export&lt;/code&gt; also doesn't suit me, since it requires shutting down WSL and produces another big file. ⛔&lt;/p&gt;

&lt;p&gt;Well, it looks like I'll have to do everything from inside WSL! And it has plenty of tools to choose from.&lt;/p&gt;
&lt;h3&gt;
  
  
  Success: The Linux way
&lt;/h3&gt;

&lt;p&gt;The first candidate is &lt;strong&gt;rsync&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rsync -av -e ssh /home rsync@192.168.1.200::/backup/wsl-files/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(On the NAS, I've added the user 'rsync' with proper privileges).&lt;/p&gt;

&lt;p&gt;This does the job pretty quickly and can be automated. To avoid the interactive password prompt, I'd need a key- or a password file (both require some effort to set up). 🛠&lt;/p&gt;

&lt;p&gt;However, I wanted to have my files in the destination folder encrypted. So I decided to use &lt;strong&gt;duplicity&lt;/strong&gt;. As a quick test, I ran this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PASSPHRASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;MySuperPassphrase
duplicity /home sftp://duplicity:MySuperPassword@192.168.1.200/home/wsl-backup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The PASSPHRASE is used to encrypt the files.&lt;br&gt;
By default, duplicity creates full backup on the 1st run and incremental backups after. 🎯&lt;/p&gt;

&lt;p&gt;There are more advanced script examples &lt;a href="https://help.ubuntu.com/community/DuplicityBackupHowto" rel="noopener noreferrer"&gt;in the Ubuntu help&lt;/a&gt;. Following them, I came to something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#! /bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;BASEDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Export vars from .env (no spaces or = in values). Required:&lt;/span&gt;
&lt;span class="c"&gt;# NAS_HOST=&lt;/span&gt;
&lt;span class="c"&gt;# NAS_USER=&lt;/span&gt;
&lt;span class="c"&gt;# NAS_PASSWORD=&lt;/span&gt;
&lt;span class="c"&gt;# PASSPHRASE=&lt;/span&gt;

&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;egrep &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s1"&gt;'^#'&lt;/span&gt; &lt;span class="nv"&gt;$BASEDIR&lt;/span&gt;/.env | xargs&lt;span class="si"&gt;)&lt;/span&gt;

duplicity &lt;span class="nt"&gt;--full-if-older-than&lt;/span&gt; 1M /home/alex/ sftp://&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAS_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAS_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;@&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAS_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/home/wsl-backup
duplicity remove-older-than 3M &lt;span class="nt"&gt;--force&lt;/span&gt; sftp://&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAS_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAS_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;@&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAS_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/home/wsl-backup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The variables used in the script are kept in the &lt;code&gt;.env&lt;/code&gt; file located in the same dir as the script.&lt;/p&gt;

&lt;p&gt;Now I just need to schedule my backup script like this, right? (&lt;code&gt;crontab -e&lt;/code&gt; to edit the current user's cron table)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 21 * * * /home/alex/nas-backup/backup.sh &amp;gt;&amp;gt;/home/alex/nas-backup/duplicity.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not so fast! It's WSL for you, so somebody has to start the cron service. 👷‍♂️&lt;/p&gt;

&lt;p&gt;There are different ways to do it, I was lazy and added this "autostart" to my .zshrc file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;service cron status || sudo service cron start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means the backup won't run if I'm not using WSL, but that's only logical.&lt;br&gt;
Windows scheduler can be used as an alternative, as described &lt;a href="https://stephenreescarter.net/automatic-backups-for-wsl2/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One last step. Since I'm starting the cron service as a superuser, I have to enter the password every time. To avoid this, I created a rule in &lt;code&gt;/etc/sudoers.d/service&lt;/code&gt;, disabling the password request for this case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alex ALL=NOPASSWD:/usr/sbin/service cron start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's a bonus script to run once in a while and verify the files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#! /bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;egrep &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s1"&gt;'^#'&lt;/span&gt; .env | xargs&lt;span class="si"&gt;)&lt;/span&gt;
duplicity verify &lt;span class="nt"&gt;-vInfo&lt;/span&gt; sftp://&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAS_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAS_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;@&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAS_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/home/wsl-backup /home/alex/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we're all set! ✅&lt;br&gt;
But wait, what about the databases and Docker containers? Well, maybe next time...&lt;/p&gt;

</description>
      <category>wsl2</category>
      <category>backup</category>
      <category>duplicity</category>
      <category>rsync</category>
    </item>
    <item>
      <title>Pixel art in Excel</title>
      <dc:creator>Alex Antsiferov</dc:creator>
      <pubDate>Sun, 21 Mar 2021 11:51:57 +0000</pubDate>
      <link>https://dev.to/wetterkrank/pixel-art-in-excel-49h0</link>
      <guid>https://dev.to/wetterkrank/pixel-art-in-excel-49h0</guid>
      <description>&lt;p&gt;One day, I had this weird idea of displaying an animation in Excel using the cells as pixels.&lt;br&gt;
First of all, I had to check if it was possible at all, and also how fast Excel would render the "pixels".&lt;/p&gt;

&lt;p&gt;So I grabbed a 320x200 image of Dangerous Dave &lt;a href="https://github.com/gmegidish/dangerous-dave-re" rel="noopener noreferrer"&gt;here&lt;/a&gt; and set off...&lt;br&gt;
To convert a PNG file to CSV I used &lt;a href="https://github.com/shark0der/image2excel/blob/master/img2csv.php" rel="noopener noreferrer"&gt;this PHP tool&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;VBA code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'Canvas reset button
Sub Button2_Click()
    Range("A1:LH200").ColumnWidth = 0.25
    Range("A1:LH200").RowHeight = 2
    Range("A1:LH200").Interior.ColorIndex = 0
End Sub

'Draw button
Sub Button1_Click()
    DrawCSVFile
End Sub

`The draw subroutine itself
Public Sub DrawCSVFile()
    Dim FilePath As String
    FilePath = ActiveWorkbook.Path &amp;amp; "\title1.csv"
    Open FilePath For Input As #1

    Y = 1
    Do Until EOF(1)
        Line Input #1, LineFromFile
        LineItems = Split(LineFromFile, ",")
        For X = 1 To 320
            RGBString = LineItems(X - 1)
            R = Val("&amp;amp;H" &amp;amp; Mid(RGBString, 1, 2))
            G = Val("&amp;amp;H" &amp;amp; Mid(RGBString, 3, 2))
            B = Val("&amp;amp;H" &amp;amp; Mid(RGBString, 5, 2))
            RGBExcel = RGB(R, G, B)
            Cells(Y, X).Interior.Color = RGBExcel
        Next
        Y = Y + 1
    Loop

    Close #1
End Sub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result: well, it works :)&lt;br&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%2Fc3q6dbkm3dq3xhw74das.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%2Fc3q6dbkm3dq3xhw74das.png" alt="Dangerous Dave title screen in Excel" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But the image above took a full whopping minute to draw.&lt;br&gt;
Reading the CSV line by line and painting individual pixels turned out to be really slow!&lt;/p&gt;

&lt;p&gt;I guess some day I should try the range copy method :)&lt;/p&gt;

&lt;p&gt;A couple of things I learned along the way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;while reading a text file, VBA expects the lines to end with CR or CRLF (in Windows at least); LF is not recognized as the line end&lt;/li&gt;
&lt;li&gt;RGB in Excel is actually BGR&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>excel</category>
      <category>vba</category>
      <category>bgr</category>
    </item>
    <item>
      <title>Troubleshooting WSL + localhost:3000 'connection refused' issue</title>
      <dc:creator>Alex Antsiferov</dc:creator>
      <pubDate>Tue, 02 Mar 2021 23:33:34 +0000</pubDate>
      <link>https://dev.to/wetterkrank/wsl2-localhost-3000-issue-191b</link>
      <guid>https://dev.to/wetterkrank/wsl2-localhost-3000-issue-191b</guid>
      <description>&lt;p&gt;Just spent an hour trying to figure out why the browser wouldn't open localhost:3000 (served by Webpack/create-react-app in &lt;strong&gt;WSL2&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;Normally, a server running in WSL should be accessible from the Windows (host) machine without any issues.&lt;/p&gt;

&lt;p&gt;My troubleshooting steps, roughly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;check &lt;code&gt;/windows/system32/drivers/etc/hosts&lt;/code&gt; file &lt;em&gt;--nothing special here...&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;check Windows firewall rules, just in case&lt;/li&gt;
&lt;li&gt;shut down WSL VM with &lt;code&gt;wsl --shutdown&lt;/code&gt; command and restart it&lt;/li&gt;
&lt;li&gt;run another app in WSL using a different port &lt;em&gt;-- hmm, this works...&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;change the port of the app in question in Webpack config: &lt;code&gt;PORT=3006 react-scripts start&lt;/code&gt; &lt;em&gt;-- also works... so the problem is in the specific port!&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;see if some process is holding the port 3000 in Linux: &lt;code&gt;ss -tulpn&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;check if a process is holding the port in Windows: &lt;code&gt;netstat -ano | findstr :3000&lt;/code&gt; or &lt;em&gt;Resource Monitor -&amp;gt; Network tab -&amp;gt; Listening ports&lt;/em&gt; -- &lt;strong&gt;aha! there is one!&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;find the process name by PID in the &lt;em&gt;Task Manager -&amp;gt; Services&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Turns out, the port was taken by &lt;strong&gt;IP Helper&lt;/strong&gt; service (iphlpsvc).&lt;/p&gt;

&lt;p&gt;I could not find a way to reconfigure it, and some quick research &lt;a href="http://revertservice.com/10/iphlpsvc/" rel="noopener noreferrer"&gt;showed&lt;/a&gt; that it wasn't a critical service (at least in my case), so I just disabled it in the &lt;em&gt;Services&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>wsl2</category>
      <category>createreactapp</category>
      <category>iphelper</category>
    </item>
  </channel>
</rss>
