<?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: ricco020</title>
    <description>The latest articles on DEV Community by ricco020 (@ricco020).</description>
    <link>https://dev.to/ricco020</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%2F3960216%2Fbc01eb94-de65-41c2-9767-966a157976ae.jpeg</url>
      <title>DEV Community: ricco020</title>
      <link>https://dev.to/ricco020</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ricco020"/>
    <language>en</language>
    <item>
      <title>GitHub says these three repos have no licence. They all do.</title>
      <dc:creator>ricco020</dc:creator>
      <pubDate>Thu, 20 Aug 2026 01:52:17 +0000</pubDate>
      <link>https://dev.to/ricco020/github-says-these-three-repos-have-no-licence-they-all-do-1nl8</link>
      <guid>https://dev.to/ricco020/github-says-these-three-repos-have-no-licence-they-all-do-1nl8</guid>
      <description>&lt;p&gt;GitHub puts a licence label on every repository. It reads it from the &lt;code&gt;LICENSE&lt;/code&gt; file and&lt;br&gt;
normalises it to an SPDX identifier — &lt;code&gt;MIT&lt;/code&gt;, &lt;code&gt;BSD-3-Clause&lt;/code&gt;, &lt;code&gt;Apache-2.0&lt;/code&gt;. When it cannot,&lt;br&gt;
the API returns &lt;code&gt;NOASSERTION&lt;/code&gt; and the web UI quietly says &lt;strong&gt;"Other"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I checked twelve popular proxy, VPN and mesh-networking repositories through the GitHub API&lt;br&gt;
on 20 August 2026. Three came back &lt;code&gt;NOASSERTION&lt;/code&gt;. Here is what each of the three actually&lt;br&gt;
ships — because "Other" is not one situation, it is three very different ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  The measurement
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repository&lt;/th&gt;
&lt;th&gt;SPDX from the API&lt;/th&gt;
&lt;th&gt;Stars&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SagerNet/sing-box&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;NOASSERTION&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;37,141&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XTLS/Xray-core&lt;/td&gt;
&lt;td&gt;MPL-2.0&lt;/td&gt;
&lt;td&gt;41,121&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;juanfont/headscale&lt;/td&gt;
&lt;td&gt;BSD-3-Clause&lt;/td&gt;
&lt;td&gt;43,009&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tailscale/tailscale&lt;/td&gt;
&lt;td&gt;BSD-3-Clause&lt;/td&gt;
&lt;td&gt;35,368&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;v2fly/v2ray-core&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;34,491&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wireguard/wireguard-go&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;4,347&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;apernet/hysteria&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;22,355&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EAimTY/tuic&lt;/td&gt;
&lt;td&gt;GPL-3.0&lt;/td&gt;
&lt;td&gt;3,261&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cloudflare/cloudflared&lt;/td&gt;
&lt;td&gt;Apache-2.0&lt;/td&gt;
&lt;td&gt;15,304&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;netbirdio/netbird&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;NOASSERTION&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;28,520&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;zerotier/ZeroTierOne&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;NOASSERTION&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;17,029&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;slackhq/nebula&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;17,630&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three out of twelve. Now the interesting part.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. sing-box — GPLv3, plus a clause of its own
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;LICENSE&lt;/code&gt; file opens with the standard GPL version 3 or later grant. Then it adds:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In addition, no derivative work may use the name or imply association with this&lt;br&gt;
application without prior consent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sentence is why the label cannot be &lt;code&gt;GPL-3.0&lt;/code&gt;: the text is no longer the unmodified&lt;br&gt;
licence. The grant is still copyleft, with a naming restriction bolted on.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. netbird — BSD-3-Clause, except where it is AGPLv3
&lt;/h2&gt;

&lt;p&gt;Its &lt;code&gt;LICENSE&lt;/code&gt; file says it plainly in the first two lines:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This BSD-3-Clause license applies to all parts of the repository except for the&lt;br&gt;
directories [...] Those directories are licensed under the GNU Affero General Public&lt;br&gt;
License version 3.0.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A permissive licence for most of the tree, and the strongest network copyleft there is for&lt;br&gt;
part of it. One SPDX identifier cannot express that, and picking the friendlier of the two&lt;br&gt;
is exactly the mistake the label would encourage.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. ZeroTier — MPL, and a &lt;code&gt;nonfree/&lt;/code&gt; directory
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;LICENSE.txt&lt;/code&gt; is three lines, and the second one is the one that matters:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;See nonfree/LICENSE.md for all non-free ("source available") portions of this repository.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Source-available is not open source. A repository can be mostly MPL and still contain a&lt;br&gt;
directory you may read but not freely reuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to take from this
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;NOASSERTION&lt;/code&gt; does not mean "no licence" and it does not mean "unclear". It means &lt;strong&gt;GitHub&lt;br&gt;
declined to summarise this one in a single word&lt;/strong&gt;, and in all three cases it was right to&lt;br&gt;
decline. An extra clause, a per-directory split, and a non-free carve-out are all things&lt;br&gt;
that matter the moment you ship something built on top.&lt;/p&gt;

&lt;p&gt;The practical rule is boring and holds up: &lt;strong&gt;if you are redistributing, open the &lt;code&gt;LICENSE&lt;/code&gt;&lt;br&gt;
file.&lt;/strong&gt; The badge is a convenience, not a statement of terms. Two minutes of reading, and&lt;br&gt;
the three repositories above stop being surprises.&lt;/p&gt;

&lt;p&gt;I went down this road while comparing two of these cores in detail — the licence turned out&lt;br&gt;
to be the sharpest difference between them, well ahead of the protocol lists everyone else&lt;br&gt;
compares:&lt;br&gt;
&lt;a href="https://www.vpnsmith.com/en/blog/sing-box-vs-xray-2026" rel="noopener noreferrer"&gt;sing-box vs Xray: the differences that actually decide it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All figures read from the GitHub REST API on 20 August 2026; star counts move, the licence&lt;br&gt;
files are the stable part.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>go</category>
      <category>licensing</category>
      <category>devops</category>
    </item>
    <item>
      <title>Your sysctl says one thing, the kernel says another</title>
      <dc:creator>ricco020</dc:creator>
      <pubDate>Wed, 19 Aug 2026 05:06:12 +0000</pubDate>
      <link>https://dev.to/ricco020/your-sysctl-says-one-thing-the-kernel-says-another-4gha</link>
      <guid>https://dev.to/ricco020/your-sysctl-says-one-thing-the-kernel-says-another-4gha</guid>
      <description>&lt;p&gt;You set &lt;code&gt;net.ipv4.ip_forward=1&lt;/code&gt; in &lt;code&gt;/etc/sysctl.conf&lt;/code&gt;. You rebooted. The value is still &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Nothing is broken, and you did not make a typo. On a host running UFW, &lt;strong&gt;there is a second sysctl file, and it wins&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two files, one kernel
&lt;/h2&gt;

&lt;p&gt;UFW ships &lt;code&gt;/etc/ufw/sysctl.conf&lt;/code&gt; and loads it when the firewall comes up. It takes precedence over &lt;code&gt;/etc/sysctl.conf&lt;/code&gt;. Anything you set in the familiar file that UFW also sets is quietly overwritten on the next &lt;code&gt;ufw enable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Quietly is the operative word. There is no warning, no log line, no diff. The file you edited still contains exactly what you typed — which is why this costs hours rather than minutes: you keep re-reading a correct file.&lt;/p&gt;

&lt;p&gt;The symptom that most often brings people here: a WireGuard tunnel that comes up, a client that receives its address, and no traffic going anywhere. &lt;code&gt;ip_forward&lt;/code&gt; fell back to &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read the effective value, not the file
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sysctl net.ipv4.ip_forward          &lt;span class="c"&gt;# what the kernel applies&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/sys/net/ipv4/ip_forward   &lt;span class="c"&gt;# same thing, one file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that disagrees with what you wrote, look in &lt;code&gt;/etc/ufw/sysctl.conf&lt;/code&gt;. Note the notation there uses slashes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;net&lt;/span&gt;/&lt;span class="n"&gt;ipv4&lt;/span&gt;/&lt;span class="n"&gt;ip_forward&lt;/span&gt;=&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both &lt;code&gt;net.ipv4.ip_forward&lt;/code&gt; and &lt;code&gt;net/ipv4/ip_forward&lt;/code&gt; name the same knob. Grepping for only one of them is a good way to conclude the setting is absent when it is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small script to stop guessing
&lt;/h2&gt;

