<?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: Ercross</title>
    <description>The latest articles on DEV Community by Ercross (@ercross).</description>
    <link>https://dev.to/ercross</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%2F2087500%2F6f21f81f-a282-46be-9be2-8eb5f7298eda.jpeg</url>
      <title>DEV Community: Ercross</title>
      <link>https://dev.to/ercross</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ercross"/>
    <language>en</language>
    <item>
      <title>Vulnerability Report: GO-2026-6809 [Breakdown]</title>
      <dc:creator>Ercross</dc:creator>
      <pubDate>Thu, 22 Oct 2026 08:33:48 +0000</pubDate>
      <link>https://dev.to/ercross/vulnerability-report-go-2026-6809-breakdown-3o74</link>
      <guid>https://dev.to/ercross/vulnerability-report-go-2026-6809-breakdown-3o74</guid>
      <description>&lt;p&gt;𝗛𝗲𝗿𝗲'𝘀 𝘄𝗵𝗮𝘁 𝗖𝗩𝗘-𝟮𝟬𝟮𝟲-𝟭𝟭𝟭𝟵𝟰𝟯 𝗺𝗲𝗮𝗻𝘀 𝗳𝗼𝗿 𝘆𝗼𝘂𝗿 𝗚𝗼 𝘀𝗲𝗿𝘃𝗶𝗰𝗲𝘀 &lt;/p&gt;

&lt;p&gt;Go 1.27.3 and 1.26.10 shipped this week with a fix in 𝚌𝚛𝚢𝚙𝚝𝚘/𝚡𝟻𝟶𝟿. The changelog makes it sound small. It isn't, so here's the plain-English version.&lt;/p&gt;

&lt;p&gt;𝗧𝗵𝗲 𝗽𝗿𝗼𝗯𝗹𝗲𝗺, 𝘀𝗶𝗺𝗽𝗹𝘆&lt;/p&gt;

&lt;p&gt;When your Go code calls 𝚑𝚝𝚝𝚙.𝙶𝚎𝚝("𝚑𝚝𝚝𝚙𝚜://𝚊𝚙𝚒.𝚜𝚝𝚛𝚒𝚙𝚎.𝚌𝚘𝚖"), how does it know it's really talking to Stripe?&lt;/p&gt;

&lt;p&gt;The server shows a certificate, like an ID card. Your program checks that the ID was stamped by an authority it trusts (a Certificate Authority, or CA).&lt;/p&gt;

&lt;p&gt;Now imagine one of those authorities made its official stamps so badly that anyone could recreate them in seconds.&lt;/p&gt;

&lt;p&gt;That's roughly what happened with eMudhra, an Indian CA. A flaw in their key generation produced 8 intermediate CA keys with almost no randomness. One key was literally the number 4. The others were small whole numbers under 256. Researchers recovered all of them from the public certificates in under a second on a laptop.&lt;/p&gt;

&lt;p&gt;With one of those keys, an attacker can create a "valid" certificate for ANY domain: your bank, your payment provider, your own API. Your Go program would happily accept it. 😬&lt;/p&gt;

&lt;p&gt;𝗪𝗵𝗲𝗻 𝗮𝗿𝗲 𝘆𝗼𝘂 𝗮𝗰𝘁𝘂𝗮𝗹𝗹𝘆 𝗲𝘅𝗽𝗼𝘀𝗲𝗱?&lt;/p&gt;

