<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Essay · XB Field Notes</title><link>https://www.xbcatm.com/tags/essay/</link><description>Illustrated field notes on HTTP, DNS, TLS, Caddy, SSH, releases, and recovery.</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://www.xbcatm.com/tags/essay/index.xml" rel="self" type="application/rss+xml"/><item><title>Readable evidence before dashboards</title><link>https://www.xbcatm.com/essays/readable-evidence-before-dashboards/</link><pubDate>Tue, 18 Aug 2026 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/essays/readable-evidence-before-dashboards/</guid><description>A live site's health is better proven by headers an operator can read in a terminal than by graphs that aggregate the same facts with a lag.</description><content:encoded>&lt;p&gt;Monitoring dashboards are convenient, but convenience is not the same as evidence. This essay argues that for a small static service, the most auditable form of &amp;ldquo;is the site healthy?&amp;rdquo; is a set of plain-text facts an operator can read, re-run, and paste into a report — not an aggregated panel.&lt;/p&gt;
&lt;p&gt;The distinction matters because the two answer different questions. A dashboard is a lens over many samples, tuned to alert you to trends. A probe is a single, reproducible observation of the public edge. When the job is to prove a release or diagnose a specific request, the probe is the higher-fidelity artifact.&lt;/p&gt;
&lt;h2 id="the-dashboards-aggregation-is-the-risk"&gt;The dashboard&amp;rsquo;s aggregation is the risk&lt;/h2&gt;
&lt;p&gt;A dashboard turns thousands of raw observations into a series of aggregated panels: average latency, error rate, uptime percentage. Aggregation is where the audit trail thins. An &amp;ldquo;uptime 99.98%&amp;rdquo; figure does not tell you which request failed, which header was wrong, or whether today&amp;rsquo;s release is the one being served. It is a summary that erases the evidence that produced it unless you keep the raw log elsewhere.&lt;/p&gt;
&lt;p&gt;For a confidence property like &amp;ldquo;the edge serves the tree I staged&amp;rdquo;, a dashboard is the wrong tool. That property is answered by a single deterministic observation, and a terminal command can answer it exactly once.&lt;/p&gt;
&lt;h2 id="a-probe-you-can-read"&gt;A probe you can read&lt;/h2&gt;
&lt;p&gt;The site ships a small read-only helper, &lt;code&gt;downloads/check-http-headers.sh&lt;/code&gt;, that prints the served response in plain text:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;HTTP/2 200
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;content-type: text/html; charset=utf-8
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;content-security-policy: default-src &amp;#39;self&amp;#39;; …; script-src &amp;#39;none&amp;#39;; …
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A human reads this line and knows three things at once: the status is 200, the type is HTML, and the no-script policy is actually being served. Compare that to a panel that says &amp;ldquo;edge OK&amp;rdquo; — the panel requires trust in whatever computed it; the header line is itself the evidence. The certificate is judged the same way, by reading &lt;code&gt;notAfter&lt;/code&gt; as a date rather than a color in a panel.&lt;/p&gt;
&lt;h2 id="replace-signal-loss-with-a-checkable-statement"&gt;Replace signal loss with a checkable statement&lt;/h2&gt;
&lt;p&gt;The mechanism the site favors instead of a dashboard is a contract enforced during build and a set of deterministic commands for the release window. Roughly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Offline checks validate the artifact (every internal link, every SVG, the no-script boundary, the checksums).&lt;/li&gt;
&lt;li&gt;A single read-only probe against the public edge confirms status, headers, and certificate in one invocation.&lt;/li&gt;
&lt;li&gt;A byte manifest ties the served tree to the staged tree so &amp;ldquo;agrees with what we staged&amp;rdquo; is a hash, not a feeling.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these is a sentence an operator can paste into an issue or a changelog and a future auditor can re-run. None of them requires a panel to be understood.&lt;/p&gt;
&lt;h2 id="when-you-genuinely-need-a-dashboard"&gt;When you genuinely need a dashboard&lt;/h2&gt;
&lt;p&gt;None of this says observability is pointless. If you operate many instances, aggregate capacity, or long trends, a dashboard is the right view to see the forest. The claim is narrower: when the question is &amp;ldquo;is this one release healthy at the public edge&amp;rdquo;, the answer lives in a reproducible terminal read, and bolting a panel on top adds latency and aggregation loss without adding evidence.&lt;/p&gt;
&lt;p&gt;The decision heuristic is simple. If a statement can be checked with one command and its output is the proof, prefer the command and keep dashboard only where you genuinely need the aggregate. That keeps the site&amp;rsquo;s own operational story as auditable as the site&amp;rsquo;s content.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://curl.se/docs/manpage.html"&gt;Curl documentation&lt;/a&gt;, reading response headers from a single invocation, checked 2026-08-18.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://12factor.net/logs"&gt;Twelve-Factor: Logs&lt;/a&gt;, treating logs as event streams an operator can read, checked 2026-08-18.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/"&gt;OpenTelemetry: Observability primer&lt;/a&gt;, observability as high-cardinality, contextual output, checked 2026-08-18.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Cache validation is two clocks, not one freshness setting</title><link>https://www.xbcatm.com/essays/caching-as-two-clocks/</link><pubDate>Fri, 14 Aug 2026 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/essays/caching-as-two-clocks/</guid><description>HTTP caching is a freshness clock and a validation clock; a static file shows why you need both to avoid serving stale or re-downloading bytes.</description><content:encoded>&lt;p&gt;Caching is the easiest place to introduce a silent correctness bug, because a cache looks correct right up until it serves bytes that do not match the origin. The mental model that keeps this straight treats every cache as decoding two independent clocks instead of one expiry value.&lt;/p&gt;
&lt;p&gt;A conditional request carries two kinds of information, and conflating them is how stale content ships. One clock is &lt;strong&gt;freshness&lt;/strong&gt; — how long a response may be reused without asking. The other is &lt;strong&gt;validation&lt;/strong&gt; — how a client asks whether its copy is still current. This essay reads both clocks through this site&amp;rsquo;s live response and shows what each one is actually for.&lt;/p&gt;
&lt;h2 id="freshness-how-long-to-trust-a-byte-without-asking"&gt;Freshness: how long to trust a byte without asking&lt;/h2&gt;
&lt;p&gt;Freshness is the clock that decides whether a request is served from the cache at all. It is normally expressed as &lt;code&gt;Cache-Control&lt;/code&gt; (&lt;code&gt;max-age&lt;/code&gt;) or an &lt;code&gt;Expires&lt;/code&gt; date. Within the freshness window, a cache returns the stored response and never contacts the origin. Its job is to reduce latency and origin load; its cost is that it can serve a version slightly behind the origin within the window.&lt;/p&gt;
&lt;p&gt;For a static site the freshness window and the immutable artifact agree: a deploy replaces the whole tree, so a short freshness plus a validator is a safe, boring default. The important thing is that freshness alone cannot detect stale content after the window expires — that is the second clock&amp;rsquo;s job.&lt;/p&gt;
&lt;h2 id="validation-how-a-client-asks-is-it-still-current"&gt;Validation: how a client asks &amp;ldquo;is it still current?&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;When freshness expires, the client does not necessarily re-download the whole resource. Validation is the conditional request: send the stored identifier, and the origin replies either with the new representation or with a status saying the stored copy is still current. The two identifiers HTTP defines are the &lt;code&gt;ETag&lt;/code&gt; (an opaque validator, typically derived from entity bytes) and &lt;code&gt;Last-Modified&lt;/code&gt; (a timestamp validator).&lt;/p&gt;
&lt;p&gt;The live response of this site shows both clocks concretely:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;etag: &amp;#34;dkx84jwth7nkc9e&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;last-modified: Mon, 24 Aug 2026 00:00:00 GMT
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A conditional &lt;code&gt;If-None-Match&lt;/code&gt; request against that ETag yields a &lt;code&gt;304 Not Modified&lt;/code&gt; — the validators agree, so the client reuses the cached copy without re-downloading the body. This is the second clock doing its job: a cheap round trip, not a full transfer.&lt;/p&gt;
&lt;h2 id="why-freshness-and-validation-are-independent"&gt;Why freshness and validation are independent&lt;/h2&gt;
&lt;p&gt;The two clocks must be understood separately because one does not imply the other. A response can be freshly cached and need no validation, yet the validation identifiers still matter for the moment freshness lapses. Conversely you can have strong validators but a freshness policy that causes needless revalidation for resources that barely change. For a static file the two are easy to set coherently: a short or public freshness, a strong ETag from the artifact bytes, and a revalidation path the client uses the moment freshness passes.&lt;/p&gt;
&lt;p&gt;The failure mode to watch is collapsing both into &amp;ldquo;the cache expired, so re-download&amp;rdquo;. That ignores validators and turns every expiry into a full transfer. The compressed artifact makes the round trip small — this page&amp;rsquo;s HTML transfers as a few kilobytes — but the principle (the validators principle (a cache refuses stale but avoids redundant whole-body transfer) holds regardless of size.&lt;/p&gt;
&lt;h2 id="the-contract-worth-asserting"&gt;The contract worth asserting&lt;/h2&gt;
&lt;p&gt;Once you can name freshness and validation separately, a release check can assert them: the served headers include both &lt;code&gt;etag&lt;/code&gt; and &lt;code&gt;last-modified&lt;/code&gt;; the freshness window is short enough to be honest; and a conditional request returns &lt;code&gt;304&lt;/code&gt; when the validator matches. Those three checks describe a cache that cannot silently serve indefinitely-old bytes or leak bandwidth. That is a weaker word to prove than dashboard graphs, which is why it belongs in a decision log rather than in a monitoring chart.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching"&gt;MDN: HTTP caching&lt;/a&gt;, expiration and validation as distinct mechanisms, checked 2026-08-14.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag"&gt;MDN: HTTP &lt;code&gt;ETag&lt;/code&gt;&lt;/a&gt;, conditional request validation, checked 2026-08-14.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9111"&gt;RFC 9111: HTTP Caching&lt;/a&gt;, cache expiration and validation rules, checked 2026-08-14.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>The zero-JavaScript static site is a defensible choice</title><link>https://www.xbcatm.com/essays/zero-js-as-a-defensible-choice/</link><pubDate>Thu, 13 Aug 2026 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/essays/zero-js-as-a-defensible-choice/</guid><description>Why turning off scripting on a content site is a security and reliability decision you can justify with headers, not taste.</description><content:encoded>&lt;p&gt;A static site does not need JavaScript to be defensible; the absence of a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag can itself be the point. This essay argues the engineering case for shipping a site whose script lanes are closed by policy, then proves the claim with the header this publication actually serves.&lt;/p&gt;
&lt;p&gt;The argument has three parts: the threat surface you delete, the contract that makes deletion observable, and the cases where deleting the layer is the wrong call.&lt;/p&gt;
&lt;h2 id="the-threat-surface-you-delete-by-not-having-a-script-lane"&gt;The threat surface you delete by not having a script lane&lt;/h2&gt;
&lt;p&gt;A page that executes no own or third-party script removes an entire class of failure and abuse at once. Cross-site scripting lives in a document that can run injected code; a document that cannot run code at all has nothing for an injection to escalate into. Third-party trackers and widgets are gone because there is no script tag to embed them. Supply-chain risk from accidental dependency drift in the browser bundle is gone because there is no bundle. Each of these is a bounded, nameable surface — the small-systems property of choosing boundaries you can articulate.&lt;/p&gt;
&lt;p&gt;None of this claims the site is &amp;ldquo;secure&amp;rdquo;. It claims the attack surface is narrower and, crucially, easier to reason about because it is enumerated by construction rather than by audit.&lt;/p&gt;
&lt;h2 id="the-header-that-makes-the-boundary-enforceable"&gt;The header that makes the boundary enforceable&lt;/h2&gt;
&lt;p&gt;A taste is not a policy. The difference here is that the no-script contract is enforced by the server, not by developer convention. The live response of this site carries:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;content-security-policy: default-src &amp;#39;self&amp;#39;; base-uri &amp;#39;self&amp;#39;; form-action &amp;#39;none&amp;#39;; frame-ancestors &amp;#39;none&amp;#39;; object-src &amp;#39;none&amp;#39;; script-src &amp;#39;none&amp;#39;; style-src &amp;#39;self&amp;#39;; img-src &amp;#39;self&amp;#39; data:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;script-src 'none'&lt;/code&gt; directive is the enforceable part. Even if a future release lets a stray &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; into a page, the browser refuses to execute it. The absence of JavaScript is therefore a served contract, checked by the verifier on every build (&lt;code&gt;script-free&lt;/code&gt;), not a hope. This is the difference between saying &amp;ldquo;we do not use JS&amp;rdquo; and &amp;ldquo;the public edge refuses to run JS&amp;rdquo;.&lt;/p&gt;
&lt;h2 id="what-the-static-structure-buys-after-the-removal"&gt;What the static structure buys after the removal&lt;/h2&gt;
&lt;p&gt;With no scripting, the content model reverts to something closer to published documents. Caching is simpler because a static file has a stable byte identity and an ETag rather than a mutable DOM. Reading the served HTML in a terminal, using &lt;code&gt;view-source&lt;/code&gt;, or indexing the tree is straightforward because the artifact is ordinary files on disk. The trade the site accepts is deliberate: no interactive state, no client-side rendering, no feedback that needs a view model. For a documentation and operations site that is the correct exchange, and it is worth writing down.&lt;/p&gt;
&lt;h2 id="when-the-zero-js-claim-is-the-wrong-call"&gt;When the zero-JS claim is the wrong call&lt;/h2&gt;
&lt;p&gt;The boundary is a feature, not a badge of purity. A site that genuinely needs client-side interactivity — a realtime dashboard, a document editor, a payment flow — should not contort itself into serving static HTML and then bolting on a workaround. The wrong application of this decision is to shave a product to fit the constraint. The right application is to recognize that content that is read, searched, and archived profits from being a document, and to reserve scripting for where a document is insufficient.&lt;/p&gt;
&lt;p&gt;For this property, the served header is the evidence, and it is reproducible on any release by reading the public edge. That is the difference between a preference and a deployed decision.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src"&gt;MDN: Content Security Policy (&lt;code&gt;script-src&lt;/code&gt;)&lt;/a&gt;, directives that disable script execution, checked 2026-08-13.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portswigger.net/web-security/cross-site-scripting"&gt;PortSwigger: What is cross-site scripting (XSS)?&lt;/a&gt;, the attack class removed when scripts cannot run, checked 2026-08-13.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://owasp.org/www-community/controls/Content_Security_Policy"&gt;OWASP: Content Security Policy&lt;/a&gt;, CSP as a defense-in-depth control, checked 2026-08-13.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>