&lt;p&gt;I wrote a read-only checker that compares &lt;strong&gt;effective&lt;/strong&gt; sysctl values against hardening recommendations and, more importantly, flags the ones that are &lt;em&gt;declared somewhere and not applied&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ECART  kernel.kptr_restrict            effectif=0      attendu=2
 ECART  fs.protected_hardlinks          effectif=0      attendu=1
         declare a 1 dans /etc/sysctl.conf, mais le noyau applique 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That second line is the whole point. No dependencies, Python 3 only, changes nothing:&lt;br&gt;
&lt;a href="https://gitlab.com/ler.eric/sysctl-hardening-check" rel="noopener noreferrer"&gt;https://gitlab.com/ler.eric/sysctl-hardening-check&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  While we are here: what hardening does not cover
&lt;/h2&gt;

&lt;p&gt;Tightening sysctl narrows what a compromised process reaches &lt;em&gt;on that machine&lt;/em&gt;. It says nothing about the paths that never touch the machine — a password reused on a service that leaked, a session left open on a device you no longer carry, the one account that receives every recovery link.&lt;/p&gt;

&lt;p&gt;That distinction is worth keeping in view: &lt;a href="https://secure-os.org/articles/linux-hardening" rel="noopener noreferrer"&gt;Linux hardening, what it covers and what it does not&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One line to keep: &lt;strong&gt;the file you edited is not necessarily the file that wins.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>security</category>
      <category>devops</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>UFW and WireGuard: the tunnel is up and nothing goes through</title>
      <dc:creator>ricco020</dc:creator>
      <pubDate>Wed, 19 Aug 2026 03:37:35 +0000</pubDate>
      <link>https://dev.to/ricco020/ufw-and-wireguard-the-tunnel-is-up-and-nothing-goes-through-4lpo</link>
      <guid>https://dev.to/ricco020/ufw-and-wireguard-the-tunnel-is-up-and-nothing-goes-through-4lpo</guid>
      <description>&lt;p&gt;The tunnel comes up. &lt;code&gt;wg show&lt;/code&gt; prints a recent handshake. The client has its address inside the tunnel. And not a single byte reaches the internet.&lt;/p&gt;

&lt;p&gt;Almost every guide answers this with "open UDP 51820 in the firewall". You already did that — it is why the handshake works at all. The problem is somewhere else, and UFW makes the distinction easy to miss:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Entering a machine and traversing it are two different permissions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ufw allow 51820/udp&lt;/code&gt; lets packets &lt;em&gt;arrive&lt;/em&gt; at the server. Your clients' traffic does not stop there — it goes &lt;em&gt;through&lt;/em&gt; the box and out the public interface. That path lives in the FORWARD chain, which UFW denies by default and which no &lt;code&gt;allow&lt;/code&gt; rule touches.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four things to check, in order
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. IP forwarding — and the file that overwrites the other file
&lt;/h3&gt;

&lt;p&gt;This is the one that costs hours, because the setting &lt;em&gt;looks&lt;/em&gt; done.&lt;/p&gt;

&lt;p&gt;UFW loads &lt;strong&gt;its own sysctl file&lt;/strong&gt; at startup, and it takes precedence over the system one. A value you carefully set in &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; can be silently overwritten on the next &lt;code&gt;ufw enable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The right place is &lt;code&gt;/etc/ufw/sysctl.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;net&lt;/span&gt;/&lt;span class="n"&gt;ipv4&lt;/span&gt;/&lt;span class="n"&gt;ip_forward&lt;/span&gt;=&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;net&lt;/span&gt;/&lt;span class="n"&gt;ipv6&lt;/span&gt;/&lt;span class="n"&gt;conf&lt;/span&gt;/&lt;span class="n"&gt;default&lt;/span&gt;/&lt;span class="n"&gt;forwarding&lt;/span&gt;=&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;net&lt;/span&gt;/&lt;span class="n"&gt;ipv6&lt;/span&gt;/&lt;span class="n"&gt;conf&lt;/span&gt;/&lt;span class="n"&gt;all&lt;/span&gt;/&lt;span class="n"&gt;forwarding&lt;/span&gt;=&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then check the &lt;strong&gt;effective&lt;/strong&gt; value, not the file you just edited:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sysctl net.ipv4.ip_forward
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Forwarding, which is not the same as ingress
&lt;/h3&gt;

&lt;p&gt;Targeted, and the one to prefer:&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;ufw route allow &lt;span class="k"&gt;in &lt;/span&gt;on wg0 out on eth0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or globally, in &lt;code&gt;/etc/default/ufw&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="nv"&gt;DEFAULT_FORWARD_POLICY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ACCEPT"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second opens forwarding for &lt;em&gt;every&lt;/em&gt; interface. It is a good ten-second diagnostic and a poor permanent configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. NAT, which UFW never adds on its own
&lt;/h3&gt;

&lt;p&gt;Without it, packets leave carrying their tunnel address, which nothing on the internet knows how to answer. In &lt;code&gt;/etc/ufw/before.rules&lt;/code&gt;, &lt;strong&gt;at the very top&lt;/strong&gt;, before the &lt;code&gt;*filter&lt;/code&gt; line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
COMMIT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two classic mistakes here: putting this block &lt;em&gt;after&lt;/em&gt; &lt;code&gt;*filter&lt;/code&gt; (it is then ignored), and copying &lt;code&gt;eth0&lt;/code&gt; without checking.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The real name of the egress interface
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;eth0&lt;/code&gt; is an article convention, not a fact. Depending on the host it will be &lt;code&gt;ens3&lt;/code&gt;, &lt;code&gt;enp1s0&lt;/code&gt;, something else. The command that answers without guessing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ip route get 1.1.1.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The test that separates the two families of causes
&lt;/h2&gt;

&lt;p&gt;From a connected client, try to reach the &lt;strong&gt;server's address inside the tunnel&lt;/strong&gt; (often &lt;code&gt;10.8.0.1&lt;/code&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you observe&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;tunnel address answers, nothing external does&lt;/td&gt;
&lt;td&gt;tunnel is fine — forwarding or NAT missing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tunnel address does not answer either&lt;/td&gt;
&lt;td&gt;upstream: keys, &lt;code&gt;AllowedIPs&lt;/code&gt;, client routing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;everything answers but names do not resolve&lt;/td&gt;
&lt;td&gt;not UFW at all — it is the DNS pushed to the client&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That third row is worth knowing. "I have no internet" very often means "no name resolves". Pinging an IP directly tells you in one command.&lt;/p&gt;

&lt;h2&gt;
  
  
  After applying
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;before.rules&lt;/code&gt; is only re-read on reload:&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;ufw disable &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nb"&gt;enable
sudo &lt;/span&gt;ufw status verbose
&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;-L&lt;/span&gt; POSTROUTING &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One last thing that produces duplicate configurations: if your &lt;code&gt;PostUp&lt;/code&gt;/&lt;code&gt;PostDown&lt;/code&gt; rules in the WireGuard config already add &lt;code&gt;MASQUERADE&lt;/code&gt;, you do not need the &lt;code&gt;*nat&lt;/code&gt; block above. Both together rarely break anything, but they make the &lt;em&gt;next&lt;/em&gt; outage much harder to read — and there will be a next one.&lt;/p&gt;




&lt;p&gt;If you are still choosing a port or wondering whether 443/UDP is worth it, I wrote a longer piece on &lt;a href="https://www.vpnsmith.com/en/blog/wireguard-port" rel="noopener noreferrer"&gt;WireGuard ports, changing them and opening them properly&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One line to keep: &lt;strong&gt;an open port means packets can enter, not that they can traverse.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>wireguard</category>
      <category>linux</category>
      <category>networking</category>
      <category>devops</category>
    </item>
    <item>
      <title>Your drive says SMART PASSED and is already failing: the 5 attributes that actually predict it</title>
      <dc:creator>ricco020</dc:creator>
      <pubDate>Sat, 15 Aug 2026 15:52:24 +0000</pubDate>
      <link>https://dev.to/ricco020/your-drive-says-smart-passed-and-is-already-failing-the-5-attributes-that-actually-predict-it-6g</link>
      <guid>https://dev.to/ricco020/your-drive-says-smart-passed-and-is-already-failing-the-5-attributes-that-actually-predict-it-6g</guid>
      <description>&lt;p&gt;A drive that SMART calls &lt;strong&gt;PASSED&lt;/strong&gt; can already be losing your data. The overall&lt;br&gt;
self-assessment is a manufacturer threshold, not a statement about your files — and it stays&lt;br&gt;
green well past the point where sectors have started going unreadable.&lt;/p&gt;

&lt;p&gt;Here is the case that matters, from a real report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SMART overall verdict : PASSED

Reallocated_Sector_Ct  = 24   sectors already remapped
Current_Pending_Sector = 8    sectors unreadable RIGHT NOW
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That drive passes. It is also actively failing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five attributes worth reading
&lt;/h2&gt;

&lt;p&gt;Forget the table of forty values. Five of them describe the two families that precede&lt;br&gt;
unreadable data — reallocation and pending sectors:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ID&lt;/th&gt;
&lt;th&gt;attribute&lt;/th&gt;
&lt;th&gt;meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Reallocated_Sector_Ct&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;sectors already remapped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;197&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Current_Pending_Sector&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;unreadable now, waiting to be remapped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;198&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Offline_Uncorrectable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the drive could not read them offline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;187&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Reported_Uncorrect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;errors it could not correct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;188&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Command_Timeout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;commands that timed out&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A non-zero &lt;strong&gt;197&lt;/strong&gt; on a drive reporting PASSED is the exact situation to act on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read it in one command
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;smartctl &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-j&lt;/span&gt; /dev/sda | python3 smart_triage.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script is MIT, has no dependencies and no network access — it reads stdin and prints a&lt;br&gt;
verdict, with exit codes &lt;code&gt;0&lt;/code&gt; healthy, &lt;code&gt;1&lt;/code&gt; monitor, &lt;code&gt;2&lt;/code&gt; back up now, &lt;code&gt;3&lt;/code&gt; stop and image:&lt;br&gt;
&lt;a href="https://gitlab.com/ler.eric/smart-triage" rel="noopener noreferrer"&gt;https://gitlab.com/ler.eric/smart-triage&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The order of operations, which decides whether you keep the data
&lt;/h2&gt;

&lt;p&gt;If the verdict is &lt;strong&gt;stop and image&lt;/strong&gt;, the sequence is not negotiable:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stop using the drive.&lt;/strong&gt; Every hour of use turns more pending sectors into lost ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image the whole device to a file&lt;/strong&gt; with &lt;code&gt;ddrescue&lt;/code&gt; — not a file copy, a block-level image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Work on the copy.&lt;/strong&gt; Never on the original.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The mistake that destroys recoverable data is running a filesystem repair tool first. &lt;code&gt;fsck&lt;/code&gt;&lt;br&gt;
and &lt;code&gt;chkdsk&lt;/code&gt; &lt;strong&gt;write&lt;/strong&gt; to the disk. Writing to a drive with pending sectors is how a&lt;br&gt;
recoverable image becomes an unrecoverable one — and it is irreversible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What SMART does not tell you
&lt;/h2&gt;

&lt;p&gt;SMART predicts a meaningful share of failures, not all of them. Controller failures, firmware&lt;br&gt;
faults and head crashes can arrive with every attribute still green. A clean report is a reason&lt;br&gt;
to keep monitoring, never a reason to skip backups.&lt;/p&gt;

&lt;p&gt;If you want the detail of what is realistically recoverable per failure mode — mechanical,&lt;br&gt;
electronic, logical, firmware — this breakdown covers it:&lt;br&gt;
&lt;a href="https://www.save-my-disk.com/en/blog/hard-drive-failure" rel="noopener noreferrer"&gt;https://www.save-my-disk.com/en/blog/hard-drive-failure&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>sysadmin</category>
      <category>hardware</category>
      <category>devops</category>
    </item>
    <item>
      <title>A failed deployment is invisible from the web</title>
      <dc:creator>ricco020</dc:creator>
      <pubDate>Fri, 14 Aug 2026 06:27:43 +0000</pubDate>
      <link>https://dev.to/ricco020/a-failed-deployment-is-invisible-from-the-web-3kc9</link>
      <guid>https://dev.to/ricco020/a-failed-deployment-is-invisible-from-the-web-3kc9</guid>
      <description>&lt;p&gt;Last night a deployment of mine failed for 21 hours and every monitoring check stayed green. The site answered &lt;code&gt;200&lt;/code&gt; the whole time. Here is why that happens, and the two-line check that catches it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The failure mode
&lt;/h2&gt;

&lt;p&gt;When a build fails on Vercel (and on Railway, and on most platforms with atomic deploys), the platform does the sensible thing: it keeps serving the &lt;strong&gt;previous&lt;/strong&gt; successful build. Your users see a working site. Your uptime monitor sees &lt;code&gt;200 OK&lt;/code&gt;. Your health check passes.&lt;/p&gt;

&lt;p&gt;What nobody sees is that &lt;strong&gt;the commit you pushed is not the code being served&lt;/strong&gt;. Every article, fix and config change since the last green build is sitting in git, live nowhere.&lt;/p&gt;

&lt;p&gt;That is a good default — far better than serving a broken build. But it means "the site is up" and "my work is deployed" are two different questions, and most monitoring only answers the first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The check that actually answers it
&lt;/h2&gt;

&lt;p&gt;Compare the timestamp of your last commit with the timestamp of the last &lt;strong&gt;successful&lt;/strong&gt; deployment. If the commit is newer by more than a build's duration, something is wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;%ct                 &lt;span class="c"&gt;# last commit, unix time&lt;/span&gt;
npx vercel &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--prod&lt;/span&gt;                    &lt;span class="c"&gt;# last deployments + their state&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the age of the newest &lt;code&gt;Ready&lt;/code&gt; deployment is older than your last commit, you are serving stale code. In my case the newest &lt;code&gt;Ready&lt;/code&gt; was 21 hours old, and two &lt;code&gt;Error&lt;/code&gt; deployments sat above it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap that cost me an hour
&lt;/h2&gt;

&lt;p&gt;Here is the part worth the read. Outside a TTY, &lt;code&gt;vercel ls&lt;/code&gt; &lt;strong&gt;splits its output across two streams&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;stdout&lt;/strong&gt; gets the deployment URLs, one per line — no status&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;stderr&lt;/strong&gt; gets the formatted table, including the &lt;code&gt;● Ready&lt;/code&gt; / &lt;code&gt;● Error&lt;/code&gt; column&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So this looks correct and silently returns nothing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;npx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vercel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--prod&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# always empty
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your script reports zero errors on a project that is entirely broken. The fix is to read both streams:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The general lesson is worth more than the specific fix: &lt;strong&gt;an empty result and a clean result are not the same thing&lt;/strong&gt;. If a check can return "nothing found" both when everything is fine and when the parsing broke, it will eventually lie to you — and it will lie in the reassuring direction. Make the two cases distinguishable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NOT VERIFIED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;     &lt;span class="c1"&gt;# not "0 errors"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What actually broke the build
&lt;/h2&gt;

&lt;p&gt;For the curious: a TypeScript error on a component prop. One call site out of 21 was missing a required &lt;code&gt;id&lt;/code&gt;. Nothing exotic — but because the previous build kept serving, nothing surfaced it either.&lt;/p&gt;

&lt;p&gt;I found the second occurrence only after the first fix failed, because I had listed the call sites with &lt;code&gt;grep ... | head -8&lt;/code&gt; on a file with 21 of them. &lt;strong&gt;A &lt;code&gt;head&lt;/code&gt; on a search meant to be exhaustive turns "I saw nothing" into "there is nothing".&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things to take away
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Uptime monitoring does not detect failed deployments. Compare commit time to last successful deploy time.&lt;/li&gt;
&lt;li&gt;Check the &lt;strong&gt;state&lt;/strong&gt; of recent deployments, not just the age of the last good one.&lt;/li&gt;
&lt;li&gt;When a check returns nothing, make sure it can tell you &lt;em&gt;why&lt;/em&gt; — no data and no problem must not look identical.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are wiring up this kind of monitoring, the same reasoning applies to the telemetry you collect about it — &lt;a href="https://coldwa.st/e/blog/2026-08-14-what-is-opentelemetry.html" rel="noopener noreferrer"&gt;what OpenTelemetry actually is, and what it deliberately does not give you&lt;/a&gt; covers the difference between producing telemetry and having observability, which is the trap one layer up.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>monitoring</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>You don't need to restart WireGuard to add a peer</title>
      <dc:creator>ricco020</dc:creator>
      <pubDate>Thu, 13 Aug 2026 15:09:57 +0000</pubDate>
      <link>https://dev.to/ricco020/you-dont-need-to-restart-wireguard-to-add-a-peer-2b3l</link>
      <guid>https://dev.to/ricco020/you-dont-need-to-restart-wireguard-to-add-a-peer-2b3l</guid>
      <description>&lt;p&gt;Most guides answer "how do I add a WireGuard peer" with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wg-quick down wg0 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; wg-quick up wg0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That works. It also tears down &lt;strong&gt;every&lt;/strong&gt; active tunnel on the box to add one line to a file. On a server anyone actually depends on, that is the reason people batch peer additions for weeks instead of doing them when asked.&lt;/p&gt;

