<?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>Forem: Benji377</title>
    <description>The latest articles on Forem by Benji377 (@benji377).</description>
    <link>https://forem.com/benji377</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%2F2931673%2Fbe4339ea-2302-43bf-8b2c-7b3c3f3b3a1d.png</url>
      <title>Forem: Benji377</title>
      <link>https://forem.com/benji377</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/benji377"/>
    <language>en</language>
    <item>
      <title>OpenMusic - My first Open source project</title>
      <dc:creator>Benji377</dc:creator>
      <pubDate>Sun, 22 Mar 2026 16:29:48 +0000</pubDate>
      <link>https://forem.com/benji377/openmusic-my-first-open-source-project-2kha</link>
      <guid>https://forem.com/benji377/openmusic-my-first-open-source-project-2kha</guid>
      <description>&lt;h2&gt;
  
  
  OpenMusic (formerly SocyMusic)
&lt;/h2&gt;

&lt;p&gt;OpenMusic began as my venture into creating the ultimate free open-source music player for Android devices. My aim was simple: a clean, minimalist design packed with features catering to music enthusiasts of all kinds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Spark of Inspiration
&lt;/h2&gt;

&lt;p&gt;This project sparked to life during one summer break when I found myself with ample free time and a desire to delve into Java programming for Android. While I had prior experience with Java, crafting an Android app was an uncharted territory for me. I figured that designing a music player would be a straightforward task, and so, I embarked on this exciting journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Learning Curve
&lt;/h2&gt;

&lt;p&gt;With little guidance on what my music player should offer, I turned to Reddit for suggestions. What I didn't anticipate was the flood of responses that poured in. My humble music player project on GitHub garnered unexpected attention, attracting a diverse community eager to collaborate and assist.&lt;/p&gt;

&lt;p&gt;My misstep here was relying too heavily on these contributors. As they poured in code, the project's scope spiraled out of control, and I soon lost sight of its direction. Additionally, I made the mistake of attempting to meet every demand, which eventually overwhelmed me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setback
&lt;/h2&gt;

&lt;p&gt;As my project spiraled out of control, real life beckoned with the return of school. This significantly reduced my free time, leading to the inevitable downfall of OpenMusic. I had no choice but to shelve it, albeit not permanently. I opted to archive the project, ensuring that others could still benefit from it if the need arose.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lessons Learned
&lt;/h2&gt;

&lt;p&gt;OpenMusic taught me invaluable lessons in managing open-source projects. Structuring projects methodically, maintaining order, and documenting every step and feature are crucial to avoid chaos. Effective communication is equally vital. Without a means to interact with fellow developers, seeking specific features, conducting tests, and documenting code contributions become nearly impossible.&lt;/p&gt;

&lt;p&gt;Even long after I archived the project, I stumbled upon Reddit posts expressing disappointment about the unfinished app. This tugged at my heartstrings, but by then, the post was old, and I had moved on to other projects. I made the difficult choice to let OpenMusic remain in the past.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Retrospect
&lt;/h2&gt;

&lt;p&gt;The key takeaway from this experience is not to push oneself beyond limits. Setting priorities in life is crucial to prevent unnecessary stress. Careful planning, constant communication with contributors, and publishing projects when you're genuinely ready for feedback are all essential. It's important to recognize that it's impossible for a single person to appease everyone's desires in a project.&lt;/p&gt;

&lt;p&gt;Ultimately, OpenMusic served as a valuable teacher. I made mistakes along the way, but I wouldn't change a thing. I'm proud of what I accomplished, and it was a significant learning opportunity.&lt;/p&gt;

&lt;p&gt;You can find the archived OpenMusic project &lt;a href="https://github.com/Benji377/OpenMusic" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>android</category>
      <category>opensource</category>
      <category>music</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Data2Image - Hide Any File Inside a PNG Image</title>
      <dc:creator>Benji377</dc:creator>
      <pubDate>Tue, 10 Mar 2026 16:22:47 +0000</pubDate>
      <link>https://forem.com/benji377/data2image-hide-any-file-inside-a-png-image-2ncm</link>
      <guid>https://forem.com/benji377/data2image-hide-any-file-inside-a-png-image-2ncm</guid>
      <description>&lt;p&gt;Have you ever needed to share a file on a platform that only accepts images? Or wanted to create a visual backup of important data? &lt;strong&gt;Data2Image&lt;/strong&gt; lets you convert any file - PDFs, ZIP archives, documents, even executables - into a PNG image that you can decode back to the exact original bytes.&lt;/p&gt;

&lt;p&gt;No steganography magic, no hidden pixels. Just straightforward data-to-image encoding with &lt;strong&gt;CRC-32 integrity checks&lt;/strong&gt;, &lt;strong&gt;compression&lt;/strong&gt;, and &lt;strong&gt;perfect lossless round-trips&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;🌐 &lt;strong&gt;&lt;a href="https://benji377.github.io/data2image/" rel="noopener noreferrer"&gt;Try it now in your browser&lt;/a&gt;&lt;/strong&gt; - no installation needed!&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Data2Image?
&lt;/h2&gt;

&lt;p&gt;Data2Image is an open-source tool that encodes arbitrary files into PNG images. Each pixel stores your data as RGBA values, creating a visual representation of your file that can be perfectly restored later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lossless round-trip&lt;/strong&gt;: decode always returns the exact original bytes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRC-32 integrity verification&lt;/strong&gt;: detects corrupted data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deflate compression&lt;/strong&gt;: minimizes image size&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filename preservation&lt;/strong&gt;: original name is embedded in the image&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform&lt;/strong&gt;: works in browsers, Node.js, Deno, and Bun&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The encoded images use the &lt;code&gt;.d2i.png&lt;/code&gt; extension, so they're still viewable as regular PNGs, they just look like colorful static!&lt;/p&gt;

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

&lt;p&gt;The encoding process is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Compress&lt;/strong&gt; your file data with deflate (same as ZIP)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frame it&lt;/strong&gt; with metadata: magic bytes, file size, CRC-32 checksum, and filename&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rasterize&lt;/strong&gt; the bytes as RGBA pixels in a square PNG image&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decode&lt;/strong&gt; reverses the process and verifies the checksum
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[4B magic "D2I\x01"]
[4B payload length]
[4B CRC-32 checksum]
[2B filename length]
[N filename (UTF-8)]
[... compressed data]
[... zero padding to fill square]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The format is intentionally straightforward: no encryption, no obfuscation. If you want security, encrypt your file first, &lt;em&gt;then&lt;/em&gt; encode it to an image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Web App
&lt;/h2&gt;

&lt;p&gt;The easiest way to try Data2Image is the &lt;strong&gt;&lt;a href="https://benji377.github.io/data2image/" rel="noopener noreferrer"&gt;web interface&lt;/a&gt;&lt;/strong&gt;, no installation, no sign-up, everything runs locally in your browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Encoding Files
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Drop files&lt;/strong&gt; onto the drop zone (or click to browse)&lt;/li&gt;
&lt;li&gt;The app auto-detects: regular files → encode, &lt;code&gt;.d2i.png&lt;/code&gt; → decode&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"Process All"&lt;/strong&gt; to encode/decode in bulk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Download individual results&lt;/strong&gt; or click &lt;strong&gt;"Download All (ZIP)"&lt;/strong&gt; for bulk download&lt;/li&gt;
&lt;/ol&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%2F50wcxhscjs0w8ox35nyt.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%2F50wcxhscjs0w8ox35nyt.png" alt="Screenshot of encoding multiple files" width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each encoded file gets a thumbnail preview, yes, your PDF really does look like colorful noise!&lt;/p&gt;

&lt;h3&gt;
  
  
  Decoding Files
&lt;/h3&gt;

&lt;p&gt;Decoding is just as easy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Drop &lt;code&gt;.d2i.png&lt;/code&gt; images into the web app&lt;/li&gt;
&lt;li&gt;The app automatically detects they're encoded files and switches to decode mode&lt;/li&gt;
&lt;li&gt;Process them, and you get back your original files with their original names&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the CRC-32 check fails (corrupted data), you'll see an error instead of a broken file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the CLI
&lt;/h2&gt;

