<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rodrigo Fuentealba Cartes</title>
    <description>The latest articles on DEV Community by Rodrigo Fuentealba Cartes (@devrfc83).</description>
    <link>https://dev.to/devrfc83</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4204183%2F3d756c08-b660-4785-9a21-096f941cfca2.jpg</url>
      <title>DEV Community: Rodrigo Fuentealba Cartes</title>
      <link>https://dev.to/devrfc83</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devrfc83"/>
    <language>en</language>
    <item>
      <title>I Built a Hidden, In-Browser Chess Analyzer Using Stockfish &amp; Chess.js</title>
      <dc:creator>Rodrigo Fuentealba Cartes</dc:creator>
      <pubDate>Sat, 31 Oct 2026 03:17:51 +0000</pubDate>
      <link>https://dev.to/devrfc83/i-built-a-hidden-in-browser-chess-analyzer-using-stockfish-chessjs-143e</link>
      <guid>https://dev.to/devrfc83/i-built-a-hidden-in-browser-chess-analyzer-using-stockfish-chessjs-143e</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm60kcfrmepineeqny4id.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm60kcfrmepineeqny4id.png" alt="Fuentealba Cartes, Rodrigo vs Lück, Gerard - ICCF Aspirers, Group 364 - https://www.iccf.com/game?id=1406503" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;(This game is &lt;a href="https://www.iccf.com/game?id=1406503" rel="noopener noreferrer"&gt;Fuentealba Cartes, Rodrigo vs Lück, Gerard - ICCF Aspirers, Group 364&lt;/a&gt;. I won with a tactical shot a couple years ago.)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you’ve ever wanted to analyze a chess game, verify a tactics puzzle, or evaluate a position on the fly without loading a heavy platform or triggering third-party pop-ups, you know the feeling: most web-based chess engines are either locked behind paywalls or buried inside complex web apps.&lt;/p&gt;

&lt;p&gt;Instead of relying on external services, I decided to build a lightweight, fully functional chess analyzer embedded directly into my website. It's powered by Stockfish and chess.js, built for fast, client-side position evaluation and simple chess database, right in your browser.&lt;/p&gt;

&lt;p&gt;To be brutally honest, I didn't build this as a revolutionary SaaS or a productivity product. I mostly built it to drive a bit of organic traffic to my website and, let’s be real, to have a fun tech trick to show off when talking shop.&lt;/p&gt;

&lt;p&gt;Still, it works like a charm for anyone who wants to evaluate FEN strings and game positions instantly. Here is how it works under the hood, the key web architecture challenges I ran into, and how you can try it out today.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture: Engine in the Browser
&lt;/h2&gt;

&lt;p&gt;The flow is built to be fast, responsive, and 100% client-side:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;State &amp;amp; Validation:&lt;/strong&gt; &lt;code&gt;chess.js&lt;/code&gt; handles game logic, move validation, legal move generation, and FEN parsing. It's the gold standard for managing chess state, move tree, and board representation in JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI &amp;amp; Board Display:&lt;/strong&gt; An interactive board interface renders the current state and listens for user interactions or FEN input. It's also powered by chess.js, and managed via React.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async Evaluation:&lt;/strong&gt; Stockfish runs as a Web Worker in the background, receiving UCI (Universal Chess Interface) commands and streaming back real-time evaluation scores and principal variations (PV). Stockfish is the world's strongest open-source chess engine.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Two Technical Challenges I Solved
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Handling PGNs with and without Engine Analysis
&lt;/h3&gt;

&lt;p&gt;The Portable Game Notation (PGN) format has existed since 1994 as a plain-text standard for chess games, containing headers, moves, and free-form comments inside braces. Over the years, conventions like [%eval] for engine evaluations, [%clk] for clocks, and [%cal]/[%csl] for arrows and highlighted squares were added. But PGN has no dedicated space for analysis.&lt;/p&gt;

