<?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: Andrea Mancuso</title>
    <description>The latest articles on Forem by Andrea Mancuso (@andreamancuso).</description>
    <link>https://forem.com/andreamancuso</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%2F2439031%2F4fec070e-e71b-4ed7-9fb4-3d38effd6c8c.png</url>
      <title>Forem: Andrea Mancuso</title>
      <link>https://forem.com/andreamancuso</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/andreamancuso"/>
    <language>en</language>
    <item>
      <title>The Strategic Value of Algebraic Data Types</title>
      <dc:creator>Andrea Mancuso</dc:creator>
      <pubDate>Wed, 11 Feb 2026 15:06:27 +0000</pubDate>
      <link>https://forem.com/andreamancuso/the-strategic-value-of-algebraic-data-types-206i</link>
      <guid>https://forem.com/andreamancuso/the-strategic-value-of-algebraic-data-types-206i</guid>
      <description>&lt;p&gt;In software management, "technical debt" is often discussed as an abstract concept. However, a significant portion of that debt stems from a specific source: &lt;strong&gt;impossible states&lt;/strong&gt;. Algebraic Data Types (ADTs) are a disciplined way of defining what is allowed in a system so that invalid configurations simply cannot exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: systems that allow nonsense
&lt;/h2&gt;

&lt;p&gt;Most software today is built using "loose" data modeling. Consider a standard user profile:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;is_logged_in&lt;/strong&gt; (True/False)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;is_admin&lt;/strong&gt; (True/False)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the surface, this is simple. But this design allows for a logical contradiction: a user who is &lt;strong&gt;not logged in&lt;/strong&gt; but is marked as an &lt;strong&gt;admin&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's the digital equivalent of saying someone is not in the building, yet they are currently in the executive boardroom. It shouldn't be possible, yet the code allows it. When a system permits these "impossible states," engineers must write defensive checks (if/else statements) everywhere to prevent bugs. Over time, this results in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increased testing overhead.&lt;/li&gt;
&lt;li&gt;Slower development cycles.&lt;/li&gt;
&lt;li&gt;Higher maintenance costs.&lt;/li&gt;
&lt;li&gt;Subtle, hard-to-track production incidents.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution: Structural Integrity
&lt;/h2&gt;

&lt;p&gt;Instead of using various "flags" to describe a user, an ADT approach defines the user as exactly one of a few specific roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Anonymous&lt;/strong&gt; (No extra data)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LoggedIn&lt;/strong&gt; (Contains a username and specific permissions)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this model, there is no physical way to be "Anonymous" and an "Admin" simultaneously. The system's architecture makes the error impossible to code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Business Consequences
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Reliable Production Environments&lt;/strong&gt; Bugs are often the result of "edge cases" where data ends up in a combination the developer didn't foresee. ADTs eliminate these edge cases by design. The system doesn't just catch errors; it refuses to represent them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Safer, Faster Iteration&lt;/strong&gt; &lt;br&gt;
When you need to change the system—for example, adding a new "Guest" tier—the compiler acts as an automated auditor. It forces the developer to update every single part of the application that handles users. Nothing is left to memory, and nothing breaks silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Lower Cognitive Load&lt;/strong&gt; &lt;br&gt;
Engineers no longer need to remember complex hidden rules (e.g., "The admin flag only counts if the login flag is also true"). The rules are encoded into the structure of the data itself. This significantly reduces onboarding time for new hires and lowers long-term maintenance costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI Advantage
&lt;/h2&gt;

&lt;p&gt;As more organizations look toward AI-assisted coding, data structure becomes even more critical. AI tools are significantly more accurate when the "possibility space" of a system is narrow and explicit.&lt;/p&gt;

&lt;p&gt;If a system clearly defines that a user is either &lt;code&gt;Anonymous&lt;/code&gt; or &lt;code&gt;LoggedIn&lt;/code&gt;, the AI doesn't have to guess which combinations of flags are valid. This leads to fewer "hallucinations" in the code and more reliable automated suggestions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary: Risk Control Through Design
&lt;/h2&gt;

&lt;p&gt;Think of ADTs like &lt;strong&gt;internal accounting controls&lt;/strong&gt;. You can either spend resources training people not to make illegal entries, or you can design the ledger so that illegal entries cannot be typed in the first place.&lt;/p&gt;

&lt;p&gt;One approach relies on constant human vigilance; the other relies on structure. Algebraic Data Types provide a structural reduction in risk, making your software more predictable, your developers more efficient, and your systems more resilient to change.&lt;/p&gt;

</description>
      <category>programming</category>
    </item>
    <item>
      <title>The trivialization of server management: the hidden costs and risks</title>
      <dc:creator>Andrea Mancuso</dc:creator>
      <pubDate>Wed, 22 Oct 2025 18:21:06 +0000</pubDate>
      <link>https://forem.com/andreamancuso/the-trivialization-of-server-management-the-hidden-costs-and-risks-4205</link>
      <guid>https://forem.com/andreamancuso/the-trivialization-of-server-management-the-hidden-costs-and-risks-4205</guid>
      <description>&lt;p&gt;&lt;em&gt;Or: Why "Just get a VPS" is dangerously incomplete advice&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The deployment illusion
&lt;/h2&gt;

&lt;p&gt;The VPS market has exploded from $2.4 billion in 2018 to over $5 billion in 2024. What was once the guarded domain of seasoned sysadmins is now a playground for anyone with a credit card. But here's what nobody talks about: &lt;strong&gt;we've made deployment easy while security remains hard&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://blog.cloudflare.com/ddos-threat-report-2023-q1/" rel="noopener noreferrer"&gt;Cloudflare's Q1 2023 DDoS Threat Report&lt;/a&gt;, attackers have shifted from compromising IoT devices to enslaving vulnerable and misconfigured VPS servers, building botnets that are up to 5,000 times more powerful than IoT-based ones. Meanwhile, &lt;a href="https://owasp.org/www-project-top-ten/" rel="noopener noreferrer"&gt;security research consistently shows&lt;/a&gt; that misconfiguration is a top security threat, with OWASP ranking Security Misconfiguration in their Top 10, noting that 90% of applications were tested for some form of misconfiguration.&lt;/p&gt;

&lt;h2&gt;
  
  
  A cautionary tale (mine)
&lt;/h2&gt;

&lt;p&gt;In 2014, I spun up a VPS for a company project. I knew enough to deploy but not enough to know what I didn't know. Password authentication was left enabled on SSH; a single configuration line: &lt;code&gt;PasswordAuthentication no&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I discovered the breach when the company owner asked about a CPU spike. Not through monitoring or alerts, through the bill.&lt;/p&gt;

&lt;p&gt;This wasn't sophisticated. Attackers simply scanned for port 22, tried common usernames, and brute-forced passwords. &lt;strong&gt;It still works today&lt;/strong&gt; because we're creating vulnerable servers faster than ever.&lt;/p&gt;

&lt;p&gt;That experience is what makes me twitchy about VPS recommendations. One checkbox. One config line. &lt;em&gt;That's all it took.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The confidence gap
&lt;/h2&gt;

