<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>OnSiteReliability.com</title>
    <description>Writing about site reliability engineering, security, software architecture, strategy, leadership, and the craft of writing.
</description>
    <link>https://onsitereliability.com//</link>
    <atom:link href="https://onsitereliability.com//feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sat, 18 Jul 2026 19:38:09 +0000</pubDate>
    <lastBuildDate>Sat, 18 Jul 2026 19:38:09 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>SRE Monitoring: A Complete Checklist</title>
        <description>&lt;p&gt;Most monitoring setups grow organically. A team adds a dashboard here, an alert there, and over time the system accumulates gaps nobody notices until an outage reveals them. The Google SRE books offer a structured alternative: a layered approach to monitoring that starts with what users experience and works inward toward root causes.&lt;/p&gt;

&lt;p&gt;This post synthesizes the monitoring guidance from three Google books: Site Reliability Engineering (2016), The Site Reliability Workbook (2018), and Building Secure and Reliable Systems (2020). The result is a single checklist you can use to audit your monitoring coverage.&lt;/p&gt;

&lt;h2 id=&quot;the-four-golden-signals&quot;&gt;The Four Golden Signals&lt;/h2&gt;

&lt;p&gt;Google’s SRE book defines four signals that every service should monitor. These are the foundation.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Signal&lt;/th&gt;
      &lt;th&gt;What to Monitor&lt;/th&gt;
      &lt;th&gt;Alert Threshold Guidance&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Latency&lt;/td&gt;
      &lt;td&gt;Request duration (split success vs error), P50/P95/P99&lt;/td&gt;
      &lt;td&gt;Page on sustained P99 breach of SLO&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Traffic&lt;/td&gt;
      &lt;td&gt;Requests/sec, connections/sec, sessions/sec&lt;/td&gt;
      &lt;td&gt;Alert on sudden drops (more than increases)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Errors&lt;/td&gt;
      &lt;td&gt;Error rate %, explicit (5xx), implicit (wrong content), policy (too slow)&lt;/td&gt;
      &lt;td&gt;Page when error budget burn rate is high&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Saturation&lt;/td&gt;
      &lt;td&gt;CPU, memory, disk, connections, queue depth: the resource that will exhaust first&lt;/td&gt;
      &lt;td&gt;Page at 80%+ of hard capacity&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Traffic drops are often more informative than spikes. A sudden drop in requests frequently means users cannot reach you at all. Spikes might just mean you are popular.&lt;/p&gt;

&lt;h2 id=&quot;the-full-monitoring-checklist&quot;&gt;The Full Monitoring Checklist&lt;/h2&gt;

&lt;p&gt;The four golden signals tell you something is wrong. A complete monitoring stack tells you why, and warns you before users notice. The checklist below is organized in tiers from user-facing symptoms down to operational meta-signals.&lt;/p&gt;

&lt;h4 id=&quot;tier-1-user-facing-slis-symptoms&quot;&gt;Tier 1: User-Facing SLIs (Symptoms)&lt;/h4&gt;

&lt;p&gt;This is where your monitoring should be strongest. Everything else exists to explain anomalies in these signals.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;End-to-end latency from the user perspective (synthetic probes)&lt;/li&gt;
  &lt;li&gt;Success rate of user-visible operations&lt;/li&gt;
  &lt;li&gt;Availability measured from outside the system (black-box)&lt;/li&gt;
  &lt;li&gt;Error rate differentiated by severity (client vs server errors)&lt;/li&gt;
  &lt;li&gt;Traffic volume with anomaly detection (sudden drop equals outage signal)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;tier-2-infrastructure-health-causes&quot;&gt;Tier 2: Infrastructure Health (Causes)&lt;/h4&gt;

&lt;p&gt;Once you know something is wrong at Tier 1, these metrics help you find the cause.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Compute saturation (CPU, memory per pod/node)&lt;/li&gt;
  &lt;li&gt;Network saturation (bandwidth, connection limits, packet loss)&lt;/li&gt;
  &lt;li&gt;Storage saturation (disk IOPS, capacity)&lt;/li&gt;
  &lt;li&gt;Database health (connections, replication lag, query latency)&lt;/li&gt;
  &lt;li&gt;Cache effectiveness (hit rate, eviction rate, memory usage)&lt;/li&gt;
  &lt;li&gt;Queue depth and processing lag&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;tier-3-dependency-health&quot;&gt;Tier 3: Dependency Health&lt;/h4&gt;

&lt;p&gt;Modern services fail at their boundaries. Monitor what you depend on.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Upstream dependency latency and error rates&lt;/li&gt;
  &lt;li&gt;Downstream dependency availability&lt;/li&gt;
  &lt;li&gt;Cloud provider service health (region-level signals)&lt;/li&gt;
  &lt;li&gt;DNS resolution success and latency&lt;/li&gt;
  &lt;li&gt;Certificate validity and expiration tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;tier-4-capacity-and-scaling&quot;&gt;Tier 4: Capacity and Scaling&lt;/h4&gt;

&lt;p&gt;Saturation problems are predictable if you track the trends.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Current utilization vs headroom for each constrained resource&lt;/li&gt;
  &lt;li&gt;Autoscaler health (desired vs actual replicas)&lt;/li&gt;
  &lt;li&gt;IP address pool exhaustion&lt;/li&gt;
  &lt;li&gt;Session/connection capacity vs current load&lt;/li&gt;
  &lt;li&gt;Rate limit proximity&lt;/li&gt;
  &lt;li&gt;Time-to-exhaustion predictions for growing resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;tier-5-configuration-and-deployment&quot;&gt;Tier 5: Configuration and Deployment&lt;/h4&gt;