&lt;p&gt;For automation and bulk operations, install the CLI tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; data2image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Encode files
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Single file&lt;/span&gt;
data2image encode document.pdf
&lt;span class="c"&gt;# → document.pdf.d2i.png&lt;/span&gt;

&lt;span class="c"&gt;# Bulk encode with glob&lt;/span&gt;
data2image encode &lt;span class="k"&gt;*&lt;/span&gt;.jpg &lt;span class="k"&gt;*&lt;/span&gt;.pdf &lt;span class="nt"&gt;-o&lt;/span&gt; output/

&lt;span class="c"&gt;# Pipe to stdout&lt;/span&gt;
data2image encode secret.txt &lt;span class="nt"&gt;--stdout&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; out.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Decode files
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Single file&lt;/span&gt;
data2image decode image.d2i.png
&lt;span class="c"&gt;# → original_filename.ext&lt;/span&gt;

&lt;span class="c"&gt;# Bulk decode&lt;/span&gt;
data2image decode &lt;span class="k"&gt;*&lt;/span&gt;.d2i.png &lt;span class="nt"&gt;-o&lt;/span&gt; restored/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI shows progress indicators for bulk operations, making it perfect for scripting and CI pipelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Library
&lt;/h2&gt;

&lt;p&gt;Want to integrate Data2Image into your own project? The core library is MIT licensed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @data2image/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Node.js Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;decode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@data2image/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;writeFileSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Encode&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;document.pdf&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;pngBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;document.pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;document.pdf.d2i.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pngBytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Decode&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pngData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;document.pdf.d2i.png&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pngData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "document.pdf"&lt;/span&gt;
&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Browser Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;decode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@data2image/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Handle file upload&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;encodeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&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;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&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;pngBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Create download&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pngBytes&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/png&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ... trigger download&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The library is fully typed with TypeScript and works across all modern JavaScript runtimes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Where is this useful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📤 &lt;strong&gt;Platform restrictions&lt;/strong&gt;: share files on image-only platforms&lt;/li&gt;
&lt;li&gt;💾 &lt;strong&gt;Visual backups&lt;/strong&gt;: create PNG archives of important documents&lt;/li&gt;
&lt;li&gt;🎨 &lt;strong&gt;Data art&lt;/strong&gt;: create visual representations of file data&lt;/li&gt;
&lt;li&gt;🧪 &lt;strong&gt;Security research&lt;/strong&gt;: test data exfiltration vectors&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Archival&lt;/strong&gt;: store data in formats accepted by archival image repositories&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; for type safety and great DX&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tsup&lt;/strong&gt; for dual ESM + CJS builds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pako&lt;/strong&gt; for deflate compression&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;upng-js&lt;/strong&gt; for PNG encoding/decoding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vite&lt;/strong&gt; for the blazing-fast web interface&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vitest&lt;/strong&gt; for comprehensive testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The entire project is a monorepo with three packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@data2image/core&lt;/code&gt; (MIT): the encoder/decoder library&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data2image&lt;/code&gt; (GPLv3): the CLI tool&lt;/li&gt;
&lt;li&gt;Website (GPLv3): the drag-and-drop web app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We use a &lt;strong&gt;hybrid license model&lt;/strong&gt;: the core library is permissively licensed (MIT) so you can use it anywhere, while the CLI and website are copyleft (GPLv3) to keep those tools open source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;Data2Image is fully open source! We welcome contributions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🐛 &lt;strong&gt;Bug reports&lt;/strong&gt;: found a corrupted file? Let us know&lt;/li&gt;
&lt;li&gt;✨ &lt;strong&gt;Feature requests&lt;/strong&gt;: encryption? Different formats? Tell us!&lt;/li&gt;
&lt;li&gt;🔧 &lt;strong&gt;Pull requests&lt;/strong&gt;: code improvements are always welcome&lt;/li&gt;
&lt;li&gt;📖 &lt;strong&gt;Documentation&lt;/strong&gt;: help make the docs better&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the &lt;strong&gt;&lt;a href="https://github.com/Benji377/data2image/blob/main/CONTRIBUTING.md" rel="noopener noreferrer"&gt;Contributing Guide&lt;/a&gt;&lt;/strong&gt; to get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Now!
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌐 &lt;strong&gt;Web App:&lt;/strong&gt; &lt;a href="https://benji377.github.io/data2image/" rel="noopener noreferrer"&gt;https://benji377.github.io/data2image/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;npm (Library):&lt;/strong&gt; &lt;code&gt;npm install @data2image/core&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;CLI:&lt;/strong&gt; &lt;code&gt;npm install -g data2image&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;💻 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Benji377/data2image" rel="noopener noreferrer"&gt;https://github.com/Benji377/data2image&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Visualize Your WiFi Stability in the Linux Terminal with Wavemon</title>
      <dc:creator>Benji377</dc:creator>
      <pubDate>Wed, 11 Feb 2026 16:15:24 +0000</pubDate>
      <link>https://forem.com/benji377/visualize-your-wifi-stability-in-the-linux-terminal-with-wavemon-44hi</link>
      <guid>https://forem.com/benji377/visualize-your-wifi-stability-in-the-linux-terminal-with-wavemon-44hi</guid>
      <description>&lt;p&gt;I recently discovered a CLI gem called &lt;a href="https://github.com/uoaerg/wavemon" rel="noopener noreferrer"&gt;&lt;strong&gt;Wavemon&lt;/strong&gt;&lt;/a&gt;, and if you are running Linux on a laptop or a device with a wireless card, you need to check this out.&lt;/p&gt;

&lt;p&gt;We've all been there: the internet feels "slow," but you aren't sure if it's your ISP, your router, or just bad signal reception in the corner of the room. &lt;code&gt;wavemon&lt;/code&gt; is an ncurses-based monitor that gives you a live, dashboard-style view of your wireless hardware right in the terminal.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠️ How to Install
&lt;/h3&gt;

&lt;p&gt;It is available in most standard repositories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debian/Ubuntu/Kali:&lt;/strong&gt;&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;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;wavemon

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Arch Linux:&lt;/strong&gt;&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;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; wavemon

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fedora:&lt;/strong&gt;&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;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;wavemon

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

&lt;/div&gt;



&lt;p&gt;To run it, simply type:&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;sudo &lt;/span&gt;wavemon

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

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Note: &lt;code&gt;sudo&lt;/code&gt; is often required to see the scan data and sensitive details).&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧐 Understanding the Dashboard
&lt;/h3&gt;

&lt;p&gt;When you launch it, you are greeted with the &lt;strong&gt;F1 (Info)&lt;/strong&gt; screen. It looks like a retro sci-fi dashboard, but every number tells a story. Here is how to read the most important parts:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Signal Level (The big bar)
&lt;/h4&gt;

&lt;p&gt;This is the most critical metric. It is measured in &lt;strong&gt;dBm&lt;/strong&gt; (decibel-milliwatts).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The confusing part:&lt;/strong&gt; These numbers are negative. The closer to &lt;strong&gt;0&lt;/strong&gt;, the better.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Scale:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;-30 to -50 dBm:&lt;/strong&gt; Excellent signal. You are likely very close to the router.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;-50 to -67 dBm:&lt;/strong&gt; Good signal. Perfect for streaming and gaming.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;-67 to -70 dBm:&lt;/strong&gt; Okay. Web browsing is fine, but you might notice lag in video calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;-80 dBm or lower:&lt;/strong&gt; Bad zone. Frequent disconnects and packet loss.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. RX vs. TX Rates
&lt;/h4&gt;

