<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet type="text/xsl" href="/feed.xsl"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Blogging on jvalol</title><link>https://jva.lol/categories/blogging/</link><description>Recent content in Blogging on jvalol</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 14 Aug 2026 17:26:50 -0600</lastBuildDate><atom:link href="https://jva.lol/categories/blogging/index.xml" rel="self" type="application/rss+xml"/><item><title>My comment section is static HTML</title><link>https://jva.lol/weblog/my-comment-section-is-static-html/</link><pubDate>Fri, 14 Aug 2026 17:26:50 -0600</pubDate><guid>https://jva.lol/weblog/my-comment-section-is-static-html/</guid><description>Static HTML comment section, no libraries, dependencies. Baked in by design.</description><content:encoded>&lt;h2 id="the-comment-section-here-is-static-html-reading-costs-nobody-anything"&gt;
The comment section here is static HTML. Reading costs nobody anything.
&lt;a class="anchor" href="#the-comment-section-here-is-static-html-reading-costs-nobody-anything"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Posting a comment does take a little more. It&amp;rsquo;s how I keep guard against bots and spammers, etc.&lt;/p&gt;
&lt;p&gt;This blog is a Hugo site. It has a comment section. If you view source on
any post with comments, you will find them in the HTML, because they are
compiled into the page at build time like everything else. There is no
widget, no iframe, no tracker, and no third-party script, and loading a
post makes no request to any server but this one. The page does carry
some JavaScript (the site search, a few small comforts), but the
comments never need it: with JavaScript off you can read every comment
and post one.&lt;/p&gt;
&lt;h2 id="the-shape"&gt;
The shape
&lt;a class="anchor" href="#the-shape"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The comments live on a small Phoenix server I run, called Popsicle Boat.
Each post has a companion thread there; publishing a post makes one API
call that creates the thread and writes its URL into the post&amp;rsquo;s front
matter. That URL is the only coupling between the two systems.&lt;/p&gt;
&lt;p&gt;At build time, Hugo fetches each thread&amp;rsquo;s &lt;code&gt;replies.json&lt;/code&gt; and renders the
replies as plain HTML once per post per build, cached, capped
indentation. If the server is unreachable, the build carries on and
renders no comments. The comment system cannot break the blog.&lt;/p&gt;
&lt;p&gt;Below the replies is a plain HTML form: a textarea and a submit button,
POSTing to the boat. No script, no cookie, no request on pageview. The
boat walks you through signing in if needed, you confirm, and it sends
you back to the post with &lt;code&gt;#just-posted&lt;/code&gt; on the URL. A hidden note
appears via CSS &lt;code&gt;:target&lt;/code&gt; to say your comment is on its way. It is: the
boat pings my build hook, and about a minute later the comment is part
of the page. One of the small comforts is that it refreshes for you when it lands. A manual refresh does the same.&lt;/p&gt;
&lt;h2 id="why-this-shape"&gt;
Why this shape
&lt;a class="anchor" href="#why-this-shape"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Reading is static. Writing is dynamic. That is the whole idea.&lt;/p&gt;
&lt;p&gt;A reader costs nothing and depends on nothing. The page they get here is the
page I built, comments included. A writer touches the server once, at
the moment of writing, which is the point where a server is not just
useful, but like a moat: identity, spam defense, notifications. A comment writer is protected, a website owner (like me) is notified when written to.&lt;/p&gt;
&lt;p&gt;The durability falls out for free. The comments are in my generated
pages, so they stay with the blog no matter what happens to the comment
server. Most comment systems get this backwards: the conversation lives
on their infrastructure and your page borrows it at read time, every
read, forever.&lt;/p&gt;
&lt;p&gt;There is no captcha. Spam defense is the boat&amp;rsquo;s job and stays invisible:
a honeypot, time-to-submit checks, rate limits, and the sign-in step
itself. The boat is, to put it simply, the guard at the gate.&lt;/p&gt;
&lt;h2 id="the-honest-costs"&gt;
The honest costs
&lt;a class="anchor" href="#the-honest-costs"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Comments lag by a build. About a minute here on this blog.&lt;/p&gt;
&lt;p&gt;Commenting requires an account on the boat. That stops some people. I
keep it because a signed name is what makes the no-captcha posture
possible.&lt;/p&gt;
&lt;p&gt;If the boat is down during a build, that build renders the affected
posts without new comments until a later build picks them up. I prefer a
page that sometimes forgets to a page that sometimes fails.&lt;/p&gt;
&lt;h2 id="if-you-want-this"&gt;
If you want this
&lt;a class="anchor" href="#if-you-want-this"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The API is written up in one page: create a thread, get replies as JSON, point a build
hook at your host. Documented at
&lt;a href="https://www.popsicleboat.com/for-bloggers/api"&gt;https://www.popsicleboat.com/for-bloggers/api&lt;/a&gt; — you mint your own token,
shown once, to you. No one else sees it.&lt;/p&gt;</content:encoded></item></channel></rss>