&lt;p&gt;There is a command that does it live.&lt;/p&gt;

&lt;h2&gt;
  
  
  wg set
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wg &lt;span class="nb"&gt;set &lt;/span&gt;wg0 peer &amp;lt;CLIENT_PUBLIC_KEY&amp;gt; allowed-ips 10.0.0.5/32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The peer exists from that instant. Existing sessions are untouched — &lt;code&gt;wg set&lt;/code&gt; edits the running interface through the kernel module rather than rebuilding it.&lt;/p&gt;

&lt;p&gt;Nothing is printed on success, which throws people. &lt;code&gt;wg show wg0&lt;/code&gt; is what confirms it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The catch nobody mentions
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;wg set&lt;/code&gt; changes the &lt;em&gt;running&lt;/em&gt; state, not &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt;. Reboot and your peer is gone.&lt;/p&gt;

&lt;p&gt;You can write the live state back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wg showconf wg0 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/wireguard/wg0.conf
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 /etc/wireguard/wg0.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But note what &lt;code&gt;showconf&lt;/code&gt; actually prints: the interface's &lt;strong&gt;private key&lt;/strong&gt;, along with everything else. If your umask leaves that file world-readable, you have just published it to every account on the machine. The &lt;code&gt;chmod&lt;/code&gt; is not optional politeness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reloading an edited file without dropping anyone
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wg syncconf wg0 &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;wg-quick strip wg0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;syncconf&lt;/code&gt; applies the &lt;em&gt;difference&lt;/em&gt;. &lt;code&gt;wg-quick down&lt;/code&gt;/&lt;code&gt;up&lt;/code&gt; rebuilds from scratch — the thing you were trying to avoid in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  The failure that looks like magic
&lt;/h2&gt;

&lt;p&gt;Two peers sharing an &lt;code&gt;AllowedIPs&lt;/code&gt; address is the bug that wastes an afternoon, because the symptom is backwards: you add a new client, and the &lt;strong&gt;older&lt;/strong&gt; one stops working. &lt;code&gt;AllowedIPs&lt;/code&gt; is what decides which peer a packet belongs to, so a duplicate makes that decision ambiguous.&lt;/p&gt;

&lt;p&gt;One &lt;code&gt;/32&lt;/code&gt; per client, never overlapping.&lt;/p&gt;

&lt;p&gt;I wrote a small tool that reads a folder of client configs and flags exactly that — duplicate addresses, reused private keys, and &lt;code&gt;AllowedIPs&lt;/code&gt; ranges wide enough to swallow the local network: &lt;a href="https://gitlab.com/ler.eric/wg-clients-audit" rel="noopener noreferrer"&gt;wg-clients-audit&lt;/a&gt;. Deliberately silent on &lt;code&gt;0.0.0.0/0&lt;/code&gt;, since full-tunnel is a choice, not a mistake.&lt;/p&gt;

&lt;p&gt;The longer version, with the client-side config and the three reasons a new peer fails to connect, is here: &lt;a href="https://www.vpnsmith.com/en/blog/wireguard-add-peer" rel="noopener noreferrer"&gt;WireGuard add peer without restarting the tunnel&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>wireguard</category>
      <category>linux</category>
      <category>networking</category>
      <category>vpn</category>
    </item>
    <item>
      <title>What is an API? A clear, practical explainer</title>
      <dc:creator>ricco020</dc:creator>
      <pubDate>Wed, 24 Jun 2026 12:25:36 +0000</pubDate>
      <link>https://dev.to/ricco020/what-is-an-api-a-clear-practical-explainer-4mkh</link>
      <guid>https://dev.to/ricco020/what-is-an-api-a-clear-practical-explainer-4mkh</guid>
      <description>&lt;p&gt;An API (Application Programming Interface) is a contract that lets one piece of software ask another for data or actions, without knowing how it works inside. What an API is, how a request and response work, the common styles like REST and GraphQL, and why developers build on them.&lt;/p&gt;

&lt;p&gt;An API — short for Application Programming Interface — is a set of rules that lets one program talk to another. It defines how you ask for data or an action, what you have to send, and what you get back, so two pieces of software can work together without either one knowing how the other is built inside. It is the contract between them.&lt;/p&gt;

&lt;p&gt;A common analogy is a restaurant menu. The menu lists what you can order and what each dish costs, you place an order, and the kitchen prepares it — but you never need to see the kitchen or know how the cooking is done. An API is that menu for software: it tells you what you can request and how, and hides everything behind it.&lt;/p&gt;

&lt;p&gt;More precisely, an API exposes a set of operations a service is willing to perform. A weather service might offer an operation that returns the forecast for a city; a payment service might offer one that charges a card. You call the operation with the inputs it expects, and the service does the work and hands back a result. The internals stay private and can change freely, as long as the contract holds.&lt;/p&gt;

&lt;p&gt;On the web, most APIs work over the same protocol as web pages: HTTP. Your program sends a request to a URL called an endpoint, usually with a method that signals intent — GET to read data, POST to create it, PUT or PATCH to update, DELETE to remove. The request can carry parameters and a body; the server processes it and sends back a response.&lt;/p&gt;

&lt;p&gt;That response has two important parts: a status code and a payload. The status code is a short number that says how it went — 200 means success, 404 means the thing was not found, 500 means the server hit an error. The payload is the data itself, today almost always formatted as JSON, a lightweight text format that is easy for programs to read and write.&lt;/p&gt;

&lt;p&gt;APIs come in a few common styles. REST is the most widespread: it organises everything around resources addressed by URLs and leans on the standard HTTP methods. GraphQL takes a different approach, letting the client ask for exactly the fields it needs in a single query, which avoids over-fetching. Older systems may use SOAP, and real-time apps often add WebSockets for a continuous two-way connection.&lt;/p&gt;

&lt;p&gt;Many APIs are protected, because they expose real data and actions. Authentication proves who is calling — often with an API key or a token such as OAuth — and rate limiting caps how many requests a caller can make in a window, so one client cannot overwhelm the service. Public APIs publish documentation describing every endpoint, its inputs, and its responses.&lt;/p&gt;

&lt;p&gt;Developers rely on APIs because they let you build on top of existing services instead of reinventing them. You can add maps, payments, email, login, or AI to an app by calling someone else’s API. They also let large systems split into smaller services that talk to each other, and they are how a mobile app or single-page front end fetches data from a backend.&lt;/p&gt;

&lt;p&gt;Developers rely on APIs because they let you build on top of existing services instead of reinventing them. You can add maps, payments, email, login, or AI to an app by calling someone else’s API. They also let large systems split into smaller services that talk to each other, and they are how a mobile app or single-page front end fetches data from a backend.&lt;/p&gt;

&lt;p&gt;Finally, an API has to run somewhere. The service behind it — the backend that receives requests and returns responses — needs a reliable, always-on server. For your own APIs, that means a host you control: a VPS or cloud server where the backend stays up and reachable. A solid host is the foundation the whole interface sits on.&lt;/p&gt;

&lt;p&gt;Self-hosting needs a reliable home with full runtime and network control. Infomaniak — a Swiss, privacy-respecting provider — offers VPS and cloud servers to host your CMS or app.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://versiondude.net/articles/what-is-an-api" rel="noopener noreferrer"&gt;versiondude.net&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>What Is a Higher-Order Function? Functions as Values, Explained (2026)</title>
      <dc:creator>ricco020</dc:creator>
      <pubDate>Wed, 24 Jun 2026 08:37:13 +0000</pubDate>
      <link>https://dev.to/ricco020/what-is-a-higher-order-function-functions-as-values-explained-2026-4kcp</link>
      <guid>https://dev.to/ricco020/what-is-a-higher-order-function-functions-as-values-explained-2026-4kcp</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fywjuoqqi7wk2nvl8m1xa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fywjuoqqi7wk2nvl8m1xa.jpg" alt="A code editor open on a laptop: higher-order functions are an everyday tool you write here — functions that take other functions as arguments, like map and filter." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In most languages a function is something you call. In functional programming a function is also a value — you can store it in a variable, pass it into another function, and get one back as a result. A &lt;strong&gt;higher-order function&lt;/strong&gt; is exactly the function that does one of those last two things. This guide explains what a higher-order function is, why &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt; and &lt;code&gt;fold&lt;/code&gt; are the classic examples, how they replace loops, and how they look in &lt;a href="https://coldwa.st/e/blog/2026-06-14-what-is-haskell.html" rel="noopener noreferrer"&gt;Haskell&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short definition
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A higher-order function is a function that takes one or more functions as arguments, returns a function as its result, or both.&lt;/strong&gt; A function that does neither — it only takes and returns plain values like numbers or strings — is called a first-order function. The "higher-order" name simply means it operates on functions the way an ordinary function operates on data.&lt;/p&gt;