&lt;p&gt;You will see lines for &lt;strong&gt;rx rate&lt;/strong&gt; and &lt;strong&gt;tx rate&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RX (Receive):&lt;/strong&gt; How fast your computer can &lt;em&gt;download&lt;/em&gt; data from the router.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TX (Transmit):&lt;/strong&gt; How fast your computer can &lt;em&gt;upload&lt;/em&gt; data to the router.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; These are "link speeds," not your internet speed. If your RX rate is &lt;strong&gt;780 Mbit/s&lt;/strong&gt; for example, that is the theoretical speed between you and the router. If you only pay your ISP for 100 Mbit/s internet, you will still only get 100 Mbit/s, but your local file transfers (like moving files to a NAS) will be blazing fast.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Link Quality
&lt;/h4&gt;

&lt;p&gt;This is usually a percentage or a fraction (e.g., &lt;code&gt;57/70&lt;/code&gt;). It calculates the signal-to-noise ratio. If this drops below 40%, you are likely experiencing interference from neighbors or microwaves.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. The "Retry" Count
&lt;/h4&gt;

&lt;p&gt;Look for the line that says &lt;code&gt;retries&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Low number:&lt;/strong&gt; Good.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High number (rapidly increasing):&lt;/strong&gt; This means your WiFi card is shouting data at the router, but the router isn't hearing it, so it has to shout again. This usually happens in crowded WiFi areas (apartment buildings) or when you are too far away.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  💡 Pro Tips
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;F2 (Level Histogram):&lt;/strong&gt; Press F2 to see a moving graph of your signal. Walk around your house with your laptop open to this screen. You can literally &lt;em&gt;see&lt;/em&gt; the signal drop when you walk behind a thick concrete wall or a refrigerator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;F3 (Scan):&lt;/strong&gt; Shows all other WiFi networks nearby. Great for seeing if your neighbors are clogging up your specific WiFi channel.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;If you are debugging network issues or just want to feel like a hacker while checking your WiFi strength, &lt;code&gt;wavemon&lt;/code&gt; is a must-have in your Linux toolkit.&lt;/p&gt;

&lt;p&gt;Happy networking! 🐧&lt;/p&gt;

</description>
      <category>linux</category>
      <category>networking</category>
      <category>cli</category>
      <category>productivity</category>
    </item>
    <item>
      <title>8 Hidden Linux CLI Gems</title>
      <dc:creator>Benji377</dc:creator>
      <pubDate>Wed, 14 Jan 2026 17:07:05 +0000</pubDate>
      <link>https://forem.com/benji377/8-hidden-linux-cli-gems-4g1e</link>
      <guid>https://forem.com/benji377/8-hidden-linux-cli-gems-4g1e</guid>
      <description>&lt;p&gt;We all know &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, and &lt;code&gt;top&lt;/code&gt;. But digging through a typical Linux installation often reveals powerful utilities that sit unused simply because nobody told us they were there.&lt;/p&gt;

&lt;p&gt;Here is a short list of the most useful command-line tools that might already be on your system, plus a few modern "superpowers" you should definitely add.&lt;/p&gt;




&lt;h2&gt;
  
  
  💎 The Hidden Gems
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. aria2 - The Ultra-Fast Downloader
&lt;/h3&gt;

&lt;p&gt;Most people use &lt;code&gt;wget&lt;/code&gt; or &lt;code&gt;curl&lt;/code&gt;, but &lt;code&gt;aria2&lt;/code&gt; is a lightweight multiprotocol &amp;amp; multi-source download utility. It can split files into pieces and download them in parallel from multiple sources (HTTP, FTP, BitTorrent) simultaneously, maximizing your bandwidth.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/aria2/aria2" rel="noopener noreferrer"&gt;aria2 on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Usage:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Download a file using 4 parallel connections&lt;/span&gt;
aria2c &lt;span class="nt"&gt;-x4&lt;/span&gt; http://example.com/large-iso-file.iso
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. btop - The TUI System Monitor
&lt;/h3&gt;

&lt;p&gt;If you are still using &lt;code&gt;top&lt;/code&gt; or even &lt;code&gt;htop&lt;/code&gt;, you are missing out. &lt;code&gt;btop&lt;/code&gt; provides a beautiful, mouse-clickable, gaming-style interface for monitoring CPU, memory, network, and processes. It features graphs, themes, and full process management.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/aristocratos/btop" rel="noopener noreferrer"&gt;btop on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Usage:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;btop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. duf - Disk Usage/Free (Better &lt;code&gt;df&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;A modern alternative to the old &lt;code&gt;df&lt;/code&gt; command. It displays your disk usage in a colorful, easy-to-read table with bar graphs, grouping devices automatically so you don't have to decipher &lt;code&gt;/dev/sda1&lt;/code&gt; vs &lt;code&gt;tmpfs&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/muesli/duf" rel="noopener noreferrer"&gt;duf on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Usage:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;duf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. tldr - Manual Pages for Humans
&lt;/h3&gt;

&lt;p&gt;Standard &lt;code&gt;man&lt;/code&gt; pages are comprehensive but often overwhelming. &lt;code&gt;tldr&lt;/code&gt; is a community-driven collection of simplified man pages that gives you just the most common practical examples.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://tldr.sh/" rel="noopener noreferrer"&gt;tldr pages&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Usage:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Forget how to use tar?&lt;/span&gt;
tldr &lt;span class="nb"&gt;tar&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. yt-dlp - The Ultimate Video Downloader
&lt;/h3&gt;

&lt;p&gt;A fork of the famous &lt;code&gt;youtube-dl&lt;/code&gt;. It is actively maintained, faster, and works on thousands of video sites (YouTube, Twitch, Vimeo, etc.). It is a powerhouse for archiving content or grabbing audio.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/yt-dlp/yt-dlp" rel="noopener noreferrer"&gt;yt-dlp on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Usage:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Download a video in the best available quality&lt;/span&gt;
yt-dlp &lt;span class="s2"&gt;"https://www.youtube.com/watch?v=..."&lt;/span&gt;

&lt;span class="c"&gt;# Extract audio only as MP3&lt;/span&gt;
yt-dlp &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="nt"&gt;--audio-format&lt;/span&gt; mp3 &lt;span class="s2"&gt;"https://www.youtube.com/watch?v=..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. pv - Pipe Viewer
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pv&lt;/code&gt; is a terminal-based tool for monitoring the progress of data through a pipeline. It allows you to see a progress bar, ETA, and speed for operations that normally show nothing (like &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;dd&lt;/code&gt;, or piping streams).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Homepage:&lt;/strong&gt; &lt;a href="http://www.ivarch.com/programs/pv.shtml" rel="noopener noreferrer"&gt;ivarch.com/programs/pv&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Usage:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a progress bar for a file copy&lt;/span&gt;
pv largefile.iso &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /backup/largefile.iso
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. plocate - Instant File Search
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;plocate&lt;/code&gt; is a much faster alternative to &lt;code&gt;mlocate&lt;/code&gt;. It creates an index of your filesystem, allowing you to find any file on your drive instantly—far faster than using &lt;code&gt;find&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Homepage:&lt;/strong&gt; &lt;a href="https://plocate.sesse.net/" rel="noopener noreferrer"&gt;plocate.sesse.net&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Usage:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Update the database (usually runs automatically)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;updatedb

&lt;span class="c"&gt;# Find any file containing "config" in the name&lt;/span&gt;
locate config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  👾 The "Weird" Bonus
&lt;/h2&gt;