&lt;p&gt;The core architectural issue is that a single game file has two authors: the human studying or annotating it, and the machine (Stockfish) calculating evaluations and move qualities. In the file, both write to the exact same place: the move comment. An analyzed move looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;14. Nxe5 { [%eval -1.20] Me pareció que ganaba un peón. +0.3 → -1.2 ?. Error. Mejor jugada: d4. Profundidad: 18 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exporting is easy by appending the engine text, but importing is complex. When a file is re-uploaded, the system must separate human notes from machine data across eight different languages (where "Best move" might arrive as Beste Zug or Millor jugada). Instead of searching for translated keywords, the system recognizes the engine's structure by its strict formatting contract at the end of the comment.&lt;/p&gt;

&lt;p&gt;Furthermore, within the app, analysis is decoupled from plain text: it is stored separately and associated with each move using a key formed by the position before the move plus the move itself, rather than a static move number. This ensures that opening trees, promoted variations, or transposed move orders never misalign the analysis.&lt;/p&gt;

&lt;p&gt;Because standard libraries like chess.js drop variations and comments when parsing PGNs, the project implements its own dedicated PGN reader and writer—ensuring that a file read and written back out remains structurally identical.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Preventing Stale Engine Calculations on Quick Position Changes
&lt;/h3&gt;

&lt;p&gt;When a user rapidly clicks through moves or jumps across a game, Stockfish doesn’t automatically drop its current deep calculation. If left unchecked, stale evaluation messages from an old position continue to stream in asynchronously and can temporarily "flicker" or overwrite the metrics of the new position on the UI.&lt;/p&gt;

&lt;p&gt;To keep the interface snappy and accurate, the application manages a strict cancellation flow before any new evaluation begins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Whenever the user changes position or moves a piece:&lt;/span&gt;
&lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Tell Stockfish to halt the current search immediately&lt;/span&gt;

&lt;span class="c1"&gt;// Send the new position state and restart calculation cleanly&lt;/span&gt;
&lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`position fen &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newFEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;go depth 18&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Issuing the stop command guarantees that obsolete background threads clear out instantly, ensuring the UI only reacts to the exact position the user is currently looking at.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

&lt;p&gt;The analyzer is live and accessible to everyone. You can test any FEN or set up custom board positions directly:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://www.devrfc83.com/es/ajedrez/analisis" rel="noopener noreferrer"&gt;https://www.devrfc83.com/es/ajedrez/analisis&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you don't speak Spanish (my native language), you can use the Website's language selector to redirect to the English, German, Italian, French, Dutch, Catalan, or Portuguese version of the analysis board. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT&lt;/strong&gt;: I wanted to give you this FEN so you could setup the board and start analyzing right away.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1r2k1r1/pbppnp1p/1b3P2/8/Q7/B1PB1q2/P4PPP/3R2K1 w - - 0 21
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While writing this article, I learned that this tool breaks when one tries to paste a FEN. Here is a workaround though, paste this into the PGN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Variant "From Position"]
[FEN "1r2k1r1/pbppnp1p/1b3P2/8/Q7/B1PB1q2/P4PPP/3R2K1 w - - 0 21"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if you prefer, you can use the whole PGN from what's called "The Evergreen Game" between Adolf Anderssen and Jean Dufresne, circa 1852, which remains one of the most impressive games played.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Event "Casual game"]
[Site "Berlin"]
[Date "1852.??.??"]
[White "Adolf Anderssen"]
[Black "Jean Dufresne"]
[Result "1-0"]
[Opening "Evans Gambit"]

1. e4 e5 2. Nf3 Nc6 3. Bc4 Bc5 4. b4 Bxb4 5. c3 Ba5 6. d4 exd4 7. O-O d3 8. Qb3 Qf6 9. e5 Qg6 10. Re1 Nge7 11. Ba3 b5 12. Qxb5 Rb8 13. Qa4 Bb6 14. Nbd2 Bb7 15. Ne4 Qf5 16. Bxd3 Qh5 17. Nf6+ gxf6 18. exf6 Rg8 19. Rad1 Qxf3 20. Rxe7+ Nxe7 21. Qxd7+ Kxd7 22. Bf5+ Ke8 23. Bd7+ Kf8 24. Bxe7# 1-0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the position of the puzzle I showed, right from my analysis engine (yes, it can take pictures from certain positions too! I thought about exporting entire games as GIF, but I think that's too much for something that lives in your browser).&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyducxi55ptibr75jnr8d.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyducxi55ptibr75jnr8d.png" alt="White to play" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;This hidden tool is open for anyone to use. Future updates might include PGN import/export, interactive arrow overlays for best engine lines, and customizable search depths. Consider that it still requires some UI work, as I kept throwing features without pondering much about organizing the features it has.&lt;/p&gt;

&lt;p&gt;If you test it out and have feedback, feature ideas, or spot an edge case in evaluation rendering, feel free to reach out! Also, consider visiting other sections of my website, although these might not be as fun as a good old &lt;a href="https://www.devrfc83.com/en/chess/" rel="noopener noreferrer"&gt;chess game&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;Hi! I'm Rodrigo Fuentealba Cartes, an entrepreneur, Bachelor of Engineering Sciences and Computer and Informatics Engineer. For more than two decades I've combined data analysis with software engineering to take projects from idea to production. Now I offer my services through a small company named &lt;a href="https://www.seadragon.cl/" rel="noopener noreferrer"&gt;Sea Dragon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also a &lt;a href="https://www.devrfc83.com/en/chess/" rel="noopener noreferrer"&gt;chess&lt;/a&gt; &lt;a href="https://www.devrfc83.com/en/chess/analysis" rel="noopener noreferrer"&gt;player&lt;/a&gt;, musician, motorcyclist, amateur radio operator (license CA5NFS), and a low performance athlete.&lt;/p&gt;

&lt;p&gt;You can find me around the web:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://www.devrfc83.com/" rel="noopener noreferrer"&gt;https://www.devrfc83.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/devrfc83" rel="noopener noreferrer"&gt;@devrfc83&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instagram:&lt;/strong&gt; &lt;a href="https://instagram.com/devrfc83" rel="noopener noreferrer"&gt;@devrfc83&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>node</category>
    </item>
    <item>
      <title>I Built a Lightweight SMTP-to-Mailgun Bridge in Go (In Just Two Days)</title>
      <dc:creator>Rodrigo Fuentealba Cartes</dc:creator>
      <pubDate>Sat, 24 Oct 2026 08:22:15 +0000</pubDate>
      <link>https://dev.to/devrfc83/i-built-a-lightweight-smtp-to-mailgun-bridge-in-go-in-just-two-days-10n1</link>
      <guid>https://dev.to/devrfc83/i-built-a-lightweight-smtp-to-mailgun-bridge-in-go-in-just-two-days-10n1</guid>
      <description>&lt;p&gt;If you've ever tried to plug a legacy application, an old monitoring script, or an enterprise tool into a modern email delivery service like Mailgun, you've probably hit a brick wall: &lt;strong&gt;your app only speaks SMTP&lt;/strong&gt;, but many cloud providers block outbound port 25 entirely. &lt;/p&gt;

&lt;p&gt;Instead of dealing with heavy traditional mail transfer agents (MTAs) like Postfix or Exim just to relay transactional emails, I decided to build something simpler, lighter, and modern. Meet &lt;strong&gt;Mailgun Passport&lt;/strong&gt;, a minimal SMTP-to-Mailgun bridge written in Go.&lt;/p&gt;

&lt;p&gt;I coded this project from scratch in a little over two days to solve a very specific pain point. Here is how it's built, the technical hurdles I solved along the way, and why it's structured for production from day one.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture: Keeping it Simple
&lt;/h2&gt;

&lt;p&gt;The core idea is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Listen:&lt;/strong&gt; A local SMTP server accepts incoming mail (e.g., on &lt;code&gt;127.0.0.1:587&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parse:&lt;/strong&gt; It intercepts the raw bytes, parses the MIME structure, and maps SMTP envelope recipients.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forward:&lt;/strong&gt; It translates everything cleanly into a call to Mailgun’s HTTP API (&lt;code&gt;/v3/{domain}/messages&lt;/code&gt; or raw MIME).&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; Go 1.25+ (for top-tier concurrency, small binaries, and great standard libraries).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMTP Server:&lt;/strong&gt; &lt;a href="https://github.com/emersion/go-smtp" rel="noopener noreferrer"&gt;&lt;code&gt;github.com/emersion/go-smtp&lt;/code&gt;&lt;/a&gt;—the gold standard for building Go mail servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MIME Parsing:&lt;/strong&gt; &lt;a href="https://github.com/jhillyerd/enmime" rel="noopener noreferrer"&gt;&lt;code&gt;github.com/jhillyerd/enmime&lt;/code&gt;&lt;/a&gt; to cleanly extract text, HTML, headers, and attachments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging:&lt;/strong&gt; Go's built-in structured logger (&lt;code&gt;log/slog&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Two Technical Challenges I Solved
&lt;/h2&gt;

&lt;p&gt;Building an email bridge exposes you to the quirky realities of email RFCs. Here are two highlights from the codebase:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Bridging SMTP Envelopes and MIME Headers (&lt;code&gt;RCPT TO&lt;/code&gt; vs &lt;code&gt;Bcc&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;In SMTP, recipients are passed via the envelope command (&lt;code&gt;RCPT TO&lt;/code&gt;), which might not match the &lt;code&gt;To&lt;/code&gt; or &lt;code&gt;Cc&lt;/code&gt; headers inside the MIME message (especially for blind carbon copies, &lt;code&gt;Bcc&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;In &lt;code&gt;parser.go&lt;/code&gt;, the application intelligently reconciles this: if a recipient from the SMTP envelope isn't explicitly listed in the MIME headers, it treats them as a &lt;code&gt;Bcc&lt;/code&gt; automatically, ensuring no recipient gets dropped during the translation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;applyEnvelopeRecipients&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bcc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RcptTo&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// ... maps envelope RCPT TO into Bcc safely if missing from headers&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Bulletproof Systemd Hardening
&lt;/h3&gt;

&lt;p&gt;Because this service runs on Linux servers, security matters. The companion systemd service file is heavily sandboxed using modern Linux capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privilege separation:&lt;/strong&gt; Runs under a dedicated low-privilege user (&lt;code&gt;mailgun-passport&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port binding without root:&lt;/strong&gt; Uses &lt;code&gt;AmbientCapabilities=CAP_NET_BIND_SERVICE&lt;/code&gt; so it can bind to privileged port 587 without running as &lt;code&gt;root&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filesystem lockdown:&lt;/strong&gt; Enforces &lt;code&gt;ProtectSystem=strict&lt;/code&gt; and isolates temporary paths.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Getting Started &amp;amp; Testing Locally
&lt;/h2&gt;

&lt;p&gt;Clone the repository and spin it up in seconds:&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 &lt;span class="o"&gt;[&lt;/span&gt;https://github.com/devrfc83/mailgun-passport.git]&lt;span class="o"&gt;(&lt;/span&gt;https://github.com/devrfc83/mailgun-passport.git&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;mailgun-passport

&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# Edit .env and add your MAILGUN_API_KEY and MAILGUN_DOMAIN&lt;/span&gt;

go run ./cmd/mailgun-passport
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can test it locally using &lt;strong&gt;swaks&lt;/strong&gt; (the Swiss Army knife for SMTP transactions):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;swaks &lt;span class="nt"&gt;--to&lt;/span&gt; dest@example.com &lt;span class="nt"&gt;--from&lt;/span&gt; you@yourdomain.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--server&lt;/span&gt; 127.0.0.1:587 &lt;span class="nt"&gt;--port&lt;/span&gt; 587 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Subject: Hello from Passport"&lt;/span&gt; &lt;span class="nt"&gt;--body&lt;/span&gt; &lt;span class="s2"&gt;"It works!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;This is just &lt;strong&gt;v0.1.0&lt;/strong&gt;, built over a single weekend to solve a real infrastructure problem. It already supports native RPM/DEB packaging, structured logging, flexible configuration via &lt;code&gt;.env&lt;/code&gt; or systemd, and dual parsing/MIME modes.&lt;/p&gt;

&lt;p&gt;Check out the repository on GitHub: &lt;strong&gt;&lt;a href="https://github.com/devrfc83/mailgun-passport" rel="noopener noreferrer"&gt;devrfc83/mailgun-passport&lt;/a&gt;&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;If you encounter edge cases with legacy clients, find bugs, or have ideas for improvement, PRs, issues, and stars are very welcome!&lt;/p&gt;




&lt;h3&gt;
  
  
  About the Author
&lt;/h3&gt;

&lt;p&gt;Hi! I'm &lt;strong&gt;Rodrigo Fuentealba Cartes&lt;/strong&gt;, an entrepreneur, Bachelor of Engineering Sciences and Computer and Informatics Engineer. For more than two decades I've combined data analysis with software engineering to take projects from idea to production. Now I offer my services through a small company named &lt;a href="https://www.seadragon.cl/" rel="noopener noreferrer"&gt;Sea Dragon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also a &lt;a href="https://www.devrfc83.com/en/chess/" rel="noopener noreferrer"&gt;chess&lt;/a&gt; &lt;a href="https://www.devrfc83.com/en/chess/analysis/" rel="noopener noreferrer"&gt;player&lt;/a&gt;, musician, motorcyclist, amateur radio operator (license CA5NFS), and a low performance athlete.&lt;/p&gt;

&lt;p&gt;You can find me around the web:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://www.devrfc83.com/" rel="noopener noreferrer"&gt;https://www.devrfc83.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/devrfc83" rel="noopener noreferrer"&gt;@devrfc83&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instagram:&lt;/strong&gt; &lt;a href="https://instagram.com/devrfc83" rel="noopener noreferrer"&gt;@devrfc83&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>opensource</category>
      <category>devops</category>
      <category>go</category>
    </item>
  </channel>
</rss>
