Comments on this blog now wait for two things: the section in view, and a yes.
Until you accept, there is no request. Decline and the list stays hidden. Accept and the island fetches the comments. The Worker sets a first-party pe_session cookie, writes one view row, and returns the list.
What the cookie is for
The cookie is signed, HttpOnly, and lasts thirty days. It carries a session id. After you post, it also carries the name and email you submitted — for the Worker, not for the page.
The page never reads that cookie. Name and email in the form come from localStorage after a successful post. The next visit can skip the card if you already accepted.
We do not store a session row. The cookie is the session. Missing or invalid on GET mints a new one. Missing on POST is a 401. Writes are still Turnstile plus one comment per minute per address, and now five per hour per session.
What we record
One comment_views row per accepted load: session id, post slug, time, and the country Cloudflare already has on the request. That is the analytics. There is no site-wide banner and no third-party script.
A raw GET to /api/comments still mints a cookie and writes a view. The consent card is the human path. The Worker cannot see localStorage.
What did not change
Posts are still static files. The Worker still only answers /api/*. Same two endpoints. Email is still never in the public list. Replies still nest one level.
Search, a PWA, an admin UI, and media uploads stay named absences.
Comments
Comments load when this section is visible.
To load comments we set a first-party session cookie and record that you opened this section. Name and email stay in this browser after you post.