&lt;p&gt;Many outages correlate with recent changes. Make those changes visible.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Deployment health (rollout status, rollback signals)&lt;/li&gt;
  &lt;li&gt;Configuration drift detection&lt;/li&gt;
  &lt;li&gt;Feature flag and config change correlation with errors&lt;/li&gt;
  &lt;li&gt;Canary/rollout monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;tier-6-security-and-compliance&quot;&gt;Tier 6: Security and Compliance&lt;/h4&gt;

&lt;p&gt;Security failures are reliability failures. Monitor them the same way.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Authentication/authorization failure rates&lt;/li&gt;
  &lt;li&gt;Certificate expiration (30-day, 7-day, 1-day warnings)&lt;/li&gt;
  &lt;li&gt;Anomalous access patterns&lt;/li&gt;
  &lt;li&gt;Compliance-relevant audit trail gaps&lt;/li&gt;
  &lt;li&gt;Rate limiting and abuse detection&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;tier-7-data-correctness-and-freshness&quot;&gt;Tier 7: Data Correctness and Freshness&lt;/h4&gt;

&lt;p&gt;For services that process or serve data, correctness is an SLI.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Data pipeline lag (freshness SLI)&lt;/li&gt;
  &lt;li&gt;Data completeness checks&lt;/li&gt;
  &lt;li&gt;Cross-system consistency validation&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;tier-8-incident-detection-meta-signals&quot;&gt;Tier 8: Incident Detection Meta-Signals&lt;/h4&gt;

&lt;p&gt;Your monitoring system itself can fail silently. Watch the watchers.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Customer-reported issues (manual escalation)&lt;/li&gt;
  &lt;li&gt;Correlated multi-signal alerts (not just individual thresholds)&lt;/li&gt;
  &lt;li&gt;Maintenance window awareness&lt;/li&gt;
  &lt;li&gt;Operational workflow health (runbook automation success)&lt;/li&gt;
  &lt;li&gt;Monitoring system health (dead alerts, unexercised rules)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;slo-based-alerting&quot;&gt;SLO-Based Alerting&lt;/h2&gt;

&lt;p&gt;The SRE Workbook advocates multi-window, multi-burn-rate alerting over simple threshold-based alerts. The idea: alert not on a raw error count, but on the rate at which you are consuming your error budget.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Severity&lt;/th&gt;
      &lt;th&gt;Long Window&lt;/th&gt;
      &lt;th&gt;Short Window&lt;/th&gt;
      &lt;th&gt;Burn Rate&lt;/th&gt;
      &lt;th&gt;Budget Consumed&lt;/th&gt;
      &lt;th&gt;Action&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Page&lt;/td&gt;
      &lt;td&gt;1 hour&lt;/td&gt;
      &lt;td&gt;5 minutes&lt;/td&gt;
      &lt;td&gt;14.4x&lt;/td&gt;
      &lt;td&gt;2%&lt;/td&gt;
      &lt;td&gt;Immediate&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Page&lt;/td&gt;
      &lt;td&gt;6 hours&lt;/td&gt;
      &lt;td&gt;30 minutes&lt;/td&gt;
      &lt;td&gt;6x&lt;/td&gt;
      &lt;td&gt;5%&lt;/td&gt;
      &lt;td&gt;Immediate&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Ticket&lt;/td&gt;
      &lt;td&gt;24 hours&lt;/td&gt;
      &lt;td&gt;2 hours&lt;/td&gt;
      &lt;td&gt;3x&lt;/td&gt;
      &lt;td&gt;10%&lt;/td&gt;
      &lt;td&gt;Non-urgent&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Ticket&lt;/td&gt;
      &lt;td&gt;3 days&lt;/td&gt;
      &lt;td&gt;6 hours&lt;/td&gt;
      &lt;td&gt;1x&lt;/td&gt;
      &lt;td&gt;10%&lt;/td&gt;
      &lt;td&gt;Next business day&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;A 14.4x burn rate over one hour means you will exhaust your entire monthly error budget in about two days if the condition persists. That warrants a page. A 1x burn rate sustained over three days means you will barely miss your SLO target by month’s end. That warrants a ticket, not a 3am wake-up.&lt;/p&gt;

&lt;p&gt;The dual-window approach (long window plus short window) prevents both slow-burn blindness and flappy alerting. The long window catches sustained issues. The short window confirms the problem is happening right now, not just an artifact of earlier errors still in the window.&lt;/p&gt;

&lt;h2 id=&quot;five-questions-before-creating-any-alert&quot;&gt;Five Questions Before Creating Any Alert&lt;/h2&gt;

&lt;p&gt;Before you add any new alert rule, run it through these five questions from the SRE book.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Does this rule detect an otherwise undetected condition that is urgent, actionable, and actively or imminently user-visible?&lt;/li&gt;
  &lt;li&gt;Will I ever be able to ignore this alert, knowing it is benign?&lt;/li&gt;
  &lt;li&gt;Does this alert definitely indicate that users are being negatively affected?&lt;/li&gt;
  &lt;li&gt;Can I take action in response? Is that action urgent, or could it wait until morning?&lt;/li&gt;
  &lt;li&gt;Are other people getting paged for this issue, rendering at least one page unnecessary?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the answer to question 2 is yes, the alert will train your team to ignore pages. If the answer to question 4 is “it can wait,” it should be a ticket, not a page. These questions are a forcing function for alert hygiene.&lt;/p&gt;

&lt;h2 id=&quot;key-principles&quot;&gt;Key Principles&lt;/h2&gt;

&lt;p&gt;A few principles tie the checklist together.&lt;/p&gt;

&lt;p&gt;Heavy use of white-box monitoring with modest but critical uses of black-box monitoring. White-box (metrics, logs, traces from inside the system) tells you why. Black-box (synthetic probes from outside) tells you what users actually experience. You need both, but white-box carries most of the diagnostic weight.&lt;/p&gt;

&lt;p&gt;Symptoms over causes. Spend more effort catching symptoms than causes. A user does not care whether your database is slow or your cache is cold. They care that the page did not load. Alert on what users experience, and use cause-level metrics for diagnosis.&lt;/p&gt;

&lt;p&gt;Every page should be actionable, require intelligence, and represent a novel problem. If a page requires no thought, automate the response. If it requires no action, it should not page.&lt;/p&gt;

&lt;p&gt;Alert on burn rate, not raw threshold. A single error is not an incident. A sustained rate of errors that threatens your SLO target is.&lt;/p&gt;

&lt;p&gt;SLIs are always ratios: good events divided by total events, expressed as a percentage. Error budget equals one minus the SLO target. Track on a four-week rolling window.&lt;/p&gt;

&lt;h4 id=&quot;references&quot;&gt;References&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://sre.google/sre-book/table-of-contents/&quot;&gt;Site Reliability Engineering&lt;/a&gt; (2016)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sre.google/workbook/table-of-contents/&quot;&gt;The Site Reliability Workbook&lt;/a&gt; (2018)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sre.google/books/building-secure-reliable-systems/&quot;&gt;Building Secure and Reliable Systems&lt;/a&gt; (2020)&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 18 Jul 2026 00:00:00 +0000</pubDate>
        <link>https://onsitereliability.com//SRE-Monitoring-A-Complete-Checklist/</link>
        <guid isPermaLink="true">https://onsitereliability.com//SRE-Monitoring-A-Complete-Checklist/</guid>
        
        <category>SRE</category>
        
        <category>monitoring</category>
        
        <category>observability</category>
        
        
        <category>SRE</category>
        
      </item>
    
      <item>
        <title>Engineering Confidence: A Book About qmail and Earning Trust Through Architecture</title>
        <description>&lt;p&gt;I wrote a book called &lt;em&gt;Engineering Confidence&lt;/em&gt;. It is about qmail, the 1990s mail server that shipped with a public security guarantee and a cult following among Unix administrators.  I wrote this book because qmail is worth remembering. Imagine the confidence needed to thrown open your source code to the whole world, and dare it to find a security hole. On top of it, the developer of qmail, D.J. Bernstein offered $500 to anyone who found a security hole in the latest version of qmail. That confidence is rarely seen in software today. That confidence was based on principles, it was based on structure. This structure is worth learning, worth considering when we make decisions about software today.&lt;/p&gt;

&lt;p&gt;The book is not an installation manual. It uses qmail as a case study in how software earns trust: through architecture, boundaries, constrained privilege, careful parsing, explicit claims, and long-term maintenance decisions. Modern engineering teams make claims all the time: this system is secure, reliable, simple, maintainable, zero trust, cloud native, production ready. qmail is a reminder that confidence has to be earned in the design.&lt;/p&gt;

&lt;h4 id=&quot;what-it-covers&quot;&gt;What it covers&lt;/h4&gt;

&lt;p&gt;The book follows qmail from the hostile mail environment of the 1990s, through Bernstein’s architectural response, to the long-term consequences of a frozen upstream. It examines the security guarantee, the disputes around it, the patch culture that grew in the absence of releases, and what modern engineering teams can still learn from the design.&lt;/p&gt;

&lt;h4 id=&quot;who-it-is-for&quot;&gt;Who it is for&lt;/h4&gt;

&lt;p&gt;Site reliability engineers, security engineers, infrastructure engineers, engineering managers, and anyone interested in how technical confidence is built, challenged, and maintained. No prior qmail experience required.&lt;/p&gt;

&lt;h4 id=&quot;status&quot;&gt;Status&lt;/h4&gt;

&lt;p&gt;The book is nearing completion. Formats planned: EPUB, PDF, Kindle, and paperback.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/book/&quot;&gt;Read more about the book&lt;/a&gt; or &lt;a href=&quot;https://leanpub.com/engineeringconfidence&quot;&gt;Buy on Leanpub&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate>
        <link>https://onsitereliability.com//Engineering-Confidence-A-Book-About-qmail/</link>
        <guid isPermaLink="true">https://onsitereliability.com//Engineering-Confidence-A-Book-About-qmail/</guid>
        
        <category>security</category>
        
        <category>architecture</category>
        
        <category>books</category>
        
        
        <category>Announcements</category>
        
      </item>
    
      <item>
        <title>The Morris Worm: When the Internet First Broke</title>
        <description>&lt;p&gt;On the evening of November 2, 1988, around 6 PM EST, a program was launched onto the Internet from one or more hosts. Within hours, thousands of machines running BSD UNIX were so overloaded with rogue processes that they could not function. Systems ran out of swap space. Process tables filled up. Administrators disconnected entire networks in a panic. The Internet had experienced its first major self-inflicted wound.&lt;/p&gt;

&lt;p&gt;The program responsible was a worm written by Robert T. Morris, a graduate student at Cornell. Eugene Spafford’s technical analysis of the decompiled code, published just weeks after the incident, remains one of the most thorough postmortems in computing history. What it reveals is not a work of genius, but something more instructive: a mediocre program that succeeded because the systems it attacked were unprepared for even a mediocre adversary.&lt;/p&gt;