&lt;p&gt;This only works in a language where functions are &lt;strong&gt;first-class values&lt;/strong&gt;: things you can name, pass around and return, just like an integer. Haskell, JavaScript, Python, Swift and many others treat functions this way, which is what makes higher-order functions possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three classics: map, filter and fold
&lt;/h2&gt;

&lt;p&gt;Almost every functional codebase leans on three higher-order functions. Each one takes a function as an argument and applies it across a collection:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- **map** applies a function to every element of a list and gives back a new list of the results. `map (+1) [1,2,3]` gives `[2,3,4]` — the function `(+1)` is the argument.

- **filter** keeps only the elements for which a function returns true. `filter even [1,2,3,4]` gives `[2,4]` — here `even` is the function you pass in.

- **fold** (also called reduce) collapses a list down to a single value by combining elements two at a time. `foldr (+) 0 [1,2,3]` adds them up to `6` — the combining step `(+)` is the function argument.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In each case you supply the small piece of logic — increment, "is even", add — and the higher-order function handles the walking-over-the-list part. You describe what to do to each element, not the bookkeeping of how to step through them.&lt;/p&gt;

&lt;h2&gt;
  
  
  How they replace loops
&lt;/h2&gt;

&lt;p&gt;In an imperative language you might write a loop with a counter, an accumulator, and an explicit index. The same intent expressed with a higher-order function disappears into a single line, because the iteration is already baked into &lt;code&gt;map&lt;/code&gt; or &lt;code&gt;fold&lt;/code&gt;. That is why purely functional code, which avoids mutable loop counters, leans so heavily on these functions and on &lt;a href="https://coldwa.st/e/blog/2026-06-16-what-is-recursion.html" rel="noopener noreferrer"&gt;recursion&lt;/a&gt; — the two cover the work that loops do elsewhere. &lt;a href="https://coldwa.st/e/blog/2026-06-14-haskell-list-comprehensions.html" rel="noopener noreferrer"&gt;List comprehensions&lt;/a&gt; are often a readable shorthand for the same &lt;code&gt;map&lt;/code&gt;-and-&lt;code&gt;filter&lt;/code&gt; combination.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F955qi6y2uw09tfpdk0n5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F955qi6y2uw09tfpdk0n5.jpg" alt="A higher-order function in another language: here a closure (the block in braces) is passed as the completionHandler argument to document.open — a function handed to another function, exactly the idea behind map and filter." width="800" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Returning a function: where it gets powerful
&lt;/h2&gt;

&lt;p&gt;The other half of the definition — functions that return functions — is just as common. A function can build and hand back a new, specialised function. A classic example is a "multiplier maker": you give it a number and it returns a function that multiplies its input by that number. Call it with &lt;code&gt;3&lt;/code&gt; and you get back a "times three" function you can then use like any other. The maker is higher-order because its result is itself a function.&lt;/p&gt;

&lt;p&gt;In Haskell this is woven into the language through &lt;strong&gt;currying&lt;/strong&gt;: every function of several arguments is really a chain of one-argument functions, each returning the next. That is why &lt;code&gt;map (+1)&lt;/code&gt; works — &lt;code&gt;(+1)&lt;/code&gt; is the addition function with one argument already supplied, returning a function that still expects the other. Partial application like this is higher-order functions at work, often without you noticing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Higher-order functions in Haskell
&lt;/h2&gt;

&lt;p&gt;Haskell makes the idea explicit in its &lt;strong&gt;type signatures&lt;/strong&gt;. The type of &lt;code&gt;map&lt;/code&gt; is written &lt;code&gt;map :: (a -&amp;gt; b) -&amp;gt; [a] -&amp;gt; [b]&lt;/code&gt;. Read it left to right: the first argument &lt;code&gt;(a -&amp;gt; b)&lt;/code&gt; is itself a function — that is the higher-order part — followed by a list of &lt;code&gt;a&lt;/code&gt;, producing a list of &lt;code&gt;b&lt;/code&gt;. The arrows make it visible on the page that a function is being passed in. The same pattern appears in &lt;code&gt;filter :: (a -&amp;gt; Bool) -&amp;gt; [a] -&amp;gt; [a]&lt;/code&gt; and across the standard library.&lt;/p&gt;

&lt;p&gt;You often pass these functions inline as &lt;strong&gt;lambdas&lt;/strong&gt; — small anonymous functions written with a backslash, like &lt;code&gt;map (\x -&amp;gt; x * x) [1,2,3]&lt;/code&gt; to square each element. Whether you pass a named function, an operator section like &lt;code&gt;(*2)&lt;/code&gt;, or a lambda, it is the same mechanism: a function travelling as a value into a higher-order function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why they matter
&lt;/h2&gt;

&lt;p&gt;Higher-order functions let you factor out the common shape of a computation — "do something to every element", "keep the ones that match", "combine them all" — and reuse it with different logic plugged in. That means less repeated boilerplate, code that reads closer to its intent, and small, testable pieces of logic you can compose. They are the building block on top of which much of &lt;a href="https://coldwa.st/e/blog/2026-06-15-what-is-functional-programming.html" rel="noopener noreferrer"&gt;functional programming&lt;/a&gt; is expressed, and combined with Haskell's &lt;a href="https://coldwa.st/e/blog/2026-06-14-lazy-evaluation-haskell.html" rel="noopener noreferrer"&gt;lazy evaluation&lt;/a&gt; they even let you map and filter over lists that are conceptually infinite.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest trade-offs
&lt;/h2&gt;

&lt;p&gt;Higher-order functions take some getting used to: reading &lt;code&gt;foldr&lt;/code&gt; or a chain of &lt;code&gt;map . filter&lt;/code&gt; is a skill, and deeply nested lambdas can become hard to follow. Passing functions around can also make a stack trace less obvious when something goes wrong. The payoff — far less repetitive iteration code and logic you can recombine — is why they have spread well beyond functional languages into everyday JavaScript, Python and Swift. Used with restraint, they make code shorter and clearer; overused, they can obscure it like any other tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;p&gt;What is a higher-order function in simple terms?&lt;br&gt;
A higher-order function is a function that takes another function as an argument, returns a function as its result, or both. Instead of working only on plain data like numbers and strings, it works on functions. Classic examples are map, filter and fold, which each take a small function and apply it across a list.&lt;/p&gt;

&lt;p&gt;Is map a higher-order function?&lt;br&gt;
Yes. map takes a function as its first argument and applies it to every element of a list, returning a new list of the results. Because one of its arguments is itself a function, map is a textbook higher-order function — and so are filter and fold for the same reason.&lt;/p&gt;

&lt;p&gt;What is the difference between a higher-order and a first-order function?&lt;br&gt;
A first-order function only takes and returns ordinary values, such as numbers or strings. A higher-order function takes one or more functions as arguments, or returns a function, or both. The difference is whether the function operates on data alone or also on other functions.&lt;/p&gt;

&lt;p&gt;Do higher-order functions only exist in Haskell?&lt;br&gt;
No. They exist in any language that treats functions as first-class values — values you can store, pass and return. Haskell, JavaScript, Python, Swift and many others all support them. Haskell makes the idea especially visible through its type signatures and currying, but the concept is widespread.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Recommended
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  A Linux box to build and run your Haskell code
&lt;/h3&gt;