&lt;p&gt;Developers believe they're doing it right:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"I disabled root login!" &lt;em&gt;(but left password auth enabled)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;"I have a firewall!" &lt;em&gt;(allowing 0.0.0.0/0 on port 22)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;"I keep things updated!" &lt;em&gt;(manually, when remembered)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike code that crashes immediately, a poorly secured server might run fine for months until compromised. By then, you're part of a botnet and don't know it. And unlike a failing test suite, your VPS won't warn you when it's compromised. It'll just start sending spam for someone else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The social dynamics&lt;/strong&gt;: Nobody admits they're bad at security. Can't ask without looking incompetent. Can't tell your boss you shouldn't be doing this. Can't admit to peers you're winging it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tools that could help (but often don't get mentioned)
&lt;/h2&gt;

&lt;p&gt;Modern defaults are better. &lt;a href="https://cloud-images.ubuntu.com/" rel="noopener noreferrer"&gt;Ubuntu Cloud Images&lt;/a&gt; disable password SSH by default. Infrastructure as Code (&lt;a href="https://docs.ansible.com/ansible/latest/getting_started/introduction.html" rel="noopener noreferrer"&gt;Ansible&lt;/a&gt;, &lt;a href="https://developer.hashicorp.com/terraform" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;, &lt;a href="https://opentofu.org/" rel="noopener noreferrer"&gt;OpenTofu&lt;/a&gt;) can codify security into reusable scripts. Automation tools exist for updates and monitoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Immutable infrastructure&lt;/strong&gt; offers another path: Container-based operating systems like &lt;a href="https://www.flatcar.org/" rel="noopener noreferrer"&gt;Flatcar&lt;/a&gt;, &lt;a href="https://fedoraproject.org/coreos/" rel="noopener noreferrer"&gt;CoreOS&lt;/a&gt;, or &lt;a href="https://aws.amazon.com/bottlerocket/" rel="noopener noreferrer"&gt;Bottlerocket&lt;/a&gt; shift the paradigm entirely. Instead of securing a mutable server, you deploy immutable images, use declarative configuration, and replace rather than update. This reduces the security surface significantly but it's a different mental model that requires learning &lt;a href="https://dev.to/ezinne_anne/containers-what-is-containerization-and-container-orchestration-38pe"&gt;container orchestration&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's the problem&lt;/strong&gt;: When someone says "just get a VPS," they don't follow with "but first spend 40 hours learning Terraform/OpenTofu" or "consider if immutable infrastructure fits your use case."&lt;/p&gt;

&lt;p&gt;The responsible advice is: Learn IaC, script everything, automate from day one, or use immutable infrastructure. That's honest. But it's not what people mean by "just get a VPS."&lt;/p&gt;

&lt;h2&gt;
  
  
  The true cost of "$5/Month"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Initial setup: 4-8 hours&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSH keys, firewall, fail2ban, automatic updates, SSH hardening, web server, SSL certificates, monitoring, backups.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ongoing maintenance: 2-4 hours/month&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security patches, kernel updates, certificate renewals, log analysis, dealing with attacks, backup verification.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When things break: 4-48 hours&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server unresponsive at 2 AM, failed certificate renewals, kernel panics, compromised servers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Annual time investment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First year: 28-80 hours (setup + 12 months maintenance)&lt;/li&gt;
&lt;li&gt;Subsequent years: 24-48 hours minimum&lt;/li&gt;
&lt;li&gt;Plus incidents (unpredictable)&lt;/li&gt;
&lt;li&gt;Add 20-40 hours if you learn IaC to do it properly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That "$5/month VPS" costs &lt;strong&gt;48-120 hours in year one&lt;/strong&gt; if you do it right. Price your time accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Required knowledge domains
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Baseline Survival Skills:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linux system administration (packages, processes, permissions)&lt;/li&gt;
&lt;li&gt;Basic firewall configuration&lt;/li&gt;
&lt;li&gt;SSH key management and hardening&lt;/li&gt;
&lt;li&gt;SSL/TLS certificate management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Professional Security Skills:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network security (attack vectors, DDoS mitigation, intrusion detection)&lt;/li&gt;
&lt;li&gt;Log analysis and forensics&lt;/li&gt;
&lt;li&gt;Backup strategies and disaster recovery testing&lt;/li&gt;
&lt;li&gt;Incident response procedures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even the "baseline" assumes significant systems knowledge most developers don't have. And "just get a VPS" rarely distinguishes between a hobby blog and a production system. People often use the same casual advice for both.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tool that disappeared
&lt;/h2&gt;

&lt;p&gt;Debian had a &lt;code&gt;harden&lt;/code&gt; package to help secure servers. It was removed in 2015 due to poor maintainability. Ironically, right as the VPS market exploded and first-time server operators skyrocketed. Today there's no simple &lt;code&gt;apt install debian-hardening&lt;/code&gt;. You must manually configure everything or use third-party scripts of varying trustworthiness.&lt;/p&gt;

&lt;p&gt;The gap between VPS accessibility and available hardening tools has grown wider every year.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modern tools still miss the mark
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"AI-managed VPS"&lt;/strong&gt; gives you a chatbot that answers questions. You still configure everything. It's "Stack Overflow in chat form," not automatic security.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coolify and Dokploy&lt;/strong&gt; simplify application deployment (SSL, reverse proxy, orchestration) but don't handle underlying server security. You still need to harden SSH, configure firewalls, manage updates, and set up intrusion detection.&lt;/p&gt;

&lt;h2&gt;
  
  
  The alternative: shared hosting
&lt;/h2&gt;

&lt;p&gt;For $5-10/month, shared hosting handles security updates, SSL, backups, DDoS mitigation, and monitoring. &lt;strong&gt;Quality varies&lt;/strong&gt;, and most providers don't publish security audit results. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The advantage isn't superior security, it's transferred responsibility.&lt;/strong&gt; When something breaks, it's their problem. You're paying for peace of mind, not necessarily better security.&lt;/p&gt;

&lt;p&gt;For personal blogs, portfolios, and side projects, these constraints often beat the burden of VPS management.&lt;/p&gt;

&lt;h2&gt;
  
  
  The vendor lock-in paradox
&lt;/h2&gt;

&lt;p&gt;Here's the irony: while everyone warns about PaaS vendor lock-in, &lt;strong&gt;VPS creates its own lock-in through accumulated complexity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PaaS lock-in&lt;/strong&gt; (what everyone fears): Proprietary APIs, platform-specific configs, migration effort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VPS lock-in&lt;/strong&gt; (what nobody mentions): Years of accumulated server configuration, custom security hardening, tribal knowledge, undocumented tweaks, and the dread of recreating it all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The escape hatch&lt;/strong&gt;: Infrastructure as Code. Script everything with Terraform/Ansible from day one and migration becomes trivial. But most people don't do this. Instead, they SSH in, make manual changes, and accumulate state.&lt;/p&gt;

&lt;p&gt;Try migrating a two-year-old manually-configured VPS. You'll spend days recreating everything, assuming you even documented it.&lt;/p&gt;

&lt;p&gt;VPS flexibility is real. But how many times have you actually migrated providers? Or has "flexibility" just meant "freedom to accumulate technical debt"?&lt;/p&gt;

&lt;h2&gt;
  
  
  What should exist (and why it doesn't yet)
&lt;/h2&gt;

&lt;p&gt;The most comprehensive solutions are &lt;strong&gt;debian-cis by OVH&lt;/strong&gt; and &lt;strong&gt;harbian-audit by HardenedLinux&lt;/strong&gt;. Both are production-grade hardening frameworks based on CIS benchmarks. OVH uses debian-cis for their PCI-DSS infrastructure; harbian-audit extends this with STIG compliance checks and supports Debian 9-12, CentOS 8, and Ubuntu 22.&lt;/p&gt;

&lt;p&gt;They include 230-270+ automated checks, audit and apply modes, and can achieve 80-85% compliance on a blank system. HardenedLinux even provides pre-hardened AMI images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But&lt;/strong&gt;: Even these require significant systems knowledge. They're professional tools for people who already understand server hardening—not one-click solutions for beginners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What about managed services?&lt;/strong&gt; AWS Lightsail, managed Kubernetes nodes, and similar offerings &lt;em&gt;do&lt;/em&gt; provide better defaults and handle much of the security burden. But they're managed services with less flexibility, not bare VPS with root access. Moreover, they're typically much more expensive than a regular VPS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The missing product&lt;/strong&gt;: A bare-metal VPS with full root access that's simultaneously secure by default:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSH: keys only, non-standard port, fail2ban configured&lt;/li&gt;
&lt;li&gt;Firewall: sensible defaults&lt;/li&gt;
&lt;li&gt;Automatic security updates: enabled&lt;/li&gt;
&lt;li&gt;Monitoring: basic alerting configured&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;And still:&lt;/strong&gt; full root access to break anything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it doesn't exist&lt;/strong&gt;: This is a support trade-off, not just a technical failing. The core value proposition of a traditional VPS is &lt;em&gt;flexibility&lt;/em&gt;, which is often mutually exclusive with enforced security defaults. If a provider enforces a non-standard SSH port or disables password auth, they risk breaking beginners' custom setups and generating support tickets. It's easier to provide a blank slate and let users break their own servers.&lt;/p&gt;

&lt;p&gt;The tools exist. The product incentives don't align with creating it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Responsible Advice
&lt;/h2&gt;

&lt;p&gt;If recommending VPS, include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The reality&lt;/strong&gt;: "You're responsible for security, updates, backups, monitoring—not trivial"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual resources&lt;/strong&gt;: Complete hardening checklists, not just "use fail2ban"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time expectations&lt;/strong&gt;: "4-8 hours initially, 2-4 hours monthly, plus emergencies"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consider alternatives&lt;/strong&gt;: "Would managed hosting or PaaS work better?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real consequences&lt;/strong&gt;: Compromised data, crypto mining, DDoS participation, legal liability&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;We've solved deployment. We haven't solved responsibility.&lt;/p&gt;

&lt;p&gt;The VPS boom democratized deployment but not security. We've created an ecosystem where "just get a VPS" is common advice, but "learn IaC first" isn't.&lt;/p&gt;

&lt;p&gt;The result? Growing numbers of vulnerable servers maintained by well-meaning developers who don't know what they don't know.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Just get a VPS" is incomplete advice.&lt;/strong&gt; The complete version is: "Get a VPS, learn Infrastructure as Code, automate everything, and accept the time investment." That's honest yet rarely said.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your $5 VPS isn't cheap. It's a subscription to either anxiety or learning.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you choose VPS: learn IaC properly, use established security configurations (debian-cis, Ansible roles), distinguish between hobby and production risk, and budget 48-120 hours in year one.&lt;/p&gt;

&lt;p&gt;Or recognize that managed infrastructure might be more pragmatic. Infrastructure isn't your product. It's the foundation that should be stable enough to forget about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Until "learn IaC first" becomes as common as "just get a VPS," we'll keep creating the problem we're trying to solve.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Can you really forget about that $5/month VPS?&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;If you do choose the VPS path, start here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.cisecurity.org/benchmark/debian_linux" rel="noopener noreferrer"&gt;CIS Debian Linux Benchmark&lt;/a&gt; - Industry-standard hardening guidelines&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ovh/debian-cis" rel="noopener noreferrer"&gt;debian-cis by OVH&lt;/a&gt; - Automated CIS benchmark hardening (used in OVH production)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hardenedlinux/harbian-audit" rel="noopener noreferrer"&gt;harbian-audit by HardenedLinux&lt;/a&gt; - Extended hardening with STIG compliance (includes pre-hardened images)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://wiki.mozilla.org/Security/Server_Side_TLS" rel="noopener noreferrer"&gt;Mozilla Server Side TLS Guidelines&lt;/a&gt; - SSL/TLS configuration best practices&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Have you been burned by VPS management? Share your stories in the comments. No judgment.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>infrastructureascode</category>
      <category>security</category>
    </item>
    <item>
      <title>Running Rust Binaries on Shared Hosting: A Practical Approach to Type Safety on a Budget</title>
      <dc:creator>Andrea Mancuso</dc:creator>
      <pubDate>Fri, 17 Oct 2025 18:33:15 +0000</pubDate>
      <link>https://forem.com/andreamancuso/running-rust-binaries-on-shared-hosting-a-practical-approach-to-type-safety-on-a-budget-jmb</link>
      <guid>https://forem.com/andreamancuso/running-rust-binaries-on-shared-hosting-a-practical-approach-to-type-safety-on-a-budget-jmb</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I was tired of PHP's type system. Even with &lt;a href="https://phpstan.org/" rel="noopener noreferrer"&gt;PHPStan&lt;/a&gt; and &lt;a href="https://psalm.dev/" rel="noopener noreferrer"&gt;Psalm&lt;/a&gt;, there's no substitute for real compile-time guarantees. But I'm also practical - I don't want to pay for a VPS, maintain a server, manage security updates, configure databases, set up backups, and babysit infrastructure when shared hosting costs &amp;lt; $10/month and handles all of that for me.&lt;/p&gt;

&lt;p&gt;The constraint: How do you get the benefits of compiled, statically-typed languages (Go, Rust) on restrictive shared hosting that only officially supports PHP?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: PHP as a thin wrapper
&lt;/h2&gt;

&lt;p&gt;The approach is relatively simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;PHP receives the HTTP request&lt;/li&gt;
&lt;li&gt;PHP serializes request context (headers, body, query params) to JSON&lt;/li&gt;
&lt;li&gt;PHP spawns your compiled binary as a subprocess&lt;/li&gt;
&lt;li&gt;Binary reads JSON from stdin, does its work, outputs response&lt;/li&gt;
&lt;li&gt;PHP captures stdout and returns it to the client&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's essentially CGI-style execution, but bypassing the hosting provider's CGI restrictions by going through PHP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  PHP Side (simplified)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'method'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_METHOD'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'uri'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_URI'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'query'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'headers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;getallheaders&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="s1"&gt;'body'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;file_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'php://input'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// may want to limit this&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// may want to set a timeout&lt;/span&gt;
&lt;span class="nv"&gt;$process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;proc_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'./myapp'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Your compiled binary&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'pipe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'r'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;// stdin&lt;/span&gt;
        &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'pipe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;// stdout&lt;/span&gt;
        &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'pipe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;// stderr&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nv"&gt;$pipes&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;is_resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$process&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Send context to binary&lt;/span&gt;
    &lt;span class="nb"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pipes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$context&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nb"&gt;fclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pipes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Get response&lt;/span&gt;
    &lt;span class="nv"&gt;$output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;stream_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pipes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nv"&gt;$errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;stream_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pipes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nb"&gt;fclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pipes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nb"&gt;fclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pipes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$exitCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;proc_close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$process&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$exitCode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;http_response_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Process failed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'stderr'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$errors&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Go Side (simplified)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"database/sql"&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"io"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;

    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="s"&gt;"github.com/go-sql-driver/mysql"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Method&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;              &lt;span class="s"&gt;`json:"method"`&lt;/span&gt;
    &lt;span class="n"&gt;URI&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt;              &lt;span class="s"&gt;`json:"uri"`&lt;/span&gt;
    &lt;span class="n"&gt;Query&lt;/span&gt;   &lt;span class="k"&gt;map&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="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"query"`&lt;/span&gt;
    &lt;span class="n"&gt;Headers&lt;/span&gt; &lt;span class="k"&gt;map&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="kt"&gt;string&lt;/span&gt;   &lt;span class="s"&gt;`json:"headers"`&lt;/span&gt;
    &lt;span class="n"&gt;Body&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt;              &lt;span class="s"&gt;`json:"body"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="s"&gt;`json:"data"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Read context from stdin&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Failed to read input: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Failed to parse context: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Your business logic here&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Request failed: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Output response&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type: application/json&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Connect to database (use Unix socket for shared hosting)&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mysql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user:pass@unix(/var/run/mysqld/mysqld.sock)/dbname"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Your query logic&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT * FROM my_table"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Process results...&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="c"&gt;// ... populate data ...&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building for shared hosting
&lt;/h2&gt;

&lt;p&gt;The critical part: &lt;strong&gt;statically link your binary&lt;/strong&gt; so it doesn't depend on the host's glibc version.&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;# For Go (pure Go, no cgo)&lt;/span&gt;
&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;amd64 go build &lt;span class="nt"&gt;-o&lt;/span&gt; myapp &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-ldflags&lt;/span&gt; &lt;span class="s1"&gt;'-extldflags "-static"'&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Verify it's static&lt;/span&gt;
ldd myapp  &lt;span class="c"&gt;# Should say "not a dynamic executable"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upload via SFTP/SSH and set executable permissions:&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;chmod&lt;/span&gt; +x myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Database connection gotcha
&lt;/h2&gt;

&lt;p&gt;Shared hosting typically blocks direct TCP connections to MySQL. Use Unix sockets instead:&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="c"&gt;// Unlikely to work on shared hosting:&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mysql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user:pass@tcp(localhost:3306)/dbname"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Should work on shared hosting:&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mysql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user:pass@unix(/var/run/mysqld/mysqld.sock)/dbname"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will need to find your socket path; you may use &lt;code&gt;phpinfo()&lt;/code&gt; for this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance results (YMMV)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Single row query:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total time: 40ms&lt;/li&gt;
&lt;li&gt;Memory: ~2.4 MB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;700 rows (406 KB JSON):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total time: 493ms&lt;/li&gt;
&lt;li&gt;Memory: ~2.4 MB&lt;/li&gt;
&lt;li&gt;Breakdown:

&lt;ul&gt;
&lt;li&gt;PHP + spawn overhead: ~38ms&lt;/li&gt;
&lt;li&gt;MySQL query: ~350ms&lt;/li&gt;
&lt;li&gt;JSON encoding: ~50ms&lt;/li&gt;
&lt;li&gt;Pipe I/O: ~55ms&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Memory usage is pretty good - Node.js would use &amp;gt;40 MB for the same workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Considerations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Processes spawned on shared hosting are typically &lt;strong&gt;not sandboxed&lt;/strong&gt;. Your binary runs with the same permissions as your PHP scripts - it can read/write any files your account can access and make any network connections allowed by your hosting provider.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Validate all input rigorously&lt;/strong&gt; - Never trust data passed from PHP to your binary&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't expose this pattern to untrusted users&lt;/strong&gt; - This approach is for your own applications, not multi-tenant systems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sanitize file paths&lt;/strong&gt; - Be careful with any file operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit what your binary can do&lt;/strong&gt; - Follow principle of least privilege&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor resource usage&lt;/strong&gt; - Shared hosts will kill processes that consume too much CPU/memory&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The security model is essentially the same as running PHP scripts - your code runs in your account's context with no additional isolation. Design accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Works
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Real type safety&lt;/strong&gt; - Compile-time guarantees, no runtime surprises&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt; - Compiled binaries are fast&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low memory&lt;/strong&gt; - 2-10 MB vs 50-200 MB for typical app servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No server maintenance&lt;/strong&gt; - Let your host handle updates, security patches, database management, backups&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cheap hosting&lt;/strong&gt; - Works on &amp;lt; $10/month shared hosting (though some providers disable &lt;code&gt;proc_open&lt;/code&gt; and such)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language choice&lt;/strong&gt; - Use Go, Rust, or any compiled language that can generate a fully static binary&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Familiar deployment&lt;/strong&gt; - Just upload files via FTP/SFTP&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Process spawn overhead&lt;/strong&gt; - ~30-40ms per request (mitigated with caching)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No persistent state&lt;/strong&gt; - Each request is isolated (use external storage/cache)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity&lt;/strong&gt; - Two codebases (PHP wrapper + binary)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build step&lt;/strong&gt; - Must compile for Linux x86_64&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited isolation&lt;/strong&gt; - Binaries run with your account's full permissions&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;This approach bridges two worlds: the accessibility of shared hosting and the robustness of compiled languages. It's far from perfect - you sacrifice some performance for compatibility - but for side projects and small applications, it's a sweet spot of cost, safety, and simplicity.&lt;/p&gt;

&lt;p&gt;You get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solid type-safe business logic (if you use Rust)&lt;/li&gt;
&lt;li&gt;Reasonable performance (sub-second responses)&lt;/li&gt;
&lt;li&gt;Minimal memory footprint&lt;/li&gt;
&lt;li&gt;&amp;lt; $10/month hosting with zero infrastructure management&lt;/li&gt;
&lt;li&gt;Managed database, backups, and security updates already included in your hosting plan&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do I ultimately recommend this? For low traffic websites, educational purposes, why not. For production grade systems you may want to run your VPS or go for a containerized approach.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>webdev</category>
      <category>php</category>
      <category>rust</category>
    </item>
    <item>
      <title>When OCaml Met C++: A Love Story in Signatures and Suffering</title>
      <dc:creator>Andrea Mancuso</dc:creator>
      <pubDate>Mon, 02 Jun 2025 12:42:57 +0000</pubDate>
      <link>https://forem.com/andreamancuso/when-ocaml-met-c-a-love-story-in-signatures-and-suffering-13pn</link>
      <guid>https://forem.com/andreamancuso/when-ocaml-met-c-a-love-story-in-signatures-and-suffering-13pn</guid>
      <description>&lt;p&gt;I didn't want to. Nobody &lt;em&gt;wants&lt;/em&gt; to.&lt;br&gt;
But when you're building a system that signs JSON and realize half your toolchain disagrees on what &lt;code&gt;"canonical"&lt;/code&gt; means, you either give up... or you write bindings to a C++ implementation of &lt;a href="https://datatracker.ietf.org/doc/html/rfc8785" rel="noopener noreferrer"&gt;RFC 8785&lt;/a&gt; and plug it into OCaml.&lt;/p&gt;

&lt;p&gt;So yeah. I chose violence.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;OCaml is great. But it doesn't come with a JSON canonicalizer - not for &lt;a href="https://github.com/cyberphone/json-canonicalization" rel="noopener noreferrer"&gt;JCS&lt;/a&gt; at least. And I needed deterministic output to &lt;strong&gt;sign and verify&lt;/strong&gt; my infrastructure &lt;a href="https://en.wikipedia.org/wiki/Domain-specific_language" rel="noopener noreferrer"&gt;DSL&lt;/a&gt; files in &lt;a href="https://github.com/rezn-project/rezn" rel="noopener noreferrer"&gt;rezn&lt;/a&gt;. Rust was yelling. I had no choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Found a solid implementation of JCS in &lt;a href="https://github.com/cyberphone/json-canonicalization/tree/master/go" rel="noopener noreferrer"&gt;Go&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Asked o3 to convert it to C++&lt;/li&gt;
&lt;li&gt;Politely suggested that reinventing a JSON parser was masochism - settled on &lt;a href="https://github.com/nlohmann/json" rel="noopener noreferrer"&gt;nlohmann/json&lt;/a&gt;...&lt;/li&gt;
&lt;li&gt;Write a clean C wrapper around it&lt;/li&gt;
&lt;li&gt;Hook it into &lt;a href="https://ocaml.org/" rel="noopener noreferrer"&gt;OCaml&lt;/a&gt; with &lt;code&gt;ctypes&lt;/code&gt; or &lt;a href="https://en.wikipedia.org/wiki/Foreign_function_interface" rel="noopener noreferrer"&gt;FFI&lt;/a&gt; bindings&lt;/li&gt;
&lt;li&gt;Get deterministic output and working signatures&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Outcome
&lt;/h3&gt;

&lt;p&gt;Now &lt;a href="https://github.com/rezn-project/rezn-dsl" rel="noopener noreferrer"&gt;rezn-dsl&lt;/a&gt; emits canonical JSON blobs that pass signature checks - regardless of what language consumes them. Rust is finally happy.&lt;br&gt;
I could've spent the weekend chilling. Instead, I shipped something. I'll take it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lessons Learned
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Canonicalization is real and terrifying.&lt;/li&gt;
&lt;li&gt;FFI is an art form.&lt;/li&gt;
&lt;li&gt;Sometimes it’s faster to wrap C++ than reimplement a spec.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>json</category>
      <category>cpp</category>
      <category>ocaml</category>
    </item>
    <item>
      <title>Design-by-Contract for Infrastructure: Stop Praying Your YAML Will Work</title>
      <dc:creator>Andrea Mancuso</dc:creator>
      <pubDate>Sat, 24 May 2025 19:13:44 +0000</pubDate>
      <link>https://forem.com/andreamancuso/design-by-contract-for-infrastructure-stop-praying-your-yaml-will-work-41j8</link>
      <guid>https://forem.com/andreamancuso/design-by-contract-for-infrastructure-stop-praying-your-yaml-will-work-41j8</guid>
      <description>&lt;p&gt;Let's face it: modern infrastructure is a Rube Goldberg machine built on top of YAML spaghetti, hope, and CI pipelines that pass only because someone disabled half the tests. We slap layers of "best practices" on top of chaos and call it DevOps. But what if we could actually &lt;em&gt;trust&lt;/em&gt; our infrastructure definitions?&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;Design-by-Contract for Infrastructure&lt;/strong&gt;, an idea that sounds like it came from someone who's sick of debugging "why didn't this container start?" for the 18th time this week.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is It?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://beyondtesting.dev/" rel="noopener noreferrer"&gt;Design-by-Contract&lt;/a&gt; (DbC) comes from the world of software correctness. &lt;a href="https://www.eiffel.org/doc/eiffel/Learning_Eiffel" rel="noopener noreferrer"&gt;Eiffel&lt;/a&gt; popularized it, but the core idea is simple: instead of "hope it works," you define &lt;strong&gt;preconditions&lt;/strong&gt;, &lt;strong&gt;postconditions&lt;/strong&gt;, and &lt;strong&gt;invariants&lt;/strong&gt; for your code and violations are caught immediately.&lt;/p&gt;

&lt;p&gt;Now imagine that same rigor applied to your infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Preconditions:&lt;/strong&gt; Is the image published? Are secrets available? Does the target node meet requirements?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Postconditions:&lt;/strong&gt; Did the pod reach &lt;code&gt;Running&lt;/code&gt; state? Is the service reachable?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invariants:&lt;/strong&gt; Each replica must be on a separate availability zone. All configs must be valid JSON. Ports must be unique per host.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s not about "linting" your YAML. It's about &lt;strong&gt;contractual guarantees&lt;/strong&gt;, embedded in the system, validated pre- and post-deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Infrastructure is &lt;strong&gt;code with side effects&lt;/strong&gt;, but we treat it like a text file that only matters if CI doesn't puke.&lt;/p&gt;

&lt;p&gt;This leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"It worked on staging" — the classic lie, because nobody validated the actual &lt;em&gt;contract&lt;/em&gt; between systems.&lt;/li&gt;
&lt;li&gt;Shadow failures — like autoscalers spinning phantom replicas that do nothing.&lt;/li&gt;
&lt;li&gt;Undiagnosed drift — where Terraform says "no changes", but your traffic is silently routing to &lt;code&gt;/dev/null&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Design-by-Contract forces us to &lt;strong&gt;formalize expectations&lt;/strong&gt;, which:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevents invalid states from being deployed in the first place.&lt;/li&gt;
&lt;li&gt;Allows runtime checks to validate that contracts &lt;em&gt;remain&lt;/em&gt; satisfied.&lt;/li&gt;
&lt;li&gt;Offers machine-verifiable &lt;em&gt;documentation&lt;/em&gt; for how the infrastructure is supposed to behave.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What It Could Look Like
&lt;/h2&gt;

&lt;p&gt;Imagine something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;pod&lt;/span&gt; &lt;span class="s2"&gt;"api"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-app:latest"&lt;/span&gt;
  &lt;span class="nx"&gt;replicas&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="nx"&gt;ports&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pre&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exists&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid_json&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DATABASE_URL"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Running"&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;replicas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;invariant&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unique&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;distribution&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"balanced"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not Kubernetes YAML. It's what Kubernetes &lt;em&gt;should have been&lt;/em&gt; if it hadn't been designed by a committee under Stockholm Syndrome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Isn't This Just Policy?
&lt;/h2&gt;

&lt;p&gt;No. Policies are rules someone defines &lt;em&gt;externally&lt;/em&gt;, often enforced &lt;em&gt;after&lt;/em&gt; something is already broken.&lt;/p&gt;

&lt;p&gt;Contracts are embedded &lt;strong&gt;within&lt;/strong&gt; the system definition. They travel with the code, they're checked &lt;em&gt;before&lt;/em&gt; and &lt;em&gt;after&lt;/em&gt; execution, and they fail loud and early.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is Anyone Doing This?
&lt;/h2&gt;

&lt;p&gt;Not really. There are hints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terraform&lt;/strong&gt; has &lt;code&gt;depends_on&lt;/code&gt;, but no real validation layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pulumi&lt;/strong&gt; gets closer with real programming constructs, but still lacks native contract semantics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/rezn-project/rezn" rel="noopener noreferrer"&gt;Rezn&lt;/a&gt;&lt;/strong&gt; (if it ever sees the light of day) might bake this in from the start.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But right now, the space is wide open for someone to say: &lt;strong&gt;YAML isn't good enough. My infrastructure deserves guarantees.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Design-by-Contract for Infrastructure isn; t a silver bullet, but it's &lt;em&gt;a&lt;/em&gt; bullet. And it sure beats firing blindly into production and hoping nothing explodes.&lt;/p&gt;

&lt;p&gt;If we truly believe in "infrastructure as code", then it's time we gave it the same respect we give application logic: contracts, guarantees, and no more "undefined behavior".&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>infrastructureascode</category>
      <category>devops</category>
    </item>
    <item>
      <title>Setting Up a Wi-Fi Access Point on Raspberry Pi with 5 GHz and Client Mode</title>
      <dc:creator>Andrea Mancuso</dc:creator>
      <pubDate>Wed, 07 May 2025 20:36:14 +0000</pubDate>
      <link>https://forem.com/andreamancuso/setting-up-a-wi-fi-access-point-on-raspberry-pi-with-5-ghz-and-client-mode-5a56</link>
      <guid>https://forem.com/andreamancuso/setting-up-a-wi-fi-access-point-on-raspberry-pi-with-5-ghz-and-client-mode-5a56</guid>
      <description>&lt;p&gt;If you want to use your Raspberry Pi as a Wi-Fi access point (AP) that connects to an existing shared Wi-Fi network and acts as a hotspot for other devices, here's how you can set it up.&lt;/p&gt;

&lt;p&gt;This setup leverages &lt;strong&gt;hostapd&lt;/strong&gt;, &lt;strong&gt;dnsmasq&lt;/strong&gt;, and &lt;strong&gt;iptables&lt;/strong&gt; to enable a seamless Wi-Fi AP, allowing for a faster and more reliable connection.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;A Raspberry Pi (with a &lt;strong&gt;Wi-Fi 6 USB dongle&lt;/strong&gt; or built-in support)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wi-Fi&lt;/strong&gt; router or AP&lt;/li&gt;
&lt;li&gt;A stable &lt;strong&gt;shared Wi-Fi network&lt;/strong&gt; to connect to&lt;/li&gt;
&lt;li&gt;Basic understanding of the terminal and Raspberry Pi networking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A text editor&lt;/strong&gt; (e.g., &lt;code&gt;nano&lt;/code&gt; or &lt;code&gt;vim&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1: Install Required Software&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;First, ensure your Raspberry Pi is up to date and install the necessary software 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 update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;hostapd dnsmasq iptables-persistent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2: Configure &lt;code&gt;hostapd&lt;/code&gt; for 5 GHz Wi-Fi&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Create the configuration file for &lt;code&gt;hostapd&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="nb"&gt;sudo &lt;/span&gt;nano /etc/hostapd/hostapd.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Insert the following configuration:&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;interface&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;wlan1&lt;/span&gt;
&lt;span class="py"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;nl80211&lt;/span&gt;
&lt;span class="py"&gt;ssid&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;PiRouter&lt;/span&gt;
&lt;span class="py"&gt;hw_mode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;
&lt;span class="py"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;36&lt;/span&gt;
&lt;span class="py"&gt;ieee80211d&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;ieee80211n&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;ieee80211ac&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;wmm_enabled&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;country_code&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;country_code_2_letter_ISO&lt;/span&gt;
&lt;span class="py"&gt;macaddr_acl&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;
&lt;span class="py"&gt;auth_algs&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;ignore_broadcast_ssid&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;
&lt;span class="py"&gt;wpa&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
&lt;span class="py"&gt;wpa_passphrase&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your_secure_password&lt;/span&gt;
&lt;span class="py"&gt;wpa_key_mgmt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;WPA-PSK&lt;/span&gt;
&lt;span class="py"&gt;rsn_pairwise&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;CCMP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;country_code_2_letter_ISO&lt;/code&gt; with your country code, i.e. US.&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;your_secure_password&lt;/code&gt; with your desired WPA2 passphrase.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hw_mode=a&lt;/code&gt; sets it to &lt;strong&gt;5 GHz&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ieee80211ac=1&lt;/code&gt; enables &lt;strong&gt;802.11ac&lt;/strong&gt; for Wi-Fi 6 speeds.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;channel=36&lt;/code&gt; is a good 5 GHz channel to start with.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3: Configure DHCP and DNS with &lt;code&gt;dnsmasq&lt;/code&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Edit the &lt;code&gt;dnsmasq&lt;/code&gt; configuration file:&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;nano /etc/dnsmasq.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following:&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;interface&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;wlan1&lt;/span&gt;
&lt;span class="py"&gt;dhcp-range&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;192.168.50.10,192.168.50.50,12h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets up the &lt;strong&gt;DHCP range&lt;/strong&gt; for devices that will connect to the Pi's Wi-Fi.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Step 4: Enable IP Forwarding and NAT&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Enable IP forwarding by uncommenting the appropriate line in the sysctl configuration file:&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;nano /etc/sysctl.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uncomment the line:&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;net.ipv4.ip_forward&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply the changes:&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;sysctl &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set up &lt;strong&gt;Network Address Translation (NAT)&lt;/strong&gt; to route traffic from your connected devices to the internet:&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;iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-A&lt;/span&gt; POSTROUTING &lt;span class="nt"&gt;-o&lt;/span&gt; wlan0 &lt;span class="nt"&gt;-j&lt;/span&gt; MASQUERADE
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; FORWARD &lt;span class="nt"&gt;-i&lt;/span&gt; wlan1 &lt;span class="nt"&gt;-o&lt;/span&gt; wlan0 &lt;span class="nt"&gt;-m&lt;/span&gt; state &lt;span class="nt"&gt;--state&lt;/span&gt; RELATED,ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; FORWARD &lt;span class="nt"&gt;-i&lt;/span&gt; wlan0 &lt;span class="nt"&gt;-o&lt;/span&gt; wlan1 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make these &lt;code&gt;iptables&lt;/code&gt; rules persist after reboot, run:&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;netfilter-persistent save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Step 5: Start Services&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Enable and start the &lt;code&gt;hostapd&lt;/code&gt; and &lt;code&gt;dnsmasq&lt;/code&gt; services:&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;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;hostapd
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start hostapd
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;dnsmasq
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start dnsmasq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Step 6: Reboot and Test&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Finally, reboot 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;&lt;span class="nb"&gt;sudo &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After rebooting, your Raspberry Pi should now be broadcasting the &lt;strong&gt;PiRouter&lt;/strong&gt; SSID. Devices should be able to connect to it, and &lt;strong&gt;DHCP&lt;/strong&gt; should assign IPs in the &lt;strong&gt;192.168.50.x range&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Slow speeds?&lt;/strong&gt; Make sure you’ve configured &lt;strong&gt;WMM&lt;/strong&gt; (&lt;code&gt;wmm_enabled=1&lt;/code&gt;), &lt;strong&gt;802.11ac&lt;/strong&gt; (&lt;code&gt;ieee80211ac=1&lt;/code&gt;), and check for interference in the 5 GHz band.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Devices not connecting?&lt;/strong&gt; Ensure that your device supports 802.11ac or ax for higher speeds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No IP address?&lt;/strong&gt; Double-check the &lt;code&gt;dnsmasq&lt;/code&gt; config for correct DHCP range and ensure &lt;code&gt;wlan1&lt;/code&gt; is properly assigned an IP.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;With just a few steps, you can turn your Raspberry Pi into a powerful Wi-Fi access point. This setup will improve your network’s performance and allow you to &lt;strong&gt;share your internet connection&lt;/strong&gt; over &lt;strong&gt;5 GHz Wi-Fi&lt;/strong&gt;, with &lt;strong&gt;high-speed access&lt;/strong&gt; and support for multiple devices.&lt;/p&gt;

&lt;p&gt;Feel free to tweak the configuration to fit your needs, and let me know if you have any questions!&lt;/p&gt;




&lt;p&gt;Let me know if you’d like me to tweak anything or add more details!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Multi-Arch Docker Builds at €14/Month - Just 2 VPSs</title>
      <dc:creator>Andrea Mancuso</dc:creator>
      <pubDate>Sat, 26 Apr 2025 16:37:12 +0000</pubDate>
      <link>https://forem.com/andreamancuso/multi-arch-docker-builds-at-eu14month-just-2-vpss-1eig</link>
      <guid>https://forem.com/andreamancuso/multi-arch-docker-builds-at-eu14month-just-2-vpss-1eig</guid>
      <description>&lt;p&gt;You don't need QEMU, GitHub Actions, or expensive CI services to build multi-architecture Docker images. With just two VPSs (one cheap ARM machine and one cheap x86 machine) you can build and push native &lt;code&gt;linux/amd64&lt;/code&gt; and &lt;code&gt;linux/arm64&lt;/code&gt; containers without emulation, without performance penalties, and without a monthly surprise bill. My setup costs just €14/month in total and reliably builds for both architectures using Docker's &lt;code&gt;buildx&lt;/code&gt; feature.&lt;/p&gt;

&lt;p&gt;The key is combining &lt;code&gt;docker buildx&lt;/code&gt; with SSH-based Docker contexts: each VPS becomes a node in a custom builder instance. The x86 VPS builds amd64 images locally, while the ARM VPS handles arm64 images natively. BuildKit takes care of the rest: parallel builds, multi-platform manifests, and direct pushes to the registry. It's clean, fast, and yours. No cloud CI, no opaque runners; just you, your servers, and full control over your builds.&lt;/p&gt;

&lt;p&gt;Set up the local amd64 docker node:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker buildx create --name multiarch --use --platform linux/amd64&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Set up the remote arm64 docker node:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker context create arm-vps --docker "host=ssh://user@your.arm.vps.ip"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Append the arm64 remote builder:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker buildx create --append --name multiarch --platform linux/arm64 arm-vps&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Inspect:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker buildx inspect multiarch --bootstrap&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name:          multiarch
Driver:        docker-container
Last Activity: 2025-04-26 16:31:34 +0000 UTC

Nodes:
Name:                  multiarch0
Endpoint:              unix:///var/run/docker.sock
Status:                running
BuildKit daemon flags: --allow-insecure-entitlement=network.host
BuildKit version:      v0.20.2
Platforms:             linux/amd64*, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386
Labels:
 org.mobyproject.buildkit.worker.executor:         oci
 org.mobyproject.buildkit.worker.hostname:         baab70c53c2d
 org.mobyproject.buildkit.worker.network:          host
 org.mobyproject.buildkit.worker.oci.process-mode: sandbox
 org.mobyproject.buildkit.worker.selinux.enabled:  false
 org.mobyproject.buildkit.worker.snapshotter:      overlayfs
GC Policy rule#0:
 All:            false
 Filters:        type==source.local,type==exec.cachemount,type==source.git.checkout
 Keep Duration:  48h0m0s
 Max Used Space: 488.3MiB
GC Policy rule#1:
 All:            false
 Keep Duration:  1440h0m0s
 Reserved Space: 9.313GiB
 Max Used Space: 93.13GiB
 Min Free Space: 47.5GiB
GC Policy rule#2:
 All:            false
 Reserved Space: 9.313GiB
 Max Used Space: 93.13GiB
 Min Free Space: 47.5GiB
GC Policy rule#3:
 All:            true
 Reserved Space: 9.313GiB
 Max Used Space: 93.13GiB
 Min Free Space: 47.5GiB

Name:                  multiarch1
Endpoint:              arm-vps
Status:                running
BuildKit daemon flags: --allow-insecure-entitlement=network.host
BuildKit version:      v0.20.2
Platforms:             linux/arm64*, linux/arm/v7, linux/arm/v6
Labels:
 org.mobyproject.buildkit.worker.executor:         oci
 org.mobyproject.buildkit.worker.hostname:         aba4522bbd7d
 org.mobyproject.buildkit.worker.network:          host
 org.mobyproject.buildkit.worker.oci.process-mode: sandbox
 org.mobyproject.buildkit.worker.selinux.enabled:  false
 org.mobyproject.buildkit.worker.snapshotter:      overlayfs
GC Policy rule#0:
 All:            false
 Filters:        type==source.local,type==exec.cachemount,type==source.git.checkout
 Keep Duration:  48h0m0s
 Max Used Space: 488.3MiB
GC Policy rule#1:
 All:            false
 Keep Duration:  1440h0m0s
 Reserved Space: 9.313GiB
 Max Used Space: 93.13GiB
 Min Free Space: 47.5GiB
GC Policy rule#2:
 All:            false
 Reserved Space: 9.313GiB
 Max Used Space: 93.13GiB
 Min Free Space: 47.5GiB
GC Policy rule#3:
 All:            true
 Reserved Space: 9.313GiB
 Max Used Space: 93.13GiB
 Min Free Space: 47.5GiB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the local node builds amd64 images, the remote node builds arm64 images.&lt;/p&gt;

&lt;p&gt;I hope you find this useful.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>multiplatform</category>
    </item>
    <item>
      <title>Running Phoenix applications on RISC-V: A Practical Report</title>
      <dc:creator>Andrea Mancuso</dc:creator>
      <pubDate>Sun, 20 Apr 2025 00:17:49 +0000</pubDate>
      <link>https://forem.com/andreamancuso/running-phoenix-applications-on-risc-v-a-practical-report-38d</link>
      <guid>https://forem.com/andreamancuso/running-phoenix-applications-on-risc-v-a-practical-report-38d</guid>
      <description>&lt;p&gt;As part of a personal experiment, I attempted to run a Phoenix LiveView application - &lt;a href="https://github.com/rawpair/rawpair" rel="noopener noreferrer"&gt;RawPair&lt;/a&gt; - on a RISC-V SBC (Milk-V Mars, Debian Trixie). The goal was to evaluate how viable it is to self-host modern web stacks on emerging architectures.&lt;/p&gt;

&lt;p&gt;Here's a summary of the process and findings.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Worked
&lt;/h2&gt;

&lt;p&gt;Platform: &lt;strong&gt;Debian Trixie&lt;/strong&gt; (testing) Device: &lt;a href="https://milkv.io/mars" rel="noopener noreferrer"&gt;Milk-V Mars&lt;/a&gt; (riscv64)&lt;/p&gt;

&lt;p&gt;Installed components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Elixir 1.18.3&lt;/li&gt;
&lt;li&gt;Erlang 27.3.3&lt;/li&gt;
&lt;li&gt;Node.js 22&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;Docker (docker.io package)&lt;/li&gt;
&lt;li&gt;Phoenix application code built and compiled successfully&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ttyd&lt;/code&gt; compiled from source&lt;/li&gt;
&lt;li&gt;Supervisord used for process management&lt;/li&gt;
&lt;li&gt;Basic Phoenix endpoint launched and connected to the database&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Didn't Work Out of the Box
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tailwind CSS CLI
&lt;/h3&gt;

&lt;p&gt;Phoenix LiveView's default asset pipeline depends on the Tailwind CLI (tailwind package), which wraps a native binary written in Rust (tailwindcss-oxide). The official Tailwind releases do not include builds for the riscv64 architecture.&lt;/p&gt;

&lt;p&gt;This resulted in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runtime errors stating the architecture was unsupported&lt;/li&gt;
&lt;li&gt;The Phoenix build process failing during mix assets.build&lt;/li&gt;
&lt;li&gt;Incompatibility with Tailwind's scanning and compilation steps&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Workarounds attempted
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Using Docker to run Tailwind in an arm64 container via &lt;a href="https://www.qemu.org/" rel="noopener noreferrer"&gt;QEMU emulation&lt;/a&gt; (proved too slow)&lt;/li&gt;
&lt;li&gt;Manually building Tailwind from source (required TurboRepo, also unavailable on RISC-V)&lt;/li&gt;
&lt;li&gt;Manually building TurboRepo from source (679 Rust modules)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What's the Core Issue?
&lt;/h3&gt;

&lt;p&gt;Tailwind's CLI requires a precompiled binary to run. On unsupported platforms like riscv64, no fallback mechanism exists. While the broader Phoenix stack is portable and builds fine on RISC-V, Tailwind currently introduces an architectural dependency that breaks compatibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Potential Solutions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Using the &lt;strong&gt;Play CDN&lt;/strong&gt; for Tailwind in limited cases (not viable if using &lt;code&gt;@apply&lt;/code&gt; or purging unused classes)&lt;/li&gt;
&lt;li&gt;Replacing Tailwind with a CSS approach that doesn't require native compilation (far from straightforward on Phoenix-based applications)&lt;/li&gt;
&lt;li&gt;Requesting RISC-V support upstream (see: &lt;a href="https://github.com/tailwindlabs/tailwindcss/discussions/13140" rel="noopener noreferrer"&gt;Tailwind GitHub issue&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Running Phoenix + PostgreSQL on RISC-V is possible and surprisingly smooth with Debian Trixie.&lt;/p&gt;

&lt;p&gt;Tailwind CSS currently blocks full compatibility due to its architecture-dependent binary.&lt;/p&gt;

&lt;p&gt;This experience highlights the challenges of using modern frontend tooling on emerging platforms.&lt;/p&gt;

&lt;p&gt;I'm documenting this in case others attempt something similar or are evaluating frontend stack portability.&lt;/p&gt;

</description>
      <category>elixir</category>
    </item>
    <item>
      <title>Is the browser always the right tool for the job?</title>
      <dc:creator>Andrea Mancuso</dc:creator>
      <pubDate>Fri, 15 Nov 2024 20:39:47 +0000</pubDate>
      <link>https://forem.com/andreamancuso/is-the-browser-always-the-right-tool-for-the-job-21la</link>
      <guid>https://forem.com/andreamancuso/is-the-browser-always-the-right-tool-for-the-job-21la</guid>
      <description>&lt;p&gt;For over two decades, we've been building web applications on a foundation that dates back to an era of static documents. The Document Object Model (DOM) and Cascading Style Sheets (CSS), both born in the late '90s, were designed for a simpler web. Today, we’re asking them to style and manage complex, highly interactive applications. Is it any wonder they struggle to keep up?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"CSS wasn’t built for managing the intricate layouts of complex applications—it was built to make your hyperlinks blue and underline your headings."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A System Straining Under Modern Demands&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the DOM and CSS were introduced, web pages were static and lightweight. Today, applications often render &lt;strong&gt;tens of thousands of DOM elements&lt;/strong&gt; simultaneously. This is where the cracks start to show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reflows and Repaints&lt;/strong&gt;: Every change to the DOM can trigger a cascade of recalculations across tens of thousands of elements. This process is computationally expensive, especially in data-heavy applications like dashboards or real-time trading platforms.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CSS Selectors at Scale&lt;/strong&gt;: The browser has to evaluate complex selectors across the entire DOM, leading to inefficiencies. A simple rule like .header &amp;gt; div must traverse and match elements against a tree containing thousands of nodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limited GPU Utilization in the Browser&lt;/strong&gt;: One of the biggest frustrations with traditional web-based applications is that the browser only offloads some CSS operations to the GPU. While certain CSS properties—such as transforms, animations, and opacity—can be GPU-accelerated, the majority of styles are still processed on the CPU. This creates a bottleneck, especially in complex, dynamic applications.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Nested Layers of Complexity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Modern frameworks like React or Angular mitigate some DOM inefficiencies but often exacerbate the problem by introducing their own layers of abstraction. Virtual DOMs, while helpful, still rely on underlying DOM updates to reflect changes on-screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Result: A Bottleneck for Performance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Despite modern optimizations, rendering thousands of elements with dynamic layouts and animations remains a challenge for the browser. This is particularly evident when compared to native or GPU-driven applications, where every pixel can be directly controlled.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Different Approach with XFrames
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/andreamancuso/xframes" rel="noopener noreferrer"&gt;XFrames&lt;/a&gt; takes a different approach to building user interfaces by skipping the DOM and CSS entirely. Instead of relying on the traditional browser rendering pipeline, &lt;strong&gt;XFrames&lt;/strong&gt; directly harnesses the power of the GPU to deliver high-performance, scalable user interfaces—without the bottlenecks. Also, unlike frameworks like Electron, which rely on two distinct layers — one for application logic and another for rendering via a browser process — XFrames integrates both logic and rendering into a single process (with the possibility to use Node workers for even higher performance and scalability). This eliminates the need for costly IPC communications between processes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Direct GPU Rendering&lt;/strong&gt;: One of the main advantages of XFrames is its ability to send rendering instructions directly to the GPU. Unlike the browser, which can only offload certain CSS properties to the GPU, XFrames maximizes GPU utilization for everything—from layout to animations—ensuring smooth, responsive interfaces that are native-like in performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deterministic Layouts with Yoga&lt;/strong&gt;: Rather than relying on complex CSS rules and reflows, XFrames uses Yoga, a layout engine designed for performance and flexibility. Yoga calculates layouts in a deterministic and efficient manner, which means it can handle even the most complex UIs without triggering unnecessary recalculations or reflows, resulting in a seamless experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cross-Platform Performance&lt;/strong&gt;: Whether you’re building a desktop application or a web app using &lt;strong&gt;WebAssembly&lt;/strong&gt;, XFrames supports multiple platforms out of the box. This is achieved by bypassing the limitations of the browser and leveraging native rendering capabilities, making it possible to develop high-performance applications that work consistently across different environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Eliminating the DOM Overhead&lt;/strong&gt;: By bypassing the DOM, XFrames eliminates the overhead of managing thousands of DOM elements. Instead of a slow, CPU-bound rendering cycle, XFrames sends precise instructions to the GPU for rendering, ensuring your application stays fast even as it scales.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Choose XFrames?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;: You no longer need to fight with CSS and the DOM to get the performance you need. With XFrames, you can focus on building your application’s functionality and user experience without worrying about performance degradation as your UI becomes more complex.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: XFrames was built with modern, data-intensive applications in mind. Whether you're developing a real-time trading platform or an interactive dashboard, XFrames can handle the rendering workload without breaking a sweat.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;: From the ground up, XFrames is designed to be lean and fast, leveraging the GPU for every aspect of the UI.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ready to Unlock Next-Gen Performance?
&lt;/h2&gt;

&lt;p&gt;With XFrames, you can break free from the traditional DOM and CSS bottlenecks while still leveraging WebAssembly for cross-platform compatibility. Whether you're building for the web or desktop, XFrames ensures that your application can take full advantage of GPU-driven rendering, delivering native-like performance without sacrificing flexibility.&lt;/p&gt;

&lt;p&gt;XFrames is still under heavy development and, whilst it is not quite production-ready, getting involved now means that you would be helping shape its development and influence its roadmap.&lt;br&gt;
So far, XFrames has been tested on WebGPU-enabled browsers (Chrome, Edge, Firefox Nightly) and Node.js (through OpenGL 3) on Windows 11 (x64), Ubuntu 22+ (x64), Fedora 41 (x64), Debian Trixie (x64), Raspberry OS (arm64 on a Raspberry Pi 5).&lt;/p&gt;

&lt;p&gt;Give it a try and let us know your feedback!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webassembly</category>
      <category>electron</category>
      <category>node</category>
    </item>
  </channel>
</rss>