&lt;h4 id=&quot;how-the-worm-spread&quot;&gt;How the Worm Spread&lt;/h4&gt;

&lt;p&gt;The worm targeted Sun 3 and VAX machines running 4 BSD UNIX. It used three attack vectors to break into new hosts.&lt;/p&gt;

&lt;p&gt;The first was a buffer overflow in fingerd. The finger daemon used the C library function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gets&lt;/code&gt; to read network input into a fixed-size buffer without bounds checking. The worm sent a carefully constructed 536-byte string that overflowed the buffer, overwrote the return address on the stack, and caused the program to execute &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/bin/sh&lt;/code&gt;. On VAX machines, this gave the worm a remote shell. On Suns, it produced a core dump instead, because the shellcode was VAX-specific.&lt;/p&gt;

&lt;p&gt;The second was a debug mode in sendmail. The worm issued the DEBUG command to sendmail and then specified a set of shell commands as the recipient of a message. This debug feature was not supposed to be enabled in production, but it was present in the code for testing mail delivery, and many vendors and administrators left it compiled in. The worm exploited this to pipe commands directly to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/bin/sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The third was password guessing combined with trust relationships. The worm read &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/passwd&lt;/code&gt;, attempted to crack passwords using the account name, variations of the user’s real name, and a built-in dictionary of 432 words. When it broke a password, it used rsh and rexec to reach other machines where that user had accounts, exploiting the common practice of reusing passwords across hosts.&lt;/p&gt;

&lt;h4 id=&quot;the-infection-sequence&quot;&gt;The Infection Sequence&lt;/h4&gt;

&lt;p&gt;The worm had two components: a main program and a bootstrap called the vector program. Once the worm obtained a shell on a target machine through any of the three attack methods, it followed a precise sequence.&lt;/p&gt;

&lt;p&gt;It compiled and ran the vector program on the target. The vector connected back to the infecting machine on a predetermined TCP port, authenticated with a challenge number, and downloaded three files: a Sun 3 binary of the worm, a VAX binary, and the vector source code. The infecting worm then sent shell commands to try each binary. If one ran successfully, the new host was marked as infected.&lt;/p&gt;

&lt;p&gt;The newly running worm immediately hid itself. It changed its process name, unlinked its binary from disk, killed its parent process, loaded the worm binaries into memory, encrypted them, and deleted the files. From that point on, it existed only in memory with no obvious filesystem footprint.&lt;/p&gt;

&lt;h4 id=&quot;why-it-got-out-of-control&quot;&gt;Why It Got Out of Control&lt;/h4&gt;

&lt;p&gt;The worm included a mechanism to avoid reinfecting machines. Running copies would listen on TCP port 23357 on localhost. A new worm arriving on an already-infected host would connect to that port, exchange random numbers, and based on the result, one of the two would exit.&lt;/p&gt;

&lt;p&gt;This mechanism was fatally flawed in two ways. First, one out of every seven worms would skip the check entirely and become “immortal,” ignoring any other worms on the same machine. Spafford concludes this was deliberate, designed to prevent administrators from running a fake listener on the worm port to kill incoming copies. Second, even worms marked for self-destruction would continue running through at least one full password-cracking cycle before exiting. The result was exponential growth: each pass through the main loop forced the worm to infect at least one local host, and multiple copies on a single machine would each do the same.&lt;/p&gt;

&lt;p&gt;The worm’s author apparently did not understand the propagation dynamics of his own program. The code contained mechanisms to limit growth, which suggests awareness that uncontrolled replication was undesirable. But those mechanisms were insufficient, producing the catastrophic load that made the worm so visible and so disruptive.&lt;/p&gt;

&lt;h4 id=&quot;the-response&quot;&gt;The Response&lt;/h4&gt;

&lt;p&gt;By late Wednesday night, personnel at UC Berkeley and MIT had captured copies of the program and begun analyzing it. By 5 AM Thursday, less than 12 hours after the worm appeared, the Berkeley Computer Systems Research Group had published an interim set of defenses: a patch to sendmail and a suggestion to rename the C compiler to prevent the worm from building itself on new hosts.&lt;/p&gt;