&lt;h3&gt;
  
  
  8. aplay (ALSA Utils) - Listen to your Data
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;aplay&lt;/code&gt; is standard on almost every Linux system (it's part of &lt;code&gt;alsa-utils&lt;/code&gt;). While intended for audio files, it has a famous trick: it can play &lt;strong&gt;any&lt;/strong&gt; file as raw PCM audio. This allows you to "hear" the structure of compiled code, images, or even your kernel.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Homepage:&lt;/strong&gt; &lt;a href="https://www.alsa-project.org/wiki/Main_Page" rel="noopener noreferrer"&gt;alsa-project.org&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Usage:
&lt;/h4&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Warning: Lower your volume first! This produces loud static.&lt;/strong&gt;&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;# Play a random ISO or binary file as CD-quality audio&lt;/span&gt;
aplay &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/any/file.iso

&lt;span class="c"&gt;# Or listen to your mouse movements (if you have access)&lt;/span&gt;
&lt;span class="nb"&gt;sudo cat&lt;/span&gt; /dev/input/mice | aplay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚀 Additional tools
&lt;/h2&gt;

&lt;p&gt;These might not be installed by default, but they are the first things many power users install on a new machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  fzf (Fuzzy Finder)
&lt;/h3&gt;

&lt;p&gt;A general-purpose command-line fuzzy finder. It lets you search your command history, files, or processes by typing partial, fuzzy queries.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install:&lt;/strong&gt; &lt;code&gt;sudo apt install fzf&lt;/code&gt; (Debian/Ubuntu)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/junegunn/fzf" rel="noopener noreferrer"&gt;junegunn/fzf&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ripgrep (rg)
&lt;/h3&gt;

&lt;p&gt;A line-oriented search tool that recursively searches the current directory for a regex pattern. It is faster than &lt;code&gt;grep&lt;/code&gt; and automatically ignores files in &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install:&lt;/strong&gt; &lt;code&gt;sudo apt install ripgrep&lt;/code&gt; (Debian/Ubuntu)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/BurntSushi/ripgrep" rel="noopener noreferrer"&gt;BurntSushi/ripgrep&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  bat
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;cat&lt;/code&gt; clone with syntax highlighting and Git integration. It makes reading code in the terminal a pleasant experience.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install:&lt;/strong&gt; &lt;code&gt;sudo apt install bat&lt;/code&gt; (Debian/Ubuntu)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/sharkdp/bat" rel="noopener noreferrer"&gt;sharkdp/bat&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>productivity</category>
      <category>bash</category>
      <category>cli</category>
    </item>
    <item>
      <title>How to export indicators from XSOAR to Splunk</title>
      <dc:creator>Benji377</dc:creator>
      <pubDate>Thu, 31 Jul 2025 16:00:00 +0000</pubDate>
      <link>https://forem.com/benji377/how-to-export-indicators-from-xsoar-to-splunk-330k</link>
      <guid>https://forem.com/benji377/how-to-export-indicators-from-xsoar-to-splunk-330k</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Exporting LOLBAS Indicators from XSOAR to Splunk as a Threat Intelligence Feed&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This blog post walks through how to export indicators (specifically from the &lt;a href="https://lolbas-project.github.io/" rel="noopener noreferrer"&gt;LOLBAS project&lt;/a&gt;) from &lt;strong&gt;Cortex XSOAR&lt;/strong&gt; into &lt;strong&gt;Splunk&lt;/strong&gt; using the &lt;strong&gt;Generic Export Indicators Service&lt;/strong&gt; and configure Splunk to ingest the feed as a &lt;strong&gt;Threat Intel Lookup&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Part 1: Setting Up XSOAR&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Enable LOLBAS Integration&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;First, you need to configure XSOAR to ingest indicators from LOLBAS.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Settings → Integrations → Instances&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Search for the &lt;strong&gt;LOLBAS&lt;/strong&gt; integration.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add instance&lt;/strong&gt; and configure it:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Set the fetch interval.&lt;/li&gt;
&lt;li&gt;Make sure “Fetch Indicators” is enabled.&lt;/li&gt;
&lt;li&gt;Provide any other required fields depending on the version of the integration.

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Test&lt;/strong&gt; to ensure the integration is working correctly.&lt;/li&gt;
&lt;li&gt;Save and enable the instance.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This integration will regularly ingest indicators into XSOAR, usually as &lt;code&gt;Tool&lt;/code&gt;-type indicators.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Verify LOLBAS Indicators Are Ingested&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To verify that the indicators are being pulled in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Threat Intel → Indicators&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Use the search bar to filter:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   sourceInstances:"LOLBAS Feed_instance_1"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;"LOLBAS Feed_instance_1"&lt;/code&gt; with the actual instance name you configured.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can’t use this exact query in automation or exports, but it works well in the UI for validation.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3: Enable Generic Export Indicators Service&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To make the indicators available to Splunk, we’ll expose them through a download endpoint.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go back to &lt;strong&gt;Settings → Integrations → Instances&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;Generic Export Indicators Service&lt;/strong&gt; (GEIS) by Palo Alto Networks.&lt;/li&gt;
&lt;li&gt;Add a new instance.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This integration allows you to create a &lt;strong&gt;CSV or JSON export&lt;/strong&gt; of indicators matching a query — accessible via a unique URL (e.g., for &lt;code&gt;curl&lt;/code&gt; or use by Splunk).&lt;/p&gt;

&lt;h4&gt;
  
  
  Key Configuration Options:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Output Format&lt;/strong&gt;: Choose &lt;code&gt;CSV&lt;/code&gt; for Splunk compatibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export Fields&lt;/strong&gt;: Customize which fields to export (e.g., &lt;code&gt;value&lt;/code&gt;, &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;customFields.path&lt;/code&gt;, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query&lt;/strong&gt;: Here’s the tricky part:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the time of writing, &lt;strong&gt;&lt;code&gt;sourceInstances&lt;/code&gt; is not supported&lt;/strong&gt; by GEIS. Instead, use &lt;strong&gt;tags&lt;/strong&gt; to filter LOLBAS indicators.&lt;/p&gt;

&lt;p&gt;For example, if your LOLBAS indicators are tagged with &lt;code&gt;lolbas&lt;/code&gt;, use this query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  tags:lolbas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 If your indicators aren’t already tagged, consider using a post-ingestion playbook to add consistent tags automatically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can test the GEIS query by using the &lt;code&gt;!findIndicators&lt;/code&gt; command in the Playground:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;!findIndicators query="tags:lolbas" size=10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once satisfied, save the GEIS instance and note the &lt;strong&gt;URL&lt;/strong&gt; it provides — you'll use this in Splunk.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Part 2: Setting Up Splunk&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now that your indicators are available as a CSV feed from XSOAR, let’s configure Splunk to pull it in as a threat intelligence source.&lt;/p&gt;

&lt;p&gt;Splunk Enterprise Security supports polling external sources via the &lt;code&gt;threatlist://&lt;/code&gt; stanza in &lt;code&gt;inputs.conf&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Reference:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Splunk Docs → &lt;a href="https://lantern.splunk.com/Security/UCE/Guided_Insights/Threat_intelligence/Using_threat_intelligence_in_Splunk_Enterprise_Security" rel="noopener noreferrer"&gt;Using Threat Intelligence in Splunk Enterprise Security&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example &lt;code&gt;inputs.conf&lt;/code&gt; Entry for LOLBAS Feed:&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[threatlist://dti-lolbas-indicator]&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Raw indicator feed from LOLBAS&lt;/span&gt;
&lt;span class="py"&gt;fields&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;name:$1,version:$2,CustomFields:$3&lt;/span&gt;
&lt;span class="py"&gt;file_parser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;line&lt;/span&gt;
&lt;span class="py"&gt;delim_regex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;,&lt;/span&gt;
&lt;span class="py"&gt;ignore_regex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;(^#|^&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s"&gt;*$)&lt;/span&gt;
&lt;span class="py"&gt;interval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;900&lt;/span&gt;
&lt;span class="py"&gt;is_threatintel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;max_age&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;-4h&lt;/span&gt;
&lt;span class="py"&gt;max_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;52428800&lt;/span&gt;
&lt;span class="py"&gt;retries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;3&lt;/span&gt;
&lt;span class="py"&gt;retry_interval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;60&lt;/span&gt;
&lt;span class="py"&gt;skip_header_lines&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt;
&lt;span class="py"&gt;timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;30&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;dti-indicator&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;https://&amp;lt;xsoar-url&amp;gt;/instance/execute/&amp;lt;geis-instance-name&amp;gt;&lt;/span&gt;
&lt;span class="py"&gt;workloads&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Field Notes:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fields&lt;/code&gt;: Maps CSV columns to Splunk fields.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;url&lt;/code&gt;: Replace with the actual &lt;strong&gt;GEIS URL&lt;/strong&gt; from your XSOAR instance.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;interval&lt;/code&gt;: Poll every 15 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;is_threatintel&lt;/code&gt;: Set to &lt;code&gt;true&lt;/code&gt; if you want it integrated into Splunk ES Threat Intelligence.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ Don’t forget to restart Splunk after modifying &lt;code&gt;inputs.conf&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Optional: Add a Workload (Saved Search)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you want to process the incoming data, such as filtering invalid entries or transforming fields, you can specify a &lt;code&gt;workload&lt;/code&gt;, like a &lt;strong&gt;Saved Search&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;workloads&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;[&lt;/span&gt;
    &lt;span class="err"&gt;{"type":&lt;/span&gt; &lt;span class="err"&gt;"savedsearch",&lt;/span&gt; &lt;span class="err"&gt;"workload":&lt;/span&gt; &lt;span class="err"&gt;"cleanup-lolbas-indicators"}&lt;/span&gt;
&lt;span class="err"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets you customize how LOLBAS data is cleaned or enriched after ingestion.&lt;/p&gt;

</description>
      <category>security</category>
      <category>splunk</category>
      <category>xsoar</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Build a Portable Offline Chat Server with a Raspberry Pi</title>
      <dc:creator>Benji377</dc:creator>
      <pubDate>Tue, 17 Jun 2025 19:55:45 +0000</pubDate>
      <link>https://forem.com/benji377/how-to-build-a-portable-offline-chat-server-with-a-raspberry-pi-156i</link>
      <guid>https://forem.com/benji377/how-to-build-a-portable-offline-chat-server-with-a-raspberry-pi-156i</guid>
      <description>&lt;p&gt;Looking for a fun way to create a &lt;strong&gt;local chat network&lt;/strong&gt; that works &lt;em&gt;without internet&lt;/em&gt;, &lt;em&gt;without installing any apps&lt;/em&gt;, and &lt;em&gt;without any complex setup&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;This guide will show you how to turn a Raspberry Pi into a portable IRC chat server. It hosts its own WiFi network, shows a captive portal, and lets users chat — all offline. No custom software involved — just a combination of existing tools configured to work together.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Is (and Isn’t)
&lt;/h2&gt;

&lt;p&gt;This isn’t a new app or a programming project — it's a collection of freely available software and tutorials that, when set up properly, gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A private WiFi access point&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;captive portal&lt;/strong&gt; (like those you see in cafés or airports)&lt;/li&gt;
&lt;li&gt;An IRC server for chatting&lt;/li&gt;
&lt;li&gt;An IRC client, accessible via browser or app&lt;/li&gt;
&lt;li&gt;All hosted entirely on a Raspberry Pi — no downloads or internet needed&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What You’ll Need
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hardware
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Raspberry Pi 3B+ (or similar)&lt;/li&gt;
&lt;li&gt;MicroSD card (16GB+ recommended)&lt;/li&gt;
&lt;li&gt;Power supply&lt;/li&gt;
&lt;li&gt;Ethernet cable (for setup phase)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Software Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Wireless AP:&lt;/strong&gt; &lt;a href="https://raspap.com/" rel="noopener noreferrer"&gt;RaspAP&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Captive Portal:&lt;/strong&gt; &lt;a href="https://github.com/nodogsplash/nodogsplash" rel="noopener noreferrer"&gt;Nodogsplash&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IRC Server:&lt;/strong&gt; &lt;a href="https://hybridirc.com/" rel="noopener noreferrer"&gt;Hybrid IRC&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web IRC Client:&lt;/strong&gt; &lt;a href="https://thelounge.chat/" rel="noopener noreferrer"&gt;The Lounge&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[Optional] Admin Panel:&lt;/strong&gt; &lt;a href="https://webmin.com/" rel="noopener noreferrer"&gt;Webmin&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Setup Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Set a Static IP on the Raspberry Pi
&lt;/h3&gt;

&lt;p&gt;This is important for routing and for consistent access to your chat server (we’ll use &lt;code&gt;10.3.141.1&lt;/code&gt; as an example).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pimylifeup.com/raspberry-pi-static-ip-address/" rel="noopener noreferrer"&gt;Static IP Tutorial&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Turn the Pi Into a Wireless Access Point
&lt;/h3&gt;

&lt;p&gt;Using &lt;a href="https://raspap.com/" rel="noopener noreferrer"&gt;RaspAP&lt;/a&gt;, you can easily broadcast your own WiFi network from the Pi.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pimylifeup.com/raspberry-pi-wireless-access-point/" rel="noopener noreferrer"&gt;Wireless AP Guide&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Connect Internet for Setup
&lt;/h3&gt;

&lt;p&gt;You can connect your Pi to your PC via Ethernet and share internet access temporarily. This helps when downloading packages.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Install and Configure Nodogsplash
&lt;/h3&gt;

&lt;p&gt;Nodogsplash is used to serve the captive portal page — the one users see when they connect to your network. It explains how to connect to the chat.&lt;/p&gt;

&lt;p&gt;Follow the specific RaspAP integration guide:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.raspap.com/captive/#configuration-changes" rel="noopener noreferrer"&gt;Nodogsplash with RaspAP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use this custom HTML page for your captive portal:&lt;br&gt;
&lt;a href="https://gist.github.com/Benji377/6b9ef7ab1dbd614c7ac2f2dc28cd3013" rel="noopener noreferrer"&gt;Captive Portal Template (GitHub Gist)&lt;/a&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  5. Install and Configure the IRC Server
&lt;/h3&gt;

&lt;p&gt;Install &lt;a href="https://hybridirc.com/" rel="noopener noreferrer"&gt;Hybrid IRC&lt;/a&gt; on the Pi and make sure it's listening on the correct interface (e.g., &lt;code&gt;10.3.141.1&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pimylifeup.com/raspberry-pi-irc-server/" rel="noopener noreferrer"&gt;IRC Server Setup Tutorial →&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can test your server locally using an IRC client on your computer, like &lt;a href="https://www.adiirc.com/" rel="noopener noreferrer"&gt;AdiIRC&lt;/a&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  6. Install a Web IRC Client (Optional)
&lt;/h3&gt;

&lt;p&gt;You can also install &lt;a href="https://thelounge.chat/" rel="noopener noreferrer"&gt;The Lounge&lt;/a&gt; on the Pi. It allows users to chat directly in the browser instead of using a separate IRC app.&lt;/p&gt;


&lt;h3&gt;
  
  
  7. Unplug the Ethernet Cable and Go Offline
&lt;/h3&gt;

&lt;p&gt;Once everything is configured, you can unplug the Pi and use it as a completely offline system. Users connect via WiFi, see the captive portal, and are guided into the chat system.&lt;/p&gt;


&lt;h2&gt;
  
  
  DNSMasq Offline Mode (If Needed)
&lt;/h2&gt;

&lt;p&gt;If you're running into issues with DNS resolution offline, try adding this line to your &lt;code&gt;dnsmasq.conf&lt;/code&gt;:&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;# /etc/dnsmasq.conf or /etc/dnsmasq.d/custom.conf&lt;/span&gt;
&lt;span class="nv"&gt;address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/#/10.3.141.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the file if it doesn't exist.&lt;/p&gt;




&lt;h2&gt;
  
  
  Captive Portal Instructions (What Users See)
&lt;/h2&gt;

&lt;p&gt;When users connect to the WiFi network, they’ll be redirected to a captive portal that explains how to join the local chat. Here's an example of the message you can include:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Welcome to the Local Chat Server&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You’re connected to a local IRC chat network hosted entirely on a Raspberry Pi.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No downloads needed.&lt;/strong&gt; Just follow the steps below:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click the button below to proceed. It may open an admin page — just close it if it appears.&lt;/li&gt;
&lt;li&gt;You’ll be redirected to a browser-based IRC client where you can start chatting instantly.&lt;/li&gt;
&lt;li&gt;To join a chat room, type: &lt;code&gt;/join #general&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You’re now chatting with others on the same WiFi!&lt;/li&gt;
&lt;/ol&gt;



&lt;p&gt;&lt;strong&gt;Advanced Option:&lt;/strong&gt;&lt;br&gt;
If you prefer to use a dedicated IRC client (on your phone or computer), you can manually connect using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server Address:&lt;/strong&gt; &lt;code&gt;10.3.141.1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port:&lt;/strong&gt; &lt;code&gt;6667&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; &lt;em&gt;Not required&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recommended clients:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Windows:&lt;/strong&gt; &lt;a href="https://www.adiirc.com/" rel="noopener noreferrer"&gt;AdiIRC&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Android:&lt;/strong&gt; CoreIRCLite&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This setup is a fun way to explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Raspberry Pi networking&lt;/li&gt;
&lt;li&gt;Offline-first apps&lt;/li&gt;
&lt;li&gt;IRC&lt;/li&gt;
&lt;li&gt;Captive portals&lt;/li&gt;
&lt;li&gt;WiFi provisioning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All without needing to write any code or install anything on user devices.&lt;/p&gt;

&lt;p&gt;Whether you're using this for LAN parties, workshops, classrooms, or just experimenting — it’s a great DIY project that repurposes old tools in a useful way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pimylifeup.com/" rel="noopener noreferrer"&gt;pimylifeup.com&lt;/a&gt; – for most of the tutorials referenced in this project&lt;/li&gt;
&lt;li&gt;Banner image by &lt;a href="https://unsplash.com/@joker2000?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Yannick Pipke&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/green-and-black-computer-ram-stick-GtcA8mw0t1U?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tutorial</category>
      <category>raspberrypi</category>
      <category>irc</category>
      <category>networking</category>
    </item>
    <item>
      <title>Packaging and Distributing a Rust CLI for All Platforms - A Case Study with Tooka</title>
      <dc:creator>Benji377</dc:creator>
      <pubDate>Mon, 16 Jun 2025 22:45:41 +0000</pubDate>
      <link>https://forem.com/benji377/packaging-and-distributing-a-rust-cli-for-all-platforms-a-case-study-with-tooka-172</link>
      <guid>https://forem.com/benji377/packaging-and-distributing-a-rust-cli-for-all-platforms-a-case-study-with-tooka-172</guid>
      <description>&lt;p&gt;Recently, I built a small Rust CLI tool called &lt;a href="https://github.com/tooka-org/cli" rel="noopener noreferrer"&gt;Tooka&lt;/a&gt;, and I wanted to share how I set it up to compile for &lt;strong&gt;all major systems&lt;/strong&gt; — Windows, Linux, macOS — and how I handled &lt;strong&gt;packaging and distribution&lt;/strong&gt; across app stores.&lt;/p&gt;

&lt;p&gt;This post covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Actions setup&lt;/li&gt;
&lt;li&gt;Cross-compilation with Rust&lt;/li&gt;
&lt;li&gt;Packaging with &lt;a href="https://crates.io/crates/cargo-packager" rel="noopener noreferrer"&gt;&lt;code&gt;cargo-packager&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Distributing via Snapcraft, AUR, Winget, Docker, Cloudsmith, etc.&lt;/li&gt;
&lt;li&gt;Real-world pitfalls (like GLIBC incompatibility)&lt;/li&gt;
&lt;li&gt;And a full example &lt;code&gt;Dockerfile&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Introducing Tooka
&lt;/h2&gt;

&lt;p&gt;Tooka is a tiny CLI that sorts files using &lt;strong&gt;YAML-based rules&lt;/strong&gt;. It doesn’t use a GUI or any external dependencies — a design choice that makes packaging much easier across platforms.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Side note:&lt;/em&gt; Naming it &lt;em&gt;Tooka&lt;/em&gt; turned out to be... suboptimal. There are way too many projects with similar names. But hey, too late to go back now. 😅&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/tooka-org/cli" rel="noopener noreferrer"&gt;https://github.com/tooka-org/cli&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  GitHub Setup
&lt;/h2&gt;

&lt;p&gt;I host Tooka on GitHub with some basic files to kick things off:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LICENSE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CODE_OF_CONDUCT.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;renovate.json&lt;/code&gt; — for dependency management via &lt;a href="https://docs.renovatebot.com" rel="noopener noreferrer"&gt;RenovateBot&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're familiar with Dependabot, Renovate is a great alternative — and &lt;strong&gt;free for public repos&lt;/strong&gt;. Here’s my &lt;code&gt;renovate.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://docs.renovatebot.com/renovate-schema.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"config:recommended"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lockFileMaintenance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"automerge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"packageRules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matchUpdateTypes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"minor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"patch"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matchCurrentVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;= 1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"automerge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matchDepTypes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"automerge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup ensures &lt;code&gt;Cargo.lock&lt;/code&gt; stays clean and synced, and minor updates merge automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building and Packaging Releases
&lt;/h2&gt;

&lt;p&gt;I use &lt;strong&gt;GitHub Actions&lt;/strong&gt; to build Tooka for all platforms. The release process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compiles for Windows, macOS (Intel + Apple Silicon), and multiple Linux targets&lt;/li&gt;
&lt;li&gt;Packages into &lt;code&gt;.deb&lt;/code&gt;, &lt;code&gt;.rpm&lt;/code&gt;, &lt;code&gt;.dmg&lt;/code&gt;, &lt;code&gt;.msi&lt;/code&gt;, &lt;code&gt;.exe&lt;/code&gt;, and even AppImage&lt;/li&gt;
&lt;li&gt;Uses &lt;a href="https://crates.io/crates/cargo-packager" rel="noopener noreferrer"&gt;&lt;code&gt;cargo-packager&lt;/code&gt;&lt;/a&gt; for packaging&lt;/li&gt;
&lt;li&gt;Creates a &lt;strong&gt;draft release&lt;/strong&gt; with all artifacts for review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s the &lt;a href="https://github.com/tooka-org/cli/blob/main/.github/workflows/release.yml" rel="noopener noreferrer"&gt;release workflow&lt;/a&gt;, powered by a build matrix that handles platform-specific logic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Bonus: I also use &lt;a href="https://crates.io/crates/cargo-generate-rpm" rel="noopener noreferrer"&gt;&lt;code&gt;cargo-generate-rpm&lt;/code&gt;&lt;/a&gt; since RPM isn’t yet supported by &lt;code&gt;cargo-packager&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  GLIBC Pitfall on Linux
&lt;/h3&gt;

&lt;p&gt;One real-world problem I hit: &lt;strong&gt;GLIBC incompatibilities&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you compile on a newer distro (like Ubuntu 22.04), and your user runs it on something older (like Debian 10), your app might crash with errors like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version `GLIBC_2.32' not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To discover and debug this, I created a simple Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; debian:bookworm-slim&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    curl wget jq ca-certificates &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get clean &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;LATEST_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.github.com/repos/tooka-org/cli/releases/latest | &lt;span class="se"&gt;\
&lt;/span&gt;    jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.assets[] | select(.name | endswith("_amd64.deb")) | .browser_download_url'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    wget &lt;span class="nt"&gt;-O&lt;/span&gt; tooka.deb &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LATEST_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; tooka.deb &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm &lt;/span&gt;tooka.deb

&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;-ms&lt;/span&gt; /bin/bash tooka
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; tooka&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /home/tooka/workspace&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/bin/bash"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This container mimics a minimal Linux user environment and helped uncover runtime issues early.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; Consider building with &lt;code&gt;musl&lt;/code&gt; instead of &lt;code&gt;glibc&lt;/code&gt; for max compatibility.&lt;/p&gt;




&lt;h2&gt;
  
  
  Distributing to App Stores
&lt;/h2&gt;

&lt;p&gt;Distributing CLI tools to users across platforms is... complicated. But it’s doable.&lt;/p&gt;

&lt;p&gt;Here’s where I published Tooka:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Snap Store&lt;/li&gt;
&lt;li&gt;Winget (Windows)&lt;/li&gt;
&lt;li&gt;AppImageHub&lt;/li&gt;
&lt;li&gt;AUR (&lt;code&gt;tooka-git&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Cloudsmith (for APT/RPM repos)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Snapcraft
&lt;/h3&gt;

&lt;p&gt;Setting up Snapcraft is tricky at first — especially if you’re not on Debian/Ubuntu. But once it’s live, you can automate builds via GitHub and maintain it with a &lt;code&gt;snap/&lt;/code&gt; folder.&lt;/p&gt;

&lt;h3&gt;
  
  
  AUR
&lt;/h3&gt;

&lt;p&gt;I created a &lt;code&gt;-git&lt;/code&gt; package that pulls from the latest commit or release. AUR is great for Arch users but requires manual updates for non-&lt;code&gt;-git&lt;/code&gt; versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  AppImage
&lt;/h3&gt;

&lt;p&gt;AppImage is surprisingly easy with &lt;code&gt;cargo-packager&lt;/code&gt;. Just pass the format and it’ll generate the artifact. Hosting is manual (I uploaded mine to GitHub releases). And you can then also publish it to the &lt;a href="https://github.com/AppImage/appimage.github.io" rel="noopener noreferrer"&gt;AppImage store&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Winget
&lt;/h3&gt;

&lt;p&gt;Winget setup requires a Windows box, but once configured, it’s mostly maintenance-free. I also added a &lt;a href="https://github.com/tooka-org/cli/blob/main/.github/workflows/winget.yaml" rel="noopener noreferrer"&gt;GitHub Action&lt;/a&gt; to push new versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  APT/RPM with Cloudsmith
&lt;/h3&gt;

&lt;p&gt;Want &lt;code&gt;sudo apt install tooka&lt;/code&gt;? You'll need to host your own APT repo. I used &lt;a href="https://cloudsmith.com" rel="noopener noreferrer"&gt;Cloudsmith&lt;/a&gt; — it’s free for small projects and super easy to automate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tooka-org/cli/blob/main/.github/workflows/cloudsmith.yml" rel="noopener noreferrer"&gt;Here’s my GitHub Action&lt;/a&gt; that uploads &lt;code&gt;.deb&lt;/code&gt; and &lt;code&gt;.rpm&lt;/code&gt; files to Cloudsmith on every release.&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker Support
&lt;/h2&gt;

&lt;p&gt;Tooka also has a &lt;a href="https://github.com/tooka-org/cli/blob/main/Dockerfile" rel="noopener noreferrer"&gt;Dockerfile&lt;/a&gt; for users who want to run it in a container without installing it locally.&lt;/p&gt;

&lt;p&gt;This can also be used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sandboxed usage&lt;/li&gt;
&lt;li&gt;Reproducible testing&lt;/li&gt;
&lt;li&gt;Musl-based builds&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Other Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No support for Homebrew or Nix yet — still figuring those out. PRs welcome!&lt;/li&gt;
&lt;li&gt;I built a minimal download page: &lt;a href="https://tooka.deno.dev/download" rel="noopener noreferrer"&gt;https://tooka.deno.dev/download&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Feedback Wanted
&lt;/h2&gt;

&lt;p&gt;I’m still learning Rust, packaging, and everything in between. Tooka is just a side project, but I hope others can learn from it, fork it, or even improve on it.&lt;/p&gt;

&lt;p&gt;If you have ideas, tips, or questions — I’d &lt;strong&gt;really love your feedback&lt;/strong&gt;! Feel free to open issues, submit PRs, or leave a comment here.&lt;/p&gt;

&lt;p&gt;Thanks for reading! ❤️&lt;/p&gt;

</description>
      <category>rust</category>
      <category>cli</category>
      <category>devops</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>A Guide to Deleting Your Data from Unused Websites</title>
      <dc:creator>Benji377</dc:creator>
      <pubDate>Tue, 08 Apr 2025 16:35:30 +0000</pubDate>
      <link>https://forem.com/benji377/a-guide-to-deleting-your-data-from-unused-websites-293d</link>
      <guid>https://forem.com/benji377/a-guide-to-deleting-your-data-from-unused-websites-293d</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Many of us have experienced this scenario at some point – creating an account for a service, giving it a try, and eventually deciding to move on. Unfortunately, we often neglect to delete these accounts, leaving our personal information lingering on websites. This oversight can lead to unpleasant surprises in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should I Delete My Data?
&lt;/h2&gt;

&lt;p&gt;Creating an account leaves a digital footprint on a service or website. Even if you decide to discontinue using it, your data remains stored, including your email, password, and other personal information. Worse yet, websites may continue using your data for spam emails and tracking purposes.&lt;/p&gt;

&lt;p&gt;Another significant concern arises when websites become neglected, exposing them to security vulnerabilities. This puts your information at risk of being stolen, leading to potential identity theft or phishing attacks. To check if your email has been compromised, websites like &lt;a href="https://haveibeenpwned.com/" rel="noopener noreferrer"&gt;Have I Been Pwned&lt;/a&gt; can help you track incidents of data breaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Can I Delete My Data?
&lt;/h2&gt;

&lt;p&gt;The optimal solution is to use a consolidated account, such as Google, for all your online activities. While this might compromise some privacy, managing a single secure account reduces your overall risk. Google also provides a guide on deleting unused services and apps, ensuring you maintain control over your account's access (&lt;a href="https://support.google.com/accounts/answer/3466521?hl=en" rel="noopener noreferrer"&gt;Google Account Deletion Guide&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;For websites without Google login options, consider using a temporary account (throwaway email and password) for experimental or one-time-use services. Alternatively, create a standard account and search for data deletion options on the website. Many platforms, especially those adhering to European laws, are mandated to offer this service.&lt;/p&gt;

&lt;p&gt;Remember to utilize the account deletion option once you're done using a website or app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While I'm not a security expert, my personal experience has taught me the importance of vigilance. Discovering that old accounts on breached websites exposed my email to unknown entities was a wake-up call. Now, I make it a practice to monitor and promptly delete accounts from websites I no longer need. Being proactive in safeguarding your digital presence is a small step that can have significant security benefits.&lt;/p&gt;

</description>
      <category>tutorial</category>
    </item>
    <item>
      <title>Tools to Automate GitHub Projects</title>
      <dc:creator>Benji377</dc:creator>
      <pubDate>Sat, 29 Mar 2025 20:04:37 +0000</pubDate>
      <link>https://forem.com/benji377/tools-to-automate-github-projects-2cbl</link>
      <guid>https://forem.com/benji377/tools-to-automate-github-projects-2cbl</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Automating GitHub projects can significantly streamline your workflow and enhance project efficiency. In this guide, we will explore a selection of free and user-friendly tools to help you fully automate your projects. Please note that setup instructions may vary based on your specific project requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools for Automation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Automatic Tests and Builds&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  GitHub Actions
&lt;/h4&gt;

&lt;p&gt;GitHub Actions is a powerful tool for automating builds and tests. By creating a YAML file with instructions, you can seamlessly execute tasks, such as cross-compiling your application for different operating systems. GitHub Actions also supports the execution of applications, allowing you to build and test your code effortlessly. More details can be found &lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Check Test Coverage&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Codecov
&lt;/h4&gt;

&lt;p&gt;Combine GitHub Action tests with Codecov to keep track of test coverage in your code. Codecov provides insights into how much of your code is covered by tests, ensuring comprehensive testing. You can find Codecov in the GitHub Marketplace, and additional information is available &lt;a href="https://docs.codecov.com/docs" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Automatic Dependency Management&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Dependency management can be challenging, but tools like Dependabot and Renovate Bot can help.&lt;/p&gt;

&lt;h4&gt;
  
  
  Dependabot
&lt;/h4&gt;

&lt;p&gt;Dependabot not only notifies you of available updates but also manages dependencies by creating branches and pull requests. It alerts you about vulnerabilities in external dependencies. Learn more &lt;a href="https://docs.github.com/en/code-security/dependabot/working-with-dependabot" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Renovate Bot
&lt;/h4&gt;

&lt;p&gt;Renovate Bot provides more control over dependency updates. It allows you to gauge the potential impact of new dependencies and easily set up Automerge for automatic pull request merging. Learn about Renovate Bot &lt;a href="https://docs.renovatebot.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Automatic Anomaly Detection&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When collaborating on code, it's crucial to detect anomalies in pull requests. GitHub provides tools to address this issue.&lt;/p&gt;

&lt;h4&gt;
  
  
  Dependabot
&lt;/h4&gt;

&lt;p&gt;In addition to managing dependencies, Dependabot can alert you to vulnerabilities in external dependencies. Be cautious about its depth in the dependency tree. More details can be found &lt;a href="https://docs.github.com/en/code-security/dependabot/working-with-dependabot" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  CodeQL
&lt;/h4&gt;

&lt;p&gt;CodeQL focuses on finding vulnerabilities within your code. It runs on each commit and pull request, providing a green check for successful analysis. Check out CodeQL &lt;a href="https://codeql.github.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;Automatic Translations Fetching&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If your project involves translations, make the process smoother with Crowdin.&lt;/p&gt;

&lt;h4&gt;
  
  
  Crowdin
&lt;/h4&gt;

&lt;p&gt;Crowdin simplifies translation management by syncing translations from GitHub to its dashboard. Translators can contribute without interacting with GitHub directly. Learn more about Crowdin &lt;a href="https://crowdin.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. &lt;strong&gt;Automatic Benchmarks&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Consider using Bencher to collect and display performance data.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bencher
&lt;/h4&gt;

&lt;p&gt;Bencher allows you to gather performance data from various sources and set performance change limits. It's a valuable tool for monitoring project performance. Explore Bencher &lt;a href="https://bencher.dev/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While these tools may take time to set up, the long-term benefits of automation are substantial. Choose tools based on your project's needs, and enjoy the time and effort saved. Providing direct feedback on changes' impact, ensuring security, and keeping translations up-to-date are essential aspects of a well-maintained project. Experiment with these tools, and discover the automation strategy that best suits your workflow.&lt;/p&gt;

</description>
      <category>github</category>
      <category>automation</category>
      <category>tooling</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Grafana Speed Monitor: Setting Up an Internet Monitor with Raspberry Pi</title>
      <dc:creator>Benji377</dc:creator>
      <pubDate>Tue, 11 Mar 2025 11:30:38 +0000</pubDate>
      <link>https://forem.com/benji377/grafana-speed-monitor-setting-up-an-internet-monitor-with-raspberry-pi-50jk</link>
      <guid>https://forem.com/benji377/grafana-speed-monitor-setting-up-an-internet-monitor-with-raspberry-pi-50jk</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Upon moving to a new place, I encountered widespread Wi-Fi complaints, leading technicians to attribute occasional outages to excessive gaming. Skeptical of this explanation, I decided to set up my own Internet monitor using a Raspberry Pi. In this guide, I'll walk you through creating your own monitor to assess and validate your internet speed, potentially debunking misleading claims from your Internet Service Provider (ISP).&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before diving into the setup, ensure you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Raspberry Pi or Linux device&lt;/li&gt;
&lt;li&gt;SSH access or direct access with monitor and keyboard&lt;/li&gt;
&lt;li&gt;Stable power supply for continuous operation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Update and Install Dependencies
&lt;/h3&gt;

&lt;p&gt;Ensure your system is up to date:&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;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install necessary packages:&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;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python3-pip git python3-venv build-tools build-essentials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Clone Repository
&lt;/h3&gt;

&lt;p&gt;Clone the internet-pi repository from geerlingguy on GitHub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/geerlingguy/internet-pi.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;internet-pi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Install Required Tools
&lt;/h3&gt;

&lt;p&gt;Install pipx for managing the Python virtual environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; pipx
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pipx ensurepath
&lt;span class="c"&gt;# Logout and log back in for changes to take effect&lt;/span&gt;
pipx &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--include-deps&lt;/span&gt; ansible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Configure Ansible
&lt;/h3&gt;

&lt;p&gt;Install Ansible Galaxy requirements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ansible-galaxy &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install Python requirements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;PyYAML cython
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Copy Configuration Files
&lt;/h3&gt;

&lt;p&gt;Make copies of the example configuration files and customize them:&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;cp &lt;/span&gt;example.config.yml config.yml
&lt;span class="nb"&gt;cp &lt;/span&gt;example.inventory.ini inventory.ini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Modify Configuration
&lt;/h3&gt;

&lt;p&gt;Edit the &lt;code&gt;config.yml&lt;/code&gt; file to tailor the monitoring settings to your needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano config.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Config:&lt;/strong&gt;&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="nn"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;# Internet monitoring configuration.&lt;/span&gt;
&lt;span class="na"&gt;monitoring_enable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;monitoring_grafana_admin_password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SuperSecretPassword"&lt;/span&gt;
&lt;span class="na"&gt;monitoring_speedtest_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10m&lt;/span&gt;
&lt;span class="na"&gt;monitoring_ping_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
&lt;span class="na"&gt;monitoring_ping_hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# [URL];[HUMAN_READABLE_NAME]&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;http://www.google.com/;google.com&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;https://github.com/;github.com&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Tip: Exit with CRTL+X, then press Y to confirm and Enter.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Modify Inventory
&lt;/h3&gt;

&lt;p&gt;Edit the &lt;code&gt;inventory.ini&lt;/code&gt; file if running the monitor on the Raspberry Pi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano inventory.ini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Inventory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[internet_pi]&lt;/span&gt;
&lt;span class="c"&gt;#10.0.100.52 ansible_user=pi
&lt;/span&gt;
&lt;span class="c"&gt;# Comment out the previous line and uncomment this to run inside Raspberry Pi.
&lt;/span&gt;&lt;span class="err"&gt;127.0.0.1&lt;/span&gt; &lt;span class="py"&gt;ansible_connection&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;local ansible_user=pi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  8. Run the Playbook
&lt;/h3&gt;

&lt;p&gt;Execute the playbook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ansible-playbook main.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If issues arise, check Docker settings with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano tasks/docker.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Connecting to Grafana
&lt;/h3&gt;

&lt;p&gt;Access the monitor by entering your Raspberry Pi's IP and port 3030 in a web browser, e.g., &lt;code&gt;http://10.0.128.45:3030&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Login Credentials
&lt;/h3&gt;

&lt;p&gt;Login with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Username: admin&lt;/li&gt;
&lt;li&gt;Password: the one set in the &lt;code&gt;config.yaml&lt;/code&gt; file during step 8&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Exporting Data
&lt;/h3&gt;

&lt;p&gt;Export historical data by clicking on the three dots in the upper right corner of any widget, selecting Inspect and Data, and choosing export options such as CSV.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;This monitor served as crucial evidence in addressing internet issues, providing real-time data reflecting the user experience. The Raspberry Pi mimics a typical user, capturing the nuances of the internet connection. Visualizing the data through Grafana is not only informative but also fascinating. Check out my dashboard after just a few days:&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%2F7yc3d5eq7uk8coo911wi.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%2F7yc3d5eq7uk8coo911wi.png" alt="Internet Speed Monitor Screenshot" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;p&gt;This guide draws heavy inspiration from Jeff Geerling and his comprehensive guide: &lt;a href="https://www.jeffgeerling.com/blog/2021/monitor-your-internet-raspberry-pi" rel="noopener noreferrer"&gt;https://www.jeffgeerling.com/blog/2021/monitor-your-internet-raspberry-pi&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>raspberrypi</category>
    </item>
  </channel>
</rss>