&lt;p&gt;Trying these higher-order functions for real — loading them into GHCi, building a project with map and fold, then running it — is smoother on a proper Linux machine than a laptop. A cloud server gives you full control to install GHC and a Haskell toolchain in a clean environment and reach it over &lt;a href="https://coldwa.st/e/blog/2026-06-22-what-is-ssh.html" rel="noopener noreferrer"&gt;SSH&lt;/a&gt;. Infomaniak — a Swiss, privacy-respecting provider — offers cloud servers for exactly that.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[See Infomaniak Cloud →](https://coldwa.st/go/cloud.html)
Affiliate link — it supports these free guides.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Browse more clear explainers in our &lt;a href="https://coldwa.st/e/" rel="noopener noreferrer"&gt;guides index&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://coldwa.st/e/blog/2026-06-24-higher-order-functions.html" rel="noopener noreferrer"&gt;coldwa.st&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>haskell</category>
      <category>functional</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>MCP Security: The Risks of Model Context Protocol and How to Govern It (2026)</title>
      <dc:creator>ricco020</dc:creator>
      <pubDate>Wed, 24 Jun 2026 07:31:23 +0000</pubDate>
      <link>https://dev.to/ricco020/mcp-security-the-risks-of-model-context-protocol-and-how-to-govern-it-2026-1963</link>
      <guid>https://dev.to/ricco020/mcp-security-the-risks-of-model-context-protocol-and-how-to-govern-it-2026-1963</guid>
      <description>&lt;p&gt;The &lt;a href="https://dev.to/ai/model-context-protocol"&gt;Model Context Protocol (MCP)&lt;/a&gt; is the open standard that lets an &lt;a href="https://dev.to/ai/what-is-an-ai-agent"&gt;AI agent&lt;/a&gt; plug into your tools, files, and apps through one common interface — often described as "USB-C for AI." It is genuinely useful, and through 2025 and 2026 it has been adopted across AI assistants, IDEs, and agent frameworks. But the same connector that makes an agent powerful is also its biggest attack surface. Recent moves toward governing AI agents in the enterprise — security vendors shipping tools to monitor coding agents, and MCP-based governance layers landing inside Claude, ChatGPT, and Copilot — are a sign of the same thing: connecting an agent to your environment is a security decision, not a convenience setting. Here is the honest picture of MCP security in 2026 and how to govern it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why MCP is a security problem, not just a feature
&lt;/h2&gt;

&lt;p&gt;MCP itself is just plumbing: a standard way for a model to discover tools, read their descriptions, and call them. The risk isn't the protocol — it's what flows through it.&lt;/p&gt;

&lt;p&gt;When an agent connects to an MCP server, that server provides two things the model trusts: &lt;strong&gt;tool descriptions&lt;/strong&gt; (text telling the model what each tool does and how to call it) and &lt;strong&gt;tool outputs&lt;/strong&gt; (whatever the tool returns). The model reads both and acts on them. So every MCP server you attach is effectively code and instructions running with your agent's privileges. Whatever the agent can reach — your files, a repository, an API, your email — a malicious server can try to reach &lt;em&gt;through&lt;/em&gt; the agent.&lt;/p&gt;

&lt;p&gt;This is the same shift that makes &lt;a href="https://dev.to/ai/ai-agent-security-2026"&gt;AI agent security&lt;/a&gt; hard in general, applied to a specific connector: the security of your MCP setup is the security of every server you plug into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The MCP-specific risks in 2026
&lt;/h2&gt;

&lt;p&gt;These aren't hypothetical — security researchers have documented them on real MCP clients.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tool poisoning.&lt;/strong&gt; A malicious server hides instructions inside a tool's &lt;em&gt;description&lt;/em&gt; — text the model reads but the user usually doesn't. A tool that looks like a harmless &lt;code&gt;add(a, b)&lt;/code&gt; can secretly instruct the agent to read private files and exfiltrate them. Because users tend to approve tool calls without inspecting the description, this is one of the most impactful MCP-specific attacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rug pulls (silent redefinition).&lt;/strong&gt; An MCP tool changes its own definition &lt;em&gt;after&lt;/em&gt; you've installed and approved it. You vetted something safe; the server later swaps in malicious behaviour without telling you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool shadowing and cross-server attacks.&lt;/strong&gt; When several servers connect to the same agent, a compromised one can override or intercept calls meant for a trusted tool — a "confused deputy" problem where the agent does the attacker's bidding while thinking it's using a legitimate tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The exfiltration trifecta.&lt;/strong&gt; The genuinely dangerous combination is an agent that has &lt;em&gt;private data&lt;/em&gt;, reads &lt;em&gt;untrusted content&lt;/em&gt;, and has an &lt;em&gt;exfiltration path&lt;/em&gt; to the outside. MCP makes all three easy to wire together by accident.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indirect &lt;a href="https://dev.to/ai/prompt-injection"&gt;prompt injection&lt;/a&gt;.&lt;/strong&gt; Even an honest server returns outputs the agent reads — a web page, an issue, a document — that may contain hidden instructions. The agent can obey them as if they came from you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="/images/mcp-security-body.jpg" class="article-body-image-wrapper"&gt;&lt;img src="/images/mcp-security-body.jpg" alt="A close-up of a laptop's side showing a USB port and an SD card slot, illustrating MCP as a universal connector for plugging tools into an AI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to govern MCP safely
&lt;/h2&gt;

&lt;p&gt;You don't need to avoid MCP. You need to govern what you connect and box it in so a single bad server can't become a disaster. The principles are old security wisdom applied to a new connector.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vet and pin trusted servers.&lt;/strong&gt; Prefer official or well-reviewed MCP servers. Don't attach arbitrary third-party servers to an agent that holds real access, and watch for tool definitions that change after install.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Least privilege per server.&lt;/strong&gt; Give each server only the access its job needs, using &lt;strong&gt;scoped, revocable credentials&lt;/strong&gt; — never your primary accounts or production keys. If a server only needs to read, don't let it write.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit the blast radius.&lt;/strong&gt; Avoid connecting many untrusted servers to the same agent, since one compromised server can intercept others. Isolate sensitive work from anything that reads the open web.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human in the loop for high-impact actions.&lt;/strong&gt; Require explicit confirmation before anything irreversible — sending money, deleting data, posting publicly, changing access. Let the agent draft; you approve.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat tool descriptions and outputs as untrusted.&lt;/strong&gt; Both can carry injected instructions. The same caution applies when an agent uses &lt;a href="https://dev.to/ai/ai-code-review-tools"&gt;AI code review tools&lt;/a&gt; or any tool that ingests external content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log and audit tool calls.&lt;/strong&gt; Keep a record of which servers and tools the agent used, so you can spot anomalies and revoke fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep secrets out of prompts and tool arguments.&lt;/strong&gt; Passwords and API keys pasted into a prompt or a tool call become text on a server. Use scoped tokens and secret managers instead.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The honest takeaway
&lt;/h2&gt;

&lt;p&gt;MCP security comes down to one mindset shift: an MCP server is not a plugin you install and forget — it's a new participant with autonomy and access, and you should treat it like one you don't fully trust. The protocol is open and useful; the danger is in granting broad, standing trust to servers you haven't vetted. Connect deliberately, scope every server tightly, keep a human gate on anything irreversible, and assume every tool description and output could be trying to hijack your agent. The teams now building governance around AI agents — and the &lt;a href="https://dev.to/ai/ai-coding-agent"&gt;AI coding agents&lt;/a&gt; that lean on MCP most — are converging on exactly that: connect less, trust narrowly, and verify.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is MCP security?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MCP security is the practice of safely connecting AI models and agents to external tools and data through the Model Context Protocol — an open standard introduced by Anthropic in late 2024, often described as 'USB-C for AI'. MCP itself is just a connector: the security question is what you plug into it and how much you trust it. Each MCP server an agent connects to is code and instructions running with the agent's access, so a malicious or compromised server can read your data, call other tools, or take actions on your behalf. MCP security means vetting servers, scoping permissions tightly, and treating tool descriptions and outputs as untrusted input.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is tool poisoning in MCP?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tool poisoning is when a malicious MCP server hides instructions inside a tool's description or metadata — text the model reads but the user usually doesn't. The model treats those hidden instructions as commands, so a tool that looks like a harmless 'add two numbers' function can secretly tell the agent to read private files and send them somewhere. Security researchers have documented this as one of the most impactful MCP-specific risks, because users tend to approve tool calls without inspecting the underlying descriptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is an MCP rug pull?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A rug pull, also called silent redefinition, is when an MCP tool changes its own definition after you've already installed and trusted it. You approve a tool that looks safe, and later the server quietly swaps in malicious instructions without notifying you. A related attack is tool shadowing, where a malicious server overrides or intercepts calls meant for a trusted tool. Both exploit the fact that trust granted once is rarely re-checked, which is why monitoring tool definitions for changes matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is MCP safe to use?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MCP is broadly safe for everyday use if you connect only to servers you trust and scope their access tightly, but it is not safe to wire up arbitrary third-party servers with broad permissions and walk away. The protocol is an open connector, so its safety depends entirely on the servers you attach and the access you grant them. Use official or well-reviewed servers, give each one separate revocable credentials instead of your main accounts, keep a human in the loop for high-impact actions, and review what tools can do before approving them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I secure MCP servers?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Apply least privilege: give each MCP server only the access its job needs, using scoped, revocable tokens rather than admin keys or your primary accounts. Vet and pin trusted servers, prefer official ones, and watch for tool definitions that change after install. Treat tool descriptions and tool outputs as untrusted content that may contain injected instructions. Avoid connecting many untrusted servers to the same agent, since one compromised server can intercept others. Log tool calls so you can audit and revoke, and keep secrets out of prompts and tool arguments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://alexi.sh/ai/mcp-security-2026" rel="noopener noreferrer"&gt;alexi.sh&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>llm</category>
      <category>programming</category>
    </item>
    <item>
      <title>What Is a Vector Database? A Plain-English Guide (2026)</title>
      <dc:creator>ricco020</dc:creator>
      <pubDate>Mon, 22 Jun 2026 05:27:58 +0000</pubDate>
      <link>https://dev.to/ricco020/what-is-a-vector-database-a-plain-english-guide-2026-29c</link>
      <guid>https://dev.to/ricco020/what-is-a-vector-database-a-plain-english-guide-2026-29c</guid>
      <description>&lt;p&gt;If you have read about RAG, AI search or recommendations, you have probably hit the term &lt;strong&gt;vector database&lt;/strong&gt;. Here is the plain version. A vector database stores data as &lt;em&gt;vectors&lt;/em&gt; — lists of numbers that capture meaning — and finds items by &lt;strong&gt;similarity&lt;/strong&gt;, not by exact match. That one idea is what makes modern AI search feel like it understands you.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a vector database actually is
&lt;/h2&gt;

&lt;p&gt;Normal databases are great at exact questions: find the user with this ID, or every order from last week. They struggle with "find me things that &lt;em&gt;mean&lt;/em&gt; the same thing." A vector database is built for exactly that.&lt;/p&gt;

&lt;p&gt;It works on &lt;a href="https://alexi.sh/ai/what-is-an-embedding" rel="noopener noreferrer"&gt;embeddings&lt;/a&gt; — the numeric fingerprints an AI model gives to text, images or audio. Items with similar meaning get vectors that sit close together. The database stores those vectors and, when you search, returns the ones nearest to your query.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw2gwqub1f3jdtb4hj1az.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw2gwqub1f3jdtb4hj1az.jpg" alt="A vector database keeps millions of embeddings on disk and in memory, and searches them by similarity in milliseconds." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How similarity search works
&lt;/h2&gt;

&lt;p&gt;The flow has three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Embed.&lt;/strong&gt; An embedding model turns each document, image or sentence into a vector.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index.&lt;/strong&gt; The database stores those vectors in a special index (like HNSW or IVF) so it can search huge sets fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query.&lt;/strong&gt; Your search is embedded too. The database returns the vectors closest to it by distance.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So a search for "how to reset my password" can surface an article called "recover a forgotten login." The words differ, but the meaning — and the vectors — are close.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vector database vs a normal database
&lt;/h2&gt;

&lt;p&gt;They solve different problems, and most real apps use both. A relational database holds your structured records and answers exact queries. A vector database answers "what is most like this?" You keep customer rows in one and searchable meaning in the other. Tools like pgvector even let you add vector search to a normal PostgreSQL database, so both live in one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters for AI
&lt;/h2&gt;

&lt;p&gt;A vector database is the retrieval engine behind a lot of AI. It powers semantic search, product and content recommendations, and — most importantly — the &lt;em&gt;retrieval&lt;/em&gt; step in &lt;a href="https://alexi.sh/ai/what-is-rag" rel="noopener noreferrer"&gt;RAG&lt;/a&gt;, where an assistant fetches relevant text before answering. Without fast similarity search over embeddings, none of those features would be practical at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;A vector database stores meaning as vectors and finds items by similarity instead of exact match. It does not replace your normal database — it sits beside it and answers the questions a keyword search never could. If you are building anything with semantic search or RAG, a vector database is the piece doing the heavy lifting.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is a vector database in simple terms?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A vector database stores data as vectors — long lists of numbers called embeddings that capture meaning. Instead of matching exact words, it finds items whose vectors are closest to your query's vector. So a search for 'how to reset my password' can return a help article titled 'recover a forgotten login', because they mean the same thing. It is the engine behind semantic search, recommendations, and the retrieval step in most AI assistants.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How is a vector database different from a normal database?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A normal (relational) database is built for exact, structured queries: find the row where id = 42, or where country = 'France'. A vector database is built for similarity: find the items most like this one. It does not look for an exact match — it ranks results by how close their vectors are. The two are complementary. Many apps use a normal database for records and a vector database for meaning-based search.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does similarity search actually work?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three steps. First, an embedding model turns each item (a document, image, or sentence) into a vector. Second, the vector database stores those vectors in a special index (such as HNSW or IVF) that makes nearest-neighbour search fast, even over millions of items. Third, when a query comes in, it is embedded too, and the database returns the vectors closest to it by distance. You get the most similar items back in milliseconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which vector databases are popular in 2026?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Common options include Pinecone, Weaviate, Qdrant, Milvus, and Chroma, plus pgvector, which adds vector search to PostgreSQL so you can keep everything in one database. The right choice depends on scale, whether you want a managed service or to self-host, and whether you need vectors alongside your existing relational data. For small projects, pgvector or Chroma are easy starting points.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://alexi.sh/ai/what-is-a-vector-database" rel="noopener noreferrer"&gt;alexi.sh&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>What Is a Home Server? A Plain-English Guide (2026)</title>
      <dc:creator>ricco020</dc:creator>
      <pubDate>Sun, 21 Jun 2026 13:38:09 +0000</pubDate>
      <link>https://dev.to/ricco020/what-is-a-home-server-a-plain-english-guide-2026-5085</link>
      <guid>https://dev.to/ricco020/what-is-a-home-server-a-plain-english-guide-2026-5085</guid>
      <description>&lt;p&gt;There is a running joke online: someone sets out to build a "home server," and a year later they have a rack of machines, a labelled network, and a power bill to match — they accidentally built a tiny data center. It is funny because it is true, but it hides a simpler fact. A home server does not have to be any of that. At its core, &lt;strong&gt;a home server is just a computer that stays on to serve other devices in your home.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What a home server actually is
&lt;/h2&gt;

&lt;p&gt;Think of it as a helper computer. It runs all the time, sits quietly in a corner, and waits for your other devices to ask it for something. You do not sit in front of it. Once it is set up, it has no screen or keyboard — you reach it from your phone, laptop or TV over your home Wi-Fi.&lt;/p&gt;

&lt;p&gt;The hardware can be almost anything. An old laptop works. So does a cheap mini PC, a Raspberry Pi, or a ready-made NAS box. What makes it a "server" is the job it does, not the size of the machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What people run on one
&lt;/h2&gt;

&lt;p&gt;A home server earns its keep by doing a few steady jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;File storage&lt;/strong&gt; — one place for documents and photos that every device can reach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Media streaming&lt;/strong&gt; — your own films and music, streamed to any screen in the house.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backups&lt;/strong&gt; — automatic copies of your phones and laptops, so a lost device is not a lost life.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart-home control&lt;/strong&gt; — local software that runs your lights and sensors without the cloud.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A self-hosted VPN&lt;/strong&gt; — a private door back into your home network while you travel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can run just one of these, or several at once. Most people keep each job in its own container so they stay tidy and easy to update.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzmig12xm238es7wj59tm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzmig12xm238es7wj59tm.jpg" alt="A real data center looks like this. A home server does not have to — an old laptop in a cupboard counts." width="799" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Home server vs a VPS
&lt;/h2&gt;

&lt;p&gt;The two get confused, but they solve different problems. A home server lives in your house, holds your data, and costs only the electricity to run it. The catch is that it depends on your home internet and you look after it yourself.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;VPS&lt;/strong&gt; is a server you rent in a data center. It is always online, has a fast connection and a public IP, and is easier to reach from anywhere — which is why people host a VPN or a website on one. If you are weighing the two, our guide on &lt;a href="https://www.vpnsmith.com/en/blog/what-is-a-vps" rel="noopener noreferrer"&gt;what a VPS is&lt;/a&gt; explains it in plain terms. Many setups use both: a home server for local files and media, a small VPS for anything the outside world needs to reach.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need to start
&lt;/h2&gt;

&lt;p&gt;Very little. An always-on computer you already own, a wired network connection if you can manage it, and one job to begin with — say, a file share or a media library. Add a second service only once the first is steady. Start small, keep it simple, and let it grow with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;A home server is not a data center. It is a spare computer doing useful work while you get on with your day — storing your files, backing up your devices, streaming your media, and, if you want, giving you a private way home over a VPN. Begin with one machine and one job. That is all it takes to have a home server.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is a home server in simple terms?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A home server is a computer in your home that stays on so other devices can use it. It does not need a screen or a keyboard once it is set up. You reach it from your phone, laptop or TV over your home network. People use one to store files, stream their own media, run automatic backups, host smart-home software, or run a self-hosted VPN. Any always-on computer can be a home server — an old laptop, a mini PC, a Raspberry Pi, or a NAS box.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need a home server, or is a VPS better?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It depends on what you want. A home server keeps your data in your house and costs nothing to run beyond electricity, but it relies on your home internet and you maintain it yourself. A VPS (a rented server in a data center) is always online with a fast, stable connection and a public IP, which is easier for hosting a VPN or a website that you reach from anywhere. Many people use a home server for local files and media, and a small VPS for anything that needs to be reachable from outside the house.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What can you run on a home server?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Common uses are: a file share for all your devices, a media library (so you stream your own films and music), automatic backups of phones and laptops, a photo library, smart-home automation, ad-blocking for the whole network, and a self-hosted VPN to reach your home safely while travelling. You can run one of these or several at once, usually inside containers so they stay tidy and separate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is a home server hard to set up?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not as hard as it used to be. A basic file or media server can be running in an afternoon with free software and a guide. The learning curve is real if you want remote access, secure logins, and automatic updates, but you can start small with one service and add more later. A Raspberry Pi or an old laptop is a cheap, low-risk way to learn before spending money on dedicated hardware.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://www.vpnsmith.com/en/blog/what-is-a-home-server" rel="noopener noreferrer"&gt;vpnsmith&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>selfhosted</category>
      <category>homelab</category>
      <category>linux</category>
      <category>beginners</category>
    </item>
    <item>
      <title>What Is Vibe Coding? The AI-First Way to Build Software (2026)</title>
      <dc:creator>ricco020</dc:creator>
      <pubDate>Sun, 21 Jun 2026 13:36:38 +0000</pubDate>
      <link>https://dev.to/ricco020/what-is-vibe-coding-the-ai-first-way-to-build-software-2026-260a</link>
      <guid>https://dev.to/ricco020/what-is-vibe-coding-the-ai-first-way-to-build-software-2026-260a</guid>
      <description>&lt;p&gt;Ask an AI assistant to "build me a habit-tracker app" and watch it produce working code in seconds — then ask it to tweak the colours, add a chart, fix a crash, all in plain English, without you reading a single line. That loop has a name now: &lt;strong&gt;vibe coding&lt;/strong&gt;. This guide explains what vibe coding actually is, where the term came from, how it works in practice, and the honest line between a brilliant shortcut and a trap.&lt;/p&gt;

&lt;h2&gt;
  
  
  What vibe coding actually is
&lt;/h2&gt;

&lt;p&gt;Vibe coding is building software by &lt;em&gt;describing intent&lt;/em&gt; to an AI and iterating on what it generates, instead of writing and reviewing the code yourself. You operate at the level of "what I want it to do" — add a feature, change a behaviour, fix an error — and let the model handle "how it's written." Your job shifts from author to director: you prompt, you run it, you react to the result, and you repeat.&lt;/p&gt;

&lt;p&gt;The defining trait is how little you look at the code. In classic development you read and own every line. In vibe coding you often accept the AI's output without fully reading it, judging the program by whether it &lt;em&gt;behaves&lt;/em&gt; the way you wanted rather than by how it's built.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1qo69l2k8z6521tm7fmy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1qo69l2k8z6521tm7fmy.jpg" alt="Vibe coding keeps you at the keyboard but moves your attention from writing lines to describing intent and judging the result." width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the term came from
&lt;/h2&gt;

&lt;p&gt;The phrase was popularised by Andrej Karpathy in early 2025. He described a mode of working where you "fully give in to the vibes" and rely on the AI so completely that you barely engage with the underlying code — fun and fast, in his framing, for weekend projects and throwaway tools. The name stuck because it captured something developers were already doing with conversational &lt;a href="https://alexi.sh/ai/best-ai-coding-assistants-2026" rel="noopener noreferrer"&gt;best AI coding assistants&lt;/a&gt;: less typing, more describing.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works in practice
&lt;/h2&gt;

&lt;p&gt;A vibe-coding session looks like a conversation. You state a goal, the assistant writes or edits files, you run the result, and you describe what's wrong or what to add next. The model keeps the project context, so each turn builds on the last. The faster that loop — prompt, run, react — the more it feels like "coding by vibes" rather than by syntax.&lt;/p&gt;

&lt;p&gt;This is close to, but not the same as, &lt;a href="https://alexi.sh/ai/ai-pair-programming" rel="noopener noreferrer"&gt;AI pair programming&lt;/a&gt;. Pair programming with an AI implies you're still reading along, reviewing and steering line by line, like a navigator beside a driver. Vibe coding leans further: you delegate the &lt;em&gt;how&lt;/em&gt; almost entirely and judge only the outcome. The same tools support both modes — the difference is how much you choose to look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it shines — and where it bites
&lt;/h2&gt;

&lt;p&gt;Vibe coding genuinely excels at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prototypes and MVPs&lt;/strong&gt; — get a clickable idea in front of people fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throwaway scripts&lt;/strong&gt; — one-off automation you'll run once and delete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning and exploration&lt;/strong&gt; — see one working approach, then dig in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-critical personal tools&lt;/strong&gt; — where a bug is an annoyance, not a breach.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It bites hardest when the stakes rise. Shipping code you haven't read means inheriting problems you can't see: exposed API keys, missing input validation, insecure dependencies, and subtle logic bugs. When something breaks in production, debugging code you never understood is painful, and a codebase nobody can explain becomes a maintenance liability. The accelerator that built the prototype is a poor foundation for the thing real users depend on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;Vibe coding is a real shift in how software gets made: describe intent, let the AI write it, iterate on the result. For prototypes, scripts and learning it's a superpower that collapses hours into minutes. For anything handling money, data or security, treat the AI's output as a fast first draft from a junior collaborator — read it, test it, and understand it before it ships. The developers who get the most from vibe coding are the ones who know exactly when to stop vibing and start reviewing.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is vibe coding?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vibe coding is the practice of building software by describing what you want in natural language to an AI assistant and accepting or iterating on the code it generates, rather than writing and reviewing every line yourself. You stay at the level of intent — 'add a login form', 'make this faster', 'fix that bug' — and let the model produce the implementation. It works best with conversational AI coding tools and is excellent for prototypes and small scripts, but it shifts your role from author to reviewer-and-director.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who coined the term 'vibe coding'?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The phrase was popularised by Andrej Karpathy in early 2025, when he described a way of working where you 'fully give in to the vibes' and lean on the AI so heavily that you barely look at the code. He framed it as fun and fast for throwaway projects. The term caught on quickly because it named something many developers had already started doing with tools like Cursor, GitHub Copilot and Claude Code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is vibe coding good or bad?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Neither on its own — it depends entirely on the stakes. For prototypes, personal tools, learning experiments and disposable scripts, vibe coding is a genuine accelerator: you get something working in minutes. For production software, anything handling money, personal data or security, shipping code you haven't read is risky, because you inherit bugs and vulnerabilities you can't see and will struggle to debug. The skill is knowing which mode a task calls for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is vibe coding safe for production apps?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not without review. The danger of pure vibe coding is that you ship code you don't understand, which can hide security flaws (exposed secrets, missing input validation, insecure dependencies), subtle logic bugs, and maintenance debt that nobody on the team can explain later. For anything real, treat the AI's output as a draft from a fast but junior collaborator: read it, test it, and understand it before it goes live.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://alexi.sh/ai/what-is-vibe-coding" rel="noopener noreferrer"&gt;alexi.sh&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