&lt;p&gt;By 7 PM Thursday, a simpler defense was discovered at Purdue: creating a directory named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sh&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/tmp&lt;/code&gt; caused the worm’s shell test (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if [ -f sh ]&lt;/code&gt;) to fail, blocking further infection without renaming any system utilities. This worked because the worm used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f&lt;/code&gt; flag, which tests for a regular file but returns false for directories. The author had used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[&lt;/code&gt; instead of the more portable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; command, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-e&lt;/code&gt;, both signs of limited experience with shell scripting.&lt;/p&gt;

&lt;p&gt;On November 8, the National Computer Security Center convened a hastily-organized workshop in Baltimore. The attendees agreed not to distribute their reverse-engineered source code publicly. By that date, Spafford was aware of at least eleven independent decompilations of the worm. The genie was already out of the bottle.&lt;/p&gt;

&lt;h4 id=&quot;what-the-code-revealed&quot;&gt;What the Code Revealed&lt;/h4&gt;

&lt;p&gt;Spafford’s analysis of the reverse-engineered code is ruthless in its assessment. The worm was not the work of a genius. The code quality was mediocre. Local variables went uninitialized. Functions were called with wrong numbers of arguments. Return codes from system calls were never checked. Data structures were uniformly linked lists with linear searches, where hash tables would have been trivial to implement and far more efficient.&lt;/p&gt;

&lt;p&gt;The one section that showed genuine sophistication was the password-cracking routine, which ran nine times faster than the standard Berkeley &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crypt&lt;/code&gt; function. Spafford notes that this code does not appear to have been written by the same person who wrote the rest of the worm. It included support for both encryption and decryption, even though the worm only needed encryption.&lt;/p&gt;

&lt;p&gt;The worm did not write to the filesystem except during infection. It did not transmit any information from infected systems to any external location. It did not exploit root access even when it cracked root passwords. Spafford finds this last omission difficult to attribute to deliberate restraint, suggesting instead that the author simply did not think of it.&lt;/p&gt;

&lt;p&gt;The program could have been far more dangerous. The fingerd exploit worked only on VAX, not Sun, likely because the author never built a Sun version of the shellcode. Three Purdue graduate students replicated the Sun exploit in under three hours when asked. The worm’s password-cracking dictionary was only 432 words. It did not gather hostnames from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.rhosts&lt;/code&gt; files early in its execution. It failed to infect local network hosts due to a bug in the routing logic.&lt;/p&gt;

&lt;h4 id=&quot;the-larger-lessons&quot;&gt;The Larger Lessons&lt;/h4&gt;

&lt;p&gt;The worm exposed problems that the UNIX community had long known about but never fixed. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gets&lt;/code&gt; function had no bounds checking and was documented as dangerous, yet it remained in the standard C library and in production daemons. Sendmail’s debug mode was a known risk that administrators tolerated for convenience. Password hashes were stored in world-readable files, allowing offline cracking. Trust relationships between hosts via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.rhosts&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hosts.equiv&lt;/code&gt; created lateral movement paths that one compromised account could traverse.&lt;/p&gt;

&lt;p&gt;Spafford’s recommendations read like a modern security checklist written decades early: replace unbounded string functions with bounded alternatives, audit all network-facing code for their use, run each daemon under its own user ID (least privilege), implement shadow password files, enforce password complexity, and establish coordinated vulnerability disclosure mechanisms.&lt;/p&gt;

&lt;p&gt;The response to the incident was ad hoc and nearly failed. Patches were distributed via Usenet and mailing lists, but those communication channels depended on the same Internet that was under attack. Sites that disconnected for self-protection could not receive the fixes. Three weeks after the incident, some sites had still not reconnected.&lt;/p&gt;

&lt;p&gt;The worm demonstrated that the internet’s openness was simultaneously its greatest strength and its greatest vulnerability. The same properties that allowed researchers across the country to collaborate on a fix in under 12 hours also allowed a single program to propagate across thousands of machines in the same timeframe.&lt;/p&gt;

&lt;h4 id=&quot;references&quot;&gt;References&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Eugene H. Spafford, “The Internet Worm Program: An Analysis,” Purdue Technical Report CSD-TR-823, 1988&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 21 Jun 2026 00:00:00 +0000</pubDate>
        <link>https://onsitereliability.com//The-Morris-Worm-When-the-Internet-First-Broke/</link>
        <guid isPermaLink="true">https://onsitereliability.com//The-Morris-Worm-When-the-Internet-First-Broke/</guid>
        
        <category>security</category>
        
        <category>incidents</category>
        
        <category>internet-history</category>
        
        
        <category>Security</category>
        
      </item>
    
      <item>
        <title>Boris Cherny on Building Claude Code</title>
        <description>&lt;p&gt;Boris Cherney is the creator and engineering lead behind Claude Code at Anthropic. Before that, he spent seven years at Meta leading code quality across Instagram, Facebook, WhatsApp, and Messenger. In a recent conversation on the Pragmatic Engineer podcast, Boris shared how Claude Code went from a solo side project to one of the fastest-growing developer tools, how it writes roughly 80% of all code at Anthropic, and what his daily workflow looks like when shipping 20 to 30 pull requests a day with zero handwritten code.&lt;/p&gt;

&lt;p&gt;The whole conversation is worth listening to. But the parts that stuck with me most were the practical lessons: things Boris does every day, principles that shaped the tool, and shifts in how he thinks about engineering. Here are the ones I keep coming back to.&lt;/p&gt;

&lt;h4 id=&quot;plan-first-then-let-it-one-shot&quot;&gt;Plan First, Then Let It One-Shot&lt;/h4&gt;

&lt;p&gt;Boris almost always starts in plan mode. He iterates on the plan with Claude before letting it implement anything. This is the highest-leverage workflow pattern he describes.&lt;/p&gt;

&lt;p&gt;“Once there is a good plan, it just will one-shot the implementation almost every time,” he says. “The most important thing is to go back and forth a little bit to get the plan right.”&lt;/p&gt;

&lt;p&gt;The instinct most people have is to jump straight into implementation. That works when you are the one writing the code and can course-correct on the fly. When an agent is doing the work, a bad plan means a bad implementation that you have to throw away. A few extra minutes refining the plan saves significantly more time than fixing a misdirected implementation.&lt;/p&gt;

&lt;h4 id=&quot;parallelize-your-agents&quot;&gt;Parallelize Your Agents&lt;/h4&gt;

&lt;p&gt;Boris runs five terminal tabs, each with a separate checkout of the repository, with Claude Code running in parallel across all of them. He round-robins between tabs: starts Claude in plan mode on tab one, moves to tab two, starts another, and keeps cycling. When he runs out of tabs, he overflows to the desktop app and the iOS app.&lt;/p&gt;

&lt;p&gt;“If you told me six months ago I’d be writing, I don’t know, a third, maybe like a third, half, something like this of my code on a phone. That’s crazy. But that’s what I’m doing today.”&lt;/p&gt;

&lt;p&gt;The key insight is that the bottleneck is no longer typing speed or even thinking speed. It is how well you manage multiple concurrent streams of work. Boris frames this directly: “The work has become jumping between Claudes. It’s not so much about deep work, it’s about how good am I at context-switching.”&lt;/p&gt;

&lt;h4 id=&quot;automate-recurring-review-comments&quot;&gt;Automate Recurring Review Comments&lt;/h4&gt;

&lt;p&gt;At Meta, Boris was one of the most prolific code reviewers at the company. He tracked every recurring comment in a spreadsheet. When a particular issue appeared three or four times, he wrote a lint rule for it.&lt;/p&gt;

&lt;p&gt;“Every time that I would have to comment about something, I would drop it in a spreadsheet,” he explains. “Anytime that a particular row had more than three or four instances, I would write a lint rule for it. Just automate it with an op. This is one of our superpowers as engineers: we are able to automate all of the tedious work.”&lt;/p&gt;

&lt;p&gt;The modern version is even faster. “Now what I do is when a coworker puts up a pull request and I’m like, this is lintable, I’ll just at Claude, please write a lint rule for this, in that PR on their PR.” The feedback loop from noticing a pattern to encoding it as an automated check has collapsed from days to minutes.&lt;/p&gt;

&lt;h4 id=&quot;understand-the-layer-below&quot;&gt;Understand the Layer Below&lt;/h4&gt;

&lt;p&gt;Boris spent part of his early time at Anthropic working on reinforcement learning, not because it was his job, but because he wanted to understand the layer underneath what he was building.&lt;/p&gt;

&lt;p&gt;“Always understand the layer under. It’s really important because that just gives you the depth and you have a little bit more levers to work at the layer that you actually work at.” He notes that this advice has not changed in a decade. What has changed is which layer matters. “Before it was like understand the JavaScript VM and frameworks and stuff. Now it’s: understand the model.”&lt;/p&gt;

&lt;p&gt;This is a practical call to action. If you are building on top of language models, you should have at least a working understanding of how they reason, where they fail, and what makes them perform better. That knowledge directly improves the quality of the prompts you write, the architectures you design, and the failure modes you anticipate.&lt;/p&gt;

&lt;h4 id=&quot;prototype-aggressively&quot;&gt;Prototype Aggressively&lt;/h4&gt;

&lt;p&gt;When building the todo list feature for Claude Code, Boris created roughly twenty working prototypes in about a day and a half. Not mockups, not wireframes. Working, interactive implementations. He tried each one, got a feel for it, and moved on.&lt;/p&gt;

&lt;p&gt;“On our team the culture is we don’t really write stuff. We just show. Prototyping everything is so baked into the way that we build.” He contrasts this with the old approach of starting with static mocks in Figma or writing a PRD. “There’s just no way we could have shipped this if we started with static mocks or a PRD. It’s a thing that you have to build and you have to feel.”&lt;/p&gt;

&lt;p&gt;The economic argument is simple: the cost of building has dropped dramatically. When implementation is cheap, the right move is to explore broadly rather than speculate narrowly. “Personally I’m wrong like half the time. At least half of my ideas are bad. And I don’t know which half until I try it.”&lt;/p&gt;

&lt;h4 id=&quot;embrace-generalism&quot;&gt;Embrace Generalism&lt;/h4&gt;

&lt;p&gt;At Anthropic, everyone holds the same title: Member of Technical Staff. Boris sees this as more than a cultural choice. It is a signal about how work gets done.&lt;/p&gt;

&lt;p&gt;“Without this title, the default would have been: I see your name on Slack and under your name it says software engineer. And then I’m like, okay, I guess you’re the coding person. I’m not going to ask you product questions. But when everyone’s title is Member of Technical Staff, by default you assume everyone does everything.”&lt;/p&gt;

&lt;p&gt;He believes this is a preview of where the broader industry is heading. “People are going to become more and more multi-discipline and this will become more and more rewarded. In some ways, I think this will be the year of the generalist.” When AI tools lower the cost of execution across disciplines, the people who can work across boundaries become disproportionately valuable.&lt;/p&gt;

&lt;h4 id=&quot;revisit-old-assumptions&quot;&gt;Revisit Old Assumptions&lt;/h4&gt;

&lt;p&gt;The pace of model improvement means that ideas which failed months ago may now work. Boris describes this as one of the hardest adjustments.&lt;/p&gt;

&lt;p&gt;“The model is improving so quickly that the ideas that worked with the old model might not work with a new model. The things that didn’t work with the old model might work with a new model. You just always have to bring this beginner mindset.”&lt;/p&gt;

&lt;p&gt;In a traditional engineering culture, re-trying a failed idea is often met with skepticism. Why are you doing this again? We already tried it. Boris argues that this instinct is now counterproductive. “It’s the first time ever where it’s actually not crazy to just try the same idea every few months because the model improves and it just works.”&lt;/p&gt;

&lt;p&gt;He also points out that this dynamic inverts the usual seniority advantage. “New people that are newer to the team, people that are newer to engineering, sometimes do things in a better way than I do. And I just have to look at them and I have to learn and I have to adjust my expectations.”&lt;/p&gt;

&lt;h4 id=&quot;keep-a-clean-codebase&quot;&gt;Keep a Clean Codebase&lt;/h4&gt;

&lt;p&gt;At Meta, Boris led the Better Engineering program, a Zuckerberg mandate requiring every engineer to spend 20% of their time fixing tech debt. His team used causal inference to measure the impact. The finding: code quality contributes double-digit percentage points to engineering productivity.&lt;/p&gt;

&lt;p&gt;That finding now extends to models. “When you start a migration, finish the migration,” Boris says. “As an engineer, you’re going to have a bad time. As a new hire, you’re going to have a bad time. As a model, you might just pick the wrong thing and then the user has to course-correct you. So actually the better thing to do is just always have a clean codebase. This is great for engineers and nowadays it’s great for models too.”&lt;/p&gt;

&lt;p&gt;A codebase with two half-migrated frameworks is confusing for humans and actively harmful for AI agents that cannot tell which pattern is the intended one. If code quality had a double-digit impact on human productivity, the impact on model-assisted productivity is likely even larger.&lt;/p&gt;

&lt;h4 id=&quot;layer-your-safety-measures&quot;&gt;Layer Your Safety Measures&lt;/h4&gt;

&lt;p&gt;Claude Code’s security model uses what Boris calls the Swiss cheese model: multiple overlapping layers, none of which is perfect on its own, but whose combined probability of catching problems increases with each additional layer.&lt;/p&gt;

&lt;p&gt;“For things like safety and security, there’s no one perfect answer. You just need a bunch of layers. With enough layers, the probability of catching anything goes up. You just have to count the number of nines in that probability and pick the threshold that you want.”&lt;/p&gt;

&lt;p&gt;For prompt injection specifically, there are at least three layers: model alignment (training the model to resist injection), runtime classifiers that detect suspicious requests, and sub-agent summarization that processes external content through a separate context before returning results to the main agent. “This isn’t just one mechanism. It’s a layer, and by having a bunch of these different layers, it just reduces the probability a lot.”&lt;/p&gt;

&lt;p&gt;This principle applies broadly. Any system that interacts with untrusted input needs defense in depth. A single mechanism, no matter how sophisticated, has failure modes. Layered defenses make those failure modes independent, which makes the system far more robust.&lt;/p&gt;

&lt;h4 id=&quot;the-printing-press-moment&quot;&gt;The Printing Press Moment&lt;/h4&gt;

&lt;p&gt;Boris compares this moment in software engineering to the invention of the printing press. Before the press, less than 1% of the European population was literate. Writing was a niche skill performed by scribes employed by lords and kings, many of whom were themselves illiterate.&lt;/p&gt;

&lt;p&gt;After the press, the cost of printed material dropped 100x in a few decades, and the quantity of printed material increased 10,000x over the following century. “If you think about what happened to the scribes, they ceased to become scribes, but now there’s a category of writers and authors. These people now exist. And the reason they exist is because the market for literature just expanded a ton.”&lt;/p&gt;

&lt;p&gt;The parallel to coding is direct. Programming has been a niche skill performed by a small fraction of the population. As AI tools make coding accessible to everyone, the market for software will expand in ways that are difficult to predict. “The economy as we know it would not have existed without it. So what’s next? What is the thing that we can’t even predict today that will exist because anyone can do this?”&lt;/p&gt;

&lt;p&gt;It is an optimistic framing, and one grounded in historical precedent. The scribes did not disappear. The scope of what they could do expanded beyond anything they could have imagined.&lt;/p&gt;

&lt;h4 id=&quot;references&quot;&gt;References&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=2ncBfaxONGE&quot;&gt;Pragmatic Engineer Podcast: Building Claude Code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/overview&quot;&gt;Claude Code Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 25 Apr 2026 00:00:00 +0000</pubDate>
        <link>https://onsitereliability.com//Boris-Cherny-on-Building-Claude-Code/</link>
        <guid isPermaLink="true">https://onsitereliability.com//Boris-Cherny-on-Building-Claude-Code/</guid>
        
        <category>AI</category>
        
        <category>software-engineering</category>
        
        <category>productivity</category>
        
        
        <category>Engineering</category>
        
      </item>
    
      <item>
        <title>Building a Knowledge Management System with Structured Memory</title>
        <description>&lt;p&gt;Operational knowledge about complex systems lives across dozens of chat threads, ticketing systems, wikis, and people’s heads. When a question comes up (“Which customers were affected by recent incidents?”), answering it requires searching multiple systems and reconstructing context from scratch every time. The information exists, but it is scattered and unstructured, which means it might as well not exist when you need it fast.&lt;/p&gt;

&lt;p&gt;I built a file-based knowledge management system using Claude Code’s persistent memory to solve this problem. The system stores knowledge as individual Markdown files with structured YAML frontmatter. Each file represents one discrete unit of knowledge: an incident, a service architecture, a customer relationship, or operational feedback. The approach is simple, but the structure is what makes it work.&lt;/p&gt;

&lt;h4 id=&quot;how-a-memory-file-is-structured&quot;&gt;How a Memory File Is Structured&lt;/h4&gt;

&lt;p&gt;Every memory file has three parts, and each part serves a specific purpose.&lt;/p&gt;

&lt;p&gt;The frontmatter is YAML at the top of the file. It contains a name, a one-line description, and a type classification. The description matters more than you might think. It is written to support relevance matching in future lookups, so it needs to be specific enough that the system can decide whether this file is worth reading for a given question. A description like “Q4 incident” is almost useless. A description like “Database connection pool exhaustion caused 45-minute outage affecting billing service, October 2025” is immediately useful.&lt;/p&gt;

&lt;p&gt;The body is the actual knowledge, structured consistently by type. Incident files follow a template: timeline, root cause, resolution, customer impact, retro findings. Architecture files capture service relationships, failure modes, and operational procedures. The consistency matters because it makes cross-cutting queries possible. If every incident file has a Customer Impact section in the same format, you can ask “which customers were affected across all incidents” and get a reliable answer.&lt;/p&gt;

&lt;p&gt;The last section is a “Why / How to apply” footer. This is what distinguishes the system from a simple note archive. It encodes judgment: not just what happened, but when this knowledge should influence a decision. A memory file about a database failover incident might end with “Apply when: evaluating connection pool sizing for new services, or when diagnosing intermittent timeout errors in services that share the same database cluster.” This lets the system surface relevant context proactively rather than only responding to exact keyword matches.&lt;/p&gt;

&lt;h4 id=&quot;the-index&quot;&gt;The Index&lt;/h4&gt;

&lt;p&gt;A single file called MEMORY.md serves as the table of contents. Each entry is one line, organized by topic rather than chronologically. This file is always loaded into conversation context, so it acts as a retrieval hint. When a question comes in, the system scans the index to decide which detailed files to read. It does not need to open every file on every question. It reads the index, identifies the relevant files by their one-line descriptions, and pulls only what is needed.&lt;/p&gt;

&lt;p&gt;This is the same principle behind the AGENTS.md approach described in OpenAI’s harness engineering experiment: give the system a map, not a thousand-page instruction manual. A short index with pointers to deeper sources of truth scales far better than a monolithic document that tries to contain everything.&lt;/p&gt;

&lt;h4 id=&quot;the-type-system&quot;&gt;The Type System&lt;/h4&gt;

&lt;p&gt;Four memory types serve different purposes, and keeping them separate matters for retrieval quality.&lt;/p&gt;

&lt;p&gt;Project memories capture incidents, architecture decisions, and ongoing initiatives. These are the most frequently created and the most likely to go stale. An incident memory is useful for weeks or months after the event. An architecture memory stays relevant until the architecture changes.&lt;/p&gt;

&lt;p&gt;User memories record who the person is, their role, and their preferences. These help the system tailor its responses. An engineer asking about a service gets different context than an executive asking about the same service.&lt;/p&gt;

&lt;p&gt;Feedback memories capture corrections and confirmed approaches. When someone says “don’t mock the database in these tests” or “yes, the single bundled PR was the right call here,” that guidance gets recorded so it carries forward. Corrections are easy to notice. Confirmations are quieter but equally important. If you only save corrections, the system avoids past mistakes but drifts away from approaches that have already been validated.&lt;/p&gt;

&lt;p&gt;Reference memories are pointers to external systems. They record that bugs are tracked in a specific Linear project, or that a particular Grafana dashboard is what on-call watches. The system cannot access those external systems directly, but knowing where to look is often enough to be useful.&lt;/p&gt;

&lt;h4 id=&quot;why-separation-and-structure-matter&quot;&gt;Why Separation and Structure Matter&lt;/h4&gt;

&lt;p&gt;The key design choices are separation and structure. Each topic gets its own file rather than appending to a single document. This means updates do not risk corrupting unrelated knowledge. If you need to update the customer impact section of an incident memory, you are editing one file, not navigating a sprawling document where a misplaced edit could break something else.&lt;/p&gt;

&lt;p&gt;Consistent internal structure makes the system queryable in ways that unstructured notes never are. When every incident file follows the same template, questions that span multiple incidents become straightforward. “What patterns appear across recent incidents?” is answerable because the data is organized the same way every time.&lt;/p&gt;

&lt;h4 id=&quot;where-this-approach-fits&quot;&gt;Where This Approach Fits&lt;/h4&gt;

&lt;p&gt;This works well for teams managing dozens of services with recurring incidents, complex customer relationships, and institutional knowledge that would otherwise be lost to staff turnover or buried in chat history. The structured format makes it possible to ask questions that span multiple knowledge domains without manually correlating information from separate systems.&lt;/p&gt;

&lt;p&gt;The limitation is staleness. Memory files are snapshots. Tickets close, customers resolve issues, architecture changes. The system mitigates this by treating memories as starting context to verify against current state, not as authoritative truth. Before acting on a memory that names a specific function or file path, the system checks whether it still exists. A memory that says “X exists” is not the same as “X exists now.”&lt;/p&gt;

&lt;p&gt;Active maintenance is still required. Outdated files need to be updated or removed. But the cost of that maintenance is low compared to the alternative: rebuilding context from scratch every time someone asks a question that the team answered three months ago in a chat thread that nobody can find.&lt;/p&gt;

&lt;h4 id=&quot;references&quot;&gt;References&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://code.claude.com/docs/en/memory.md&quot;&gt;Claude Code Memory Documentation&lt;/a&gt; covers CLAUDE.md files, auto memory, and the MEMORY.md index system&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/overview&quot;&gt;Claude Code Overview&lt;/a&gt; for the broader context on how Claude Code works as an agentic coding tool&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.openai.com/index/harness-engineering/&quot;&gt;Harness Engineering (OpenAI)&lt;/a&gt; describes the AGENTS.md approach to repository knowledge management referenced in this post&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 24 Apr 2026 00:00:00 +0000</pubDate>
        <link>https://onsitereliability.com//Building-a-Knowledge-Management-System-with-Structured-Memory/</link>
        <guid isPermaLink="true">https://onsitereliability.com//Building-a-Knowledge-Management-System-with-Structured-Memory/</guid>
        
        <category>AI</category>
        
        <category>management</category>
        
        
        <category>Engineering</category>
        
      </item>
    
  </channel>
</rss>