&lt;p&gt;All of these have to be true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Your Go program makes outgoing TLS connections (HTTP clients, gRPC clients, webhooks, database drivers with TLS)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It was built with Go older than 1.26.10, or 1.27.0 to 1.27.2&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The machine or container it runs on still trusts the eMudhra roots (an old base image, a distro that hasn't shipped updated ca-certificates yet, or certs baked into a 𝚜𝚌𝚛𝚊𝚝𝚌𝚑/distroless image)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An attacker can get between your service and the server it's calling (a compromised network, DNS spoofing, a malicious proxy, shared Wi-Fi)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any one of these is false, the attack breaks. Number 4 is the hardest part for an attacker. Recovering the key is trivial.&lt;/p&gt;

&lt;p&gt;𝗪𝗵𝗮𝘁 𝘁𝗼 𝗱𝗼&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Upgrade to Go 1.27.3 or 1.26.10 and REBUILD your binaries. Go compiles the fix into your program, so updating Go on your laptop protects nothing that's already deployed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rebuild container images so they pick up fresh ca-certificates&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run 𝚐𝚘𝚟𝚞𝚕𝚗𝚌𝚑𝚎𝚌𝚔 ./... to see if you're affected&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On Go 1.25 or older? It gets no fix. This is a good reason to upgrade.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://pkg.go.dev/vuln/GO-2026-6809" rel="noopener noreferrer"&gt;https://pkg.go.dev/vuln/GO-2026-6809&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>security</category>
      <category>tls</category>
      <category>cve</category>
    </item>
    <item>
      <title>reconD v0.5.0 - Linux Packaging</title>
      <dc:creator>Ercross</dc:creator>
      <pubDate>Wed, 21 Oct 2026 12:19:52 +0000</pubDate>
      <link>https://dev.to/ercross/recond-v050-linux-packaging-23f0</link>
      <guid>https://dev.to/ercross/recond-v050-linux-packaging-23f0</guid>
      <description>&lt;p&gt;In case you missed the dev.to's introduction post to reconD, here you go&lt;br&gt;
&lt;a href="https://dev.to/ercross/recond-pull-based-deployments-for-one-server-without-a-platform-1jjb"&gt;https://dev.to/ercross/recond-pull-based-deployments-for-one-server-without-a-platform-1jjb&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  New in v0.5.0: install it like any other system service
&lt;/h2&gt;

&lt;p&gt;Until now, using reconD meant cloning the repo, building it with Go, and writing your own systemd unit. &lt;br&gt;
With the new [v0.5.0] release (&lt;a href="https://github.com/ercross/reconD/releases/tag/v0.5.0" rel="noopener noreferrer"&gt;https://github.com/ercross/reconD/releases/tag/v0.5.0&lt;/a&gt;), every release ships &lt;code&gt;.deb&lt;/code&gt; and &lt;code&gt;.rpm&lt;/code&gt; packages for amd64 and arm64.&lt;/p&gt;

&lt;p&gt;On Debian or Ubuntu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.5.0
&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;amd64   &lt;span class="c"&gt;# or arm64&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSLO&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/ercross/reconD/releases/download/v&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/recond_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.deb"&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSLO&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/ercross/reconD/releases/download/v&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/checksums.txt"&lt;/span&gt;
&lt;span class="nb"&gt;sha256sum&lt;/span&gt; &lt;span class="nt"&gt;--ignore-missing&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; checksums.txt
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"./recond_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.deb"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Fedora, RHEL-compatible, or openSUSE, it's the same with &lt;code&gt;recond-${VERSION}-1.x86_64.rpm&lt;/code&gt; and &lt;code&gt;dnf install&lt;/code&gt; or &lt;code&gt;zypper install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then:&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 cp&lt;/span&gt; /etc/recond/config.yaml.example /etc/recond/config.yaml
sudoedit /etc/recond/config.yaml
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; recond
&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; recond &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The packages behave the way you'd expect from a system service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install&lt;/strong&gt; puts the binary at &lt;code&gt;/usr/bin/recond&lt;/code&gt;, adds a systemd unit, an example config, and &lt;code&gt;/var/lib/recond&lt;/code&gt;. It doesn't start anything until you've written a config, and it prints those setup steps if you haven't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upgrade&lt;/strong&gt; restarts the service on the new binary if it was running, and leaves it stopped if it wasn't. Your config and state aren't touched.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove&lt;/strong&gt; stops and disables the service and keeps your config and state. On Debian, &lt;code&gt;apt purge&lt;/code&gt; removes those too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CI tests all of this on every pull request, including upgrading a running service from the previous release, and a release can't be published unless those tests pass.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Heads-up if you already run reconD:&lt;/strong&gt; this release is breaking. The default paths moved from &lt;code&gt;/etc/reconD&lt;/code&gt; and &lt;code&gt;/var/lib/reconD&lt;/code&gt; to lowercase &lt;code&gt;/etc/recond&lt;/code&gt; and &lt;code&gt;/var/lib/recond&lt;/code&gt;, and the binary is now &lt;code&gt;recond&lt;/code&gt;. The &lt;a href="https://github.com/ercross/reconD/blob/master/CHANGELOG.md" rel="noopener noreferrer"&gt;changelog&lt;/a&gt; has the migration steps.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>go</category>
      <category>docker</category>
      <category>opensource</category>
    </item>
    <item>
      <title>ReconD: pull-based deployments for one server, without a platform</title>
      <dc:creator>Ercross</dc:creator>
      <pubDate>Wed, 21 Oct 2026 12:08:58 +0000</pubDate>
      <link>https://dev.to/ercross/recond-pull-based-deployments-for-one-server-without-a-platform-1jjb</link>
      <guid>https://dev.to/ercross/recond-pull-based-deployments-for-one-server-without-a-platform-1jjb</guid>
      <description>&lt;p&gt;The simple use case is this: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have a machine&lt;/li&gt;
&lt;li&gt;And a containerized app running on that machine. &lt;/li&gt;
&lt;li&gt;Your CI pipeline already builds and publishes images. &lt;/li&gt;
&lt;li&gt;You do not want a full container orchestrator for this workload, but you also do not want to SSH into the box and manually run deploy scripts every time a new image is ready. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the space &lt;code&gt;reconD&lt;/code&gt; is designed for. &lt;code&gt;reconD&lt;/code&gt; is a single-host deployment reconciliation agent. To put it simply, it watches your deployment metadata and deploys the latest version of your app. &lt;/p&gt;

&lt;p&gt;A typical workflow for a sample app called my-app&lt;/p&gt;

&lt;p&gt;Your CI publishes: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a container image to GHCR &lt;/li&gt;
&lt;li&gt;a GitHub Release &lt;/li&gt;
&lt;li&gt;a &lt;code&gt;deployment-metadata.json&lt;/code&gt; file that specifies the image, its immutable manifest digest, and the git SHA. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On your virtual machine, reconD periodically asks: "What is the latest desired deployment for &lt;code&gt;dev-my-app&lt;/code&gt; or staging-my-app?" &lt;/p&gt;

&lt;p&gt;Then it checks: "Is this server already running that digest?" If not, it runs the command you configured:&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;sh make redeploy-app&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;That command can be a shell script, a Make target, a Docker command, or something that wraps Docker Compose. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;reconD&lt;/code&gt; does not try to understand your whole app topology. It does not manage networking, volumes, secrets, scaling, scheduling, ingress, or clusters. That narrowness is intentional. It only owns the reconciliation loop: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetch desired deployment metadata. &lt;/li&gt;
&lt;li&gt;Compare desired digest with local state. &lt;/li&gt;
&lt;li&gt;Inspect the runtime container. &lt;/li&gt;
&lt;li&gt;Apply an optional strategy, such as updating an env file with the new image tag. &lt;/li&gt;
&lt;li&gt;Run your deploy command. &lt;/li&gt;
&lt;li&gt;Wait for health. &lt;/li&gt;
&lt;li&gt;Commit deployment state. &lt;/li&gt;
&lt;li&gt;Roll back if the deployment fails.&lt;/li&gt;
&lt;li&gt;Notify you of deployment status &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;reconD just keeps asking: "Is this host running the latest version of your app yet?" And if the answer is no, it runs the one command you already trust. It is not Kubernetes. It is not trying to become Kubernetes. It is a small reconciliation loop for single-host deployments, built for the cases where that is exactly enough.&lt;br&gt;
That's the whole model. &lt;strong&gt;The server pulls; nothing pushes into it.&lt;/strong&gt; No inbound SSH from CI, no deploy keys in your pipeline, no open webhook port.&lt;/p&gt;
&lt;h2&gt;
  
  
  What reconD deliberately doesn't do
&lt;/h2&gt;

&lt;p&gt;reconD doesn't know what Compose is. It runs &lt;em&gt;your&lt;/em&gt; command. That command can be &lt;code&gt;docker compose up -d&lt;/code&gt;, &lt;code&gt;docker run ...&lt;/code&gt;, &lt;code&gt;make redeploy&lt;/code&gt;, or a script that runs migrations first. All the knowledge about how to deploy on this particular machine stays in that command, where you can read and test it.&lt;/p&gt;

&lt;p&gt;That keeps reconD small. It doesn't manage networks, volumes, secrets, ingress, scaling, or clusters. If a problem needs to understand your app's topology, it belongs in your deploy command, not in the agent.&lt;/p&gt;
&lt;h2&gt;
  
  
  Failure handling
&lt;/h2&gt;

&lt;p&gt;The interesting part of deployment automation isn't the happy path. It's what happens with a bad release.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bounded retries.&lt;/strong&gt; Forward deployments and rollbacks retry with exponential backoff and jitter, up to limits you set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;deployment_retry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;max_retry_forward_deployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;max_retry_rollback&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;It remembers failures.&lt;/strong&gt; Without this, a bad image gives you an endless loop: deploy, fail the health check, roll back, next poll, deploy the same bad image again. reconD records the failed digest in its state file and doesn't try it again automatically. If the rollback succeeds, your service is back up, but the failed version stays blocked. Pushing a &lt;em&gt;newer&lt;/em&gt; release unblocks deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It survives restarts.&lt;/strong&gt; The current deployment, the previous one (the rollback target), and any terminal failure live in one JSON state file on disk. Restart the agent, or the host, and it picks up where it left off.&lt;/p&gt;

&lt;p&gt;You can also point reconD at a Slack webhook to hear when a deployment starts, succeeds, or fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you run apps on a single server and want deployments that are pulled, health-checked, and can roll back, without adopting a platform, give it a try:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/ercross/reconD" rel="noopener noreferrer"&gt;github.com/ercross/reconD&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Issues, ideas, and "this broke on my distro" reports are all welcome.&lt;/p&gt;

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