<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>blog.vrypan.net</title>
    <link>https://blog.vrypan.net/</link>
    <description>blog.vrypan.net</description>
    <lastBuildDate>Sat, 18 Jul 2026 14:28:06 +0300</lastBuildDate>
    <generator>bckt</generator>
    <atom:link href="https://blog.vrypan.net/rss.xml" rel="self" type="application/rss+xml"/>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/07/18/note-74085/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/18/note-74085/</guid>
      <pubDate>Sat, 18 Jul 2026 14:28:06 +0300</pubDate>
      <description>Debating anti-LLM zealots and toxicity is a huge waste of time and energy. Just wait and they will become irrelevant; they have a short expiration time.</description>
      <content:encoded><![CDATA[
	
	<p>Debating anti-LLM zealots and toxicity is a huge waste of time and energy.
Just wait and they will become irrelevant; they have a short expiration time.</p>

]]></content:encoded>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/07/18/note-72324/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/18/note-72324/</guid>
      <pubDate>Sat, 18 Jul 2026 13:58:44 +0300</pubDate>
      <description>Z80 has turned 50. My Amstrad CPC 128 had a Z80, it was where I learned assembly. I can't help the nostalgia for a time when young me was discovering the beauty of being able to control my computer at the lowest level, to read and write directly from and to the video RAM, to jump...</description>
      <content:encoded><![CDATA[
	
	<p><a href="https://goliath32.com/blog/z80.html">Z80 has turned 50</a>.
My Amstrad CPC 128 had a Z80, it was where I learned
assembly. I can't help the nostalgia for a time when
young me was discovering the beauty of being able to
control my computer at the lowest level, to read and write
directly from and to the video RAM, to jump around
memory addresses. Locomotive BASIC (Amstrad's version of BASIC),
and Turbo Pascal (when I booted in CP/M) were nice,
but the empowerment of writing code in assembly was a
class of its own. Today, I ask AI to write most of
my code; we've gone such a long way...</p>

]]></content:encoded>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/07/16/260716-note-20011/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/16/260716-note-20011/</guid>
      <pubDate>Thu, 16 Jul 2026 19:40:12 +0300</pubDate>
      <description>bckt v0.7.5 is out, with lots of performance improvements that make a huge difference for blogs with a large number of posts. Get it here or upgrade with brew upgrade bckt.</description>
      <content:encoded><![CDATA[
	
	<p>bckt v0.7.5 is out, with lots of performance improvements
that make a huge difference for blogs with a large number of
posts. <a href="https://github.com/vrypan/bckt/releases">Get it here</a> or
upgrade with <code>brew upgrade bckt</code>.</p>

]]></content:encoded>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/07/16/260716-spacex-bellow-ipo-price/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/16/260716-spacex-bellow-ipo-price/</guid>
      <pubDate>Thu, 16 Jul 2026 10:11:49 +0000</pubDate>
      <description>SPACEX price bellow IPO opening price.</description>
      <content:encoded><![CDATA[
	
	<p>SPACEX price bellow IPO opening price.</p>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/07/16/260716-spacex-bellow-ipo-price/diagram.png"
   type="image/png"
   length="292862"/>

    </item>
    
    <item>
      <title>Verifiable AI inference</title>
      <link>https://blog.vrypan.net/2026/07/14/verifiable-ai-inference/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/14/verifiable-ai-inference/</guid>
      <pubDate>Tue, 14 Jul 2026 18:06:28 +0000</pubDate>
      <description>AI agents are increasingly being used to review code, summarize documents, analyze contracts, and answer questions. In many cases, the result itself matters less than its authenticity.</description>
      <content:encoded><![CDATA[
	
	<p>AI agents are increasingly being used to review code, summarize documents, analyze contracts, and answer questions. In many cases, the result itself matters less than its authenticity.</p>
<p>Suppose Alice wants to share <a href="/2026/07/14/code-security-review-skill/">an AI-generated security review</a> with Bob. Bob wants to know that the review is exactly what a particular agent produced for a particular input.</p>
<p>In other words, Bob wants evidence that</p>
<pre><code>output = Agent(input)
</code></pre>
<p>for a specific agent, model version, configuration, and input, <strong>without running the same inputs, and spending tokens and time</strong>.</p>
<p>Today, there is no standard way to prove this.</p>
<hr />
<p>The simplest solution is to rely on a <strong>trusted authority</strong>.</p>
<p>An inference service—or an independent auditor—runs the agent and signs the result.</p>
<p>The certificate could contain:</p>
<pre><code>Agent: OpenAI Code Security Review
Model: GPT-5.5
Agent version: v3
Input hash: SHA256(...)
Output hash: SHA256(...)
Timestamp: ...
Signature: ...
</code></pre>
<p>This does not prove that the model executed correctly. It proves that the trusted authority attests that it produced this output for this input.</p>
<p>For many applications, that is already enough. It is no different from trusting a software publisher's code signature or a Certificate Authority on the web.</p>
<p>AI providers, code hosting platforms, or independent auditors could all publish signed attestations for AI-generated artifacts.</p>
<hr />
<p>A stronger goal is to remove the trusted authority entirely.</p>
<p>Instead of relying on a signature, the inference itself would produce a cryptographic proof that the output is exactly the result of evaluating the specified model on the specified input.</p>
<p>Verification would no longer depend on trusting the service that performed the inference. Anyone could verify the proof independently.</p>
<p>This is an active area of research. Zero-knowledge proofs, zkVMs, and other verifiable computation techniques are making progress, but generating proofs for frontier AI models remains expensive.</p>
<hr />
<p>Verifiable inference would make AI outputs much more useful: <strong>IF</strong> you trust an AI agent to do a job, then verifiable inference
will allow us to do it once and reuse the reuslt.</p>
<p>A code review could be accompanied by proof that it was generated by a specific review agent. A legal analysis could be verified to have come from a particular model version. Researchers could cite AI-generated analyses with confidence that readers are seeing the original output.</p>
<hr />
<h2>Further reading</h2>
<ul>
<li>
<p><strong>EZKL</strong> — An open-source toolkit for generating zero-knowledge proofs of machine learning inference. One of the most mature implementations of verifiable ML today. <a href="https://github.com/zkonduit/ezkl">https://github.com/zkonduit/ezkl</a>  <a href="https://github.com/zkonduit/ezkl?utm_source=chatgpt.com">oai_citation:0‡GitHub</a></p>
</li>
<li>
<p><strong>ZKML: An Optimizing System for ML Inference in Zero-Knowledge Proofs</strong> (EuroSys 2024) — One of the first papers to demonstrate zero-knowledge proofs for non-trivial machine learning models.  <a href="https://doi.org/10.1145/3627703.3650088?utm_source=chatgpt.com">oai_citation:1‡DOI</a></p>
</li>
<li>
<p><strong>A Survey of Zero-Knowledge Proof Based Verifiable Machine Learning</strong> — A comprehensive overview of the field, including verifiable inference, training, current systems, and open challenges.  <a href="https://arxiv.org/abs/2502.18535?utm_source=chatgpt.com">oai_citation:2‡arXiv</a></p>
</li>
<li>
<p><strong>Verifiable ML Model Inference (ERC-7992 Draft)</strong> — A proposed Ethereum standard for identifying ML models and verifying cryptographic proofs of inference. Interesting as an example of how verifiable inference could become interoperable.  <a href="https://ercs.ethereum.org/ERCS/erc-7992?utm_source=chatgpt.com">oai_citation:3‡Ethereum Improvement Proposals</a></p>
</li>
</ul>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/07/14/verifiable-ai-inference/og.png"
   type="image/png"
   length="232010"/>

    </item>
    
    <item>
      <title>A code-security-review SKILL.md</title>
      <link>https://blog.vrypan.net/2026/07/14/code-security-review-skill/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/14/code-security-review-skill/</guid>
      <pubDate>Tue, 14 Jul 2026 17:10:14 +0000</pubDate>
      <description>I install a lot of small tools. A CLI from someone's GitHub, a Homebrew tap with twelve stars, a Rust binary that does one thing well. This is how I like software, and there has never been more of it.</description>
      <content:encoded><![CDATA[
	
	<p>I install a lot of small tools. A CLI from someone's GitHub, a Homebrew tap with twelve stars, a Rust binary that does one thing well. This is how I like software, and there has never been more of it.</p>
<p>Unless it’s a really established project, with many eyes on it, it’s a leap of faith. How do I know what the code actually does?</p>
<p>In theory, I could review it. But let’s be realistic, no one is fluent enough in every programming language, and no one has the time to carefully review the code of every tool they want to try; at least not me.</p>
<h2>README is a claim</h2>
<p>Every decent repo has a README, and the README file is a <em>claim</em>. The code is the <em>fact</em>.</p>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">The insight</p>
<p>The real question is what’s the gap between the claim (README) and the fact (code).</p>
</div>
<p>A tool that says &quot;renames files based on EXIF data&quot; has no business making network calls, reading <code>~/.ssh</code>, or spawning subprocesses. If it does, you don't need threat modeling or a CVE database. You've found it. Most malicious code reveals itself exactly at the point where behavior exceeds stated purpose.</p>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">The test is scope-match, not presence.</p>
<p>A P2P sync tool is <em>supposed</em> to open sockets. A password manager is <em>supposed</em> to touch credential stores. Capabilities aren't red flags by themselves.</p>
</div>
<p>So the question becomes: does the capability stay inside the claimed scope? A tool claiming LAN-only sync should have no HTTP client and no hardcoded external hosts. A tool claiming &quot;offline, no telemetry&quot; should have zero outbound capability, full stop. The narrower the claim, the sharper the check — and a violated narrow claim is the strongest finding you can get.</p>
<p><strong>This is the kind of task LLM agents happen to be good at.</strong> They're patient, they read every file, and they don't get bored on line 400. Grep the dependency manifest, check every network call, decode every base64 blob, read every <code>if</code> that branches on the current date. It's mechanical.</p>
<h2>The audit-repo skill</h2>
<p>So I wrote a skill that does exactly this. It's structured around the README-vs-code principle rather than a generic checklist:</p>
<ol>
<li><strong>Establish the claim.</strong> Confirm the repo is the one you meant (not a typosquat), record the exact commit hash, and write down in one sentence what the project says it does — including any scope limits it declares for itself (&quot;offline&quot;, &quot;LAN-only&quot;, &quot;read-only&quot;).</li>
<li><strong>Read the code against that claim.</strong> Network activity, filesystem access outside the working directory, environment scraping, dynamic execution, persistence (cron, systemd, <code>.zshrc</code>, LaunchAgents), obfuscation, self-downloading payloads. Then the ones a grep alone misses: credential-store access, clipboard and screen capture, environment fingerprinting, time-gated behavior, covert exfiltration through a channel that <em>looks</em> legitimate, unsafe deserialization, insecure randomness for secrets, hardcoded backdoor conditionals.</li>
<li><strong>Check the dependencies.</strong> Typosquats, wide-open version ranges, git-URL deps, and above all install-time scripts — <code>postinstall</code> runs before any other part of your review matters.</li>
<li><strong>Sandbox anyway.</strong> A clean read is not a guarantee.</li>
</ol>
<p>It also records <em>positive</em> findings: stripping setuid bits from received files, redacting secrets by default, atomic writes. Deliberate defensive choices tell you something about the author.</p>
<p>The output is a short prose report — findings, verdict, and a reliability note kept separate from the malice verdict, because a tool can be perfectly honest and still eat your files.</p>
<p>Finally, a <strong>report card</strong>: a color-coded label, one row per category, styled after the privacy nutrition labels on app stores, except each row is graded against the stated claim rather than scored in isolation. Green means &quot;matches the claim or absent&quot;. Red means &quot;undisclosed, or contradicts the claim&quot;. You can read it in three seconds.</p>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">Example: shg review</p>
<p>The review for <a href="https://github.com/vrypan/shg">shg</a> generated this card: <a href="https://claude.ai/code/artifact/dda07f29-e634-4e14-892d-456bb1603958">https://claude.ai/code/artifact/dda07f29-e634-4e14-892d-456bb1603958</a></p>
</div>
<hr />
<p><strong><a href="https://github.com/vrypan/skills">Get the code-security-review skill from github.com/vrypan/skills</a></strong>. Install it and next time, before you install
a cool new tool, ask your AI agent <em>&quot;perform a security review of <code>https://...</code>&quot;</em>.</p>
<hr />
<div class="markdown-alert markdown-alert-caution">
<p class="markdown-alert-title">Attention!</p>
<p>AI agents are getting really good. But this does not mean you can trust their security assesment blindly.
That said, performing one using an agent is probably 100x better than nothing.</p>
<p>The verdict is pinned to a <strong>commit hash</strong> — a review of <code>b70c44b</code> says nothing about <code>b2e6140</code>.</p>
<p>It also <strong>depends on your agent</strong>. A weaker model will miss more, a frontier model will get you a more reliable assessment.</p>
</div>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/07/14/code-security-review-skill/fargo-review.png"
   type="image/png"
   length="344265"/>

   <enclosure url="https://blog.vrypan.net/2026/07/14/code-security-review-skill/lilsync-review.png"
   type="image/png"
   length="394806"/>

   <enclosure url="https://blog.vrypan.net/2026/07/14/code-security-review-skill/og1.png"
   type="image/png"
   length="850341"/>

   <enclosure url="https://blog.vrypan.net/2026/07/14/code-security-review-skill/shg-review.png"
   type="image/png"
   length="321968"/>

    </item>
    
    <item>
      <title>shg, the shell guard</title>
      <link>https://blog.vrypan.net/2026/07/13/260713-shg-the-shell-guard/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/13/260713-shg-the-shell-guard/</guid>
      <pubDate>Mon, 13 Jul 2026 14:35:08 +0000</pubDate>
      <description>Shell history is one source of leaked credentials. Commands containing API keys, bearer tokens, passwords, or connection strings are often written to history files without much thought.</description>
      <content:encoded><![CDATA[
	
	<p>Shell history is one source of leaked credentials. Commands containing API keys, bearer tokens, passwords, or connection strings are often written to history files without much thought.</p>
<p>We shouldn’t do it, but, let’s admit it, we all do it, especially when we feel safe, on a computer entirely under our control.</p>
<pre lang="sh"><code>export OPENAI_API_KEY=sk-...
curl -H &quot;Authorization: Bearer ghp_...&quot; https://api.example.com
psql postgres://alice:password@example.com/db
</code></pre>
<div class="markdown-alert markdown-alert-tip">
<p class="markdown-alert-title">Best practices</p>
<p>There are best practices and tricks that will help you keep secrets from bash/zsh/fish history.
I found <a href="https://medium.com/@prasincs/hiding-secret-keys-from-shell-history-part-1-5875eb5556cc">&quot;Hiding secret keys from shell history: Part 1&quot;</a>
to be extremely useful.</p>
</div>
<p>But it’s not just shell history. It may be a <code>SELECT</code> statement in your <code>~/.mysql_history</code>, or a quick test stored permanently in your <code>~/.python_history</code>, and so on.</p>
<p>And recently, AI coding assistants have introduced one more secret-concentration point. Claude Code, Codex, Gemini CLI, and Copilot CLI keep full session transcripts on disk;  transcripts that may contain secrets copied from <code>.env</code> files, terminal output, or configuration files.</p>
<pre lang="text"><code>User: Connect to the production database.

Tool output:
DATABASE_URL=postgres://alice:password@example.com/db
OPENAI_API_KEY=sk-...
AWS_SECRET_ACCESS_KEY=...
</code></pre>
<hr />
<p><code>shg</code> scans these locations for credentials, and anything that looks like an API key or an access token. It scans shell and REPL histories, environment variables, AI agent command histories, and AI agent session transcripts. Everything runs locally, there is no network access or telemetry, and findings are redacted by default.</p>
<p>For example, running <code>shg scan</code> reports secrets found in shell/REPL history files or environment variables:</p>
<pre lang="text"><code>$ shg scan

[!!!] export OPENAI_API_KEY=s*************...**************5
      ~/.zsh_history:148 [inline_assign]

[!!!] curl -H &quot;Authorization: Bearer g*************...**************5...
      ~/.zsh_history:576 [auth_header]

2 finding(s) detected (2 high, 0 medium, 0 low).
Remove flagged history entries and rotate affected credentials.
</code></pre>
<p><code>shg deep</code> scans AI agent transcripts and groups findings by session instead of by line:</p>
<pre lang="text"><code>$ shg deep

~/.claude/projects/myapp/3f2c….jsonl
  [!!!] known_token   ghp_****…****2345   (3×, tool_output)
        …export GITHUB_TOKEN=ghp_****…****2345…

1 secret(s) across 1 session file(s).
Rotate each credential, delete the affected session files, and make sure
this directory is not synced, committed, or world-readable.
</code></pre>
<hr />
<p>If you are using <code>zsh</code> there is also a way to run <code>shg</code> on every command before it’s stored in history and prevent it from landing there if it contains secrets.</p>
<pre lang="sh"><code># the command executes normally, but it's not saved to history

$ export GH_TOKEN=ghp_381289xj_82j7nk_23
[shg] Warning: possible secret detected — not saved to history.

</code></pre>
<hr />
<p><code>shg</code> can’t eliminate the risk of secret leaks. But it makes it easier to discover accidental exposure before history files or agent transcripts are backed up, synchronized, committed, or shared.</p>
<p><a href="https://github.com/vrypan/shg">shg is currently on v0.2.6</a> and has been significantly improved since <a href="/2026/05/09/shg-the-shell-guard/">the last time</a> I wrote about it.</p>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/07/13/260713-shg-the-shell-guard/og.png"
   type="image/png"
   length="247676"/>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/07/13/260713-note-94671/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/13/260713-note-94671/</guid>
      <pubDate>Mon, 13 Jul 2026 01:17:52 +0300</pubDate>
      <description>polyagency: the practice of engaging in or being open to using multiple AI agents concurrently.</description>
      <content:encoded><![CDATA[
	
	<p><em>polyagency</em>:
the practice of engaging in or being open to using multiple AI agents concurrently.</p>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/07/13/260713-note-94671/og.png"
   type="image/png"
   length="226571"/>

    </item>
    
    <item>
      <title>Chat Control 2.0 (CSAR): a risk list</title>
      <link>https://blog.vrypan.net/2026/07/11/260711-chat-control-2-csar-a-risk-list/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/11/260711-chat-control-2-csar-a-risk-list/</guid>
      <pubDate>Sat, 11 Jul 2026 13:17:57 +0000</pubDate>
      <description>CSAR is a proposed EU Regulation whose purpose is "to prevent and combat child sexual abuse" (procedure 2022/0155(COD)).</description>
      <content:encoded><![CDATA[
	
	<p>CSAR is a proposed EU <strong>Regulation</strong> whose purpose is
&quot;to prevent and combat child sexual abuse&quot;
(procedure 2022/0155(COD))<sup class="footnote-ref"><a href="#fn-1" id="fnref-1" data-footnote-ref>1</a></sup>.</p>
<p>Most of the public opposition to CSAR boils down to &quot;no surveillance&quot; as a
first principle, full stop. On the other side, there are at least some
documented cases where the existing, voluntary Chat Control 1.0 has helped
catch people committing crimes against children — which is usually where the
counter-argument lands: &quot;yes, but we will save the kids&quot;. Both of these are
too short to be useful.</p>
<p>Here are my notes, and a huge list of references for further investigation.</p>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">There are two similarly named EU laws</p>
<p>CSAR is not to be confused with the <strong>Child Sexual Abuse Directive</strong> — a
separate EU law with an almost identical name, currently also under
revision.<sup class="footnote-ref"><a href="#fn-2" id="fnref-2" data-footnote-ref>2</a></sup> The Directive is criminal law: it defines the offences and the
penalties — what counts as child sexual abuse and how it is prosecuted. CSAR
defines what online services must do about it: detection, reporting, and
removal. This post is only about CSAR.</p>
</div>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">What happend on July 9?</p>
<p>On 9 July 2026, the European Parliament voted on a proposal to reject the
Council's extension of the temporary ePrivacy derogation commonly called
&quot;Chat Control 1.0&quot;.<sup class="footnote-ref"><a href="#fn-3" id="fnref-3" data-footnote-ref>3</a></sup></p>
<p>Of the MEPs voting, 314 supported rejection and 276 opposed it. However,
rejecting the Council text required an absolute majority of all Parliament's
members — 361 votes — so the rejection failed.<sup class="footnote-ref"><a href="#fn-4" id="fnref-4" data-footnote-ref>4</a></sup> The temporary framework
allowing providers to voluntarily scan certain private communications for
child sexual abuse material therefore moved forward, with additional
Parliament amendments intended to exclude end-to-end encrypted communications
and limit the scope of scanning.</p>
<p>This vote concerned the temporary &quot;Chat Control 1.0&quot; rules, not the permanent
CSAR proposal commonly called &quot;Chat Control 2.0&quot;.</p>
<p>CSAR remains under
negotiation and has not yet been adopted, so there is currently no confirmed
date on which it will take effect.<sup class="footnote-ref"><a href="#fn-5" id="fnref-5" data-footnote-ref>5</a></sup></p>
</div>
<hr />
<h2>Scanning private communications without consent becomes legal</h2>
<p>EU law is clear that scanning private communications is illegal without
consent.<sup class="footnote-ref"><a href="#fn-6" id="fnref-6" data-footnote-ref>6</a></sup></p>
<p>CSAR practically changes this, by permitting &quot;voluntary&quot; scanning of
content.<sup class="footnote-ref"><a href="#fn-7" id="fnref-7" data-footnote-ref>7</a></sup></p>
<ul>
<li><strong>before</strong>: scanning was prohibited unless covered by a (temporary)
exception.<sup class="footnote-ref"><a href="#fn-3" id="fnref-3-2" data-footnote-ref>3</a></sup></li>
<li><strong>after</strong>: scanning becomes an ordinary, permanent legal possibility under
EU law.</li>
</ul>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">Note</p>
<p>Even the &quot;voluntary&quot; version has been questioned by the Council's
own lawyers, who warned that indiscriminate scanning of private communications
is incompatible with EU fundamental rights.<sup class="footnote-ref"><a href="#fn-8" id="fnref-8" data-footnote-ref>8</a></sup></p>
</div>
<hr />
<h2>On-device scanning is the only technically viable option</h2>
<p>What does scanning mean?</p>
<p>A chat app may scan the content (your texts, photos, videos) either on-device,
in transit (as the message is transmitted) or in storage (when the message is
stored in their servers).</p>
<p>Modern apps apply cryptography to encrypt these messages, and the strongest
model is end-to-end encryption (E2EE). This means that the message is encrypted
before leaving your device and it can only be decrypted on the device of the
recipient. If properly implemented, it is computationally infeasible to decrypt
the message without the keys stored in the recipient's device.</p>
<p>The initial version of CSAR did not exempt end-to-end encrypted services from
detection obligations.<sup class="footnote-ref"><a href="#fn-9" id="fnref-9" data-footnote-ref>9</a></sup> In practice, complying would mean weakening
encryption — for example, by adding backdoors — so that messages <strong>could</strong> be
decrypted without the sender's or recipient's consent. This approach introduces
many problems, including the possibility of a security leak that would
<strong>jeopardize the security of every user's communications</strong> to criminals and
foreign adversaries.</p>
<p>The Commission repeatedly stated that it was not banning end-to-end encryption.
At the same time, depending on the final text, providers may be required — or
permitted — to detect known CSAM (Child Sexual Abuse Material), unknown CSAM,
and grooming.</p>
<p><strong>Which leaves providers with only one option: scan all content, on-device,
before it is encrypted.</strong></p>
<hr />
<h2>Millions of false positives</h2>
<p>So, your phone, or your laptop, will have to scan content you send and receive
to identify CSAM.</p>
<p>Scanning can be classified under two categories:</p>
<ul>
<li>Match a content hash (a digital fingerprint) against a database of known
CSAM hashes.</li>
<li>Try to use machine learning and fuzzy algorithms to identify unknown CSAM.</li>
</ul>
<p>The first one is a mature and very low-error method. The second one is much
more prone to errors.</p>
<p>But here is where big numbers are important. Even 0.1% false positives, across
billions of daily messages, produces enormous volumes of flagged innocent
content — and every flag is a person suspected of one of the worst crimes that
exist.<sup class="footnote-ref"><a href="#fn-10" id="fnref-10" data-footnote-ref>10</a></sup></p>
<p>That photo you sent to your doctor in a moment of despair, when the rash on
your kid's belly looked extremely alarming? It could flag you in some database
as a potential child offender.</p>
<p>Not to mention consensual sexting between teenagers. How would you feel if your
17-year-old, who is texting and exchanging photos with their partner, was
accused of CSAM or grooming? In order to &quot;protect&quot; our kids, we may be actually
harming them.</p>
<p>Keep in mind that wrongful suspicion is not the only consequence in these
cases: flagging may lead to house searches and police database entries.<sup class="footnote-ref"><a href="#fn-11" id="fnref-11" data-footnote-ref>11</a></sup></p>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">Note</p>
<p>The European Parliament's own study concludes that no technology can detect
CSAM or grooming without unacceptable error rates, and that consensual teen
imagery is a known weak spot of the classifiers.<sup class="footnote-ref"><a href="#fn-12" id="fnref-12" data-footnote-ref>12</a></sup></p>
</div>
<hr />
<h2>CSAM scanning is an attack vector</h2>
<p>On-device models and hash databases can be extracted and
reverse-engineered.<sup class="footnote-ref"><a href="#fn-13" id="fnref-13" data-footnote-ref>13</a></sup></p>
<p>Perceptual hashes admit adversarial collisions — i.e. innocent-looking images
crafted to match database hashes. This isn't hypothetical. Shortly after Apple announced NeuralHash in 2021, researchers demonstrated practical hash collisions.<sup class="footnote-ref"><a href="#fn-14" id="fnref-14" data-footnote-ref>14</a></sup></p>
<p>This is a new attack vector: an adversary can send victims content that appears
innocuous but will trigger reporting. People already harass journalists by
sending them CSAM and then reporting them to the authorities. Automated
reporting might provide a means to scale up such attacks.</p>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">Dear politician, you will be the victim too</p>
<p><strong>Keep in mind that politicians may be among the first victims</strong> of such an
attack.</p>
<p>How would you feel if, before an important vote or election, it was
relatively easy for your opponents to trigger an investigation accusing you
of distributing Child Sexual Abuse Material?</p>
</div>
<p>Now, add to this that flagged users are not notified, cannot contest the
classification, and may enter police databases as CSAM suspects on automated
evidence — with downstream effects on employment vetting, custody, border
crossings.<sup class="footnote-ref"><a href="#fn-9" id="fnref-9-2" data-footnote-ref>9</a></sup></p>
<hr />
<h2>The remedy for false positives is even worse</h2>
<p>OK, you may say, we will put guardrails, to protect innocent citizens from
being wrongfully accused. We can't entirely depend on automated systems, we
will also have humans review these flags.</p>
<p>CSAR has this covered: flagged content may be reviewed by provider staff, by
the EU Centre, and by law enforcement.<sup class="footnote-ref"><a href="#fn-9" id="fnref-9-3" data-footnote-ref>9</a></sup></p>
<div class="markdown-alert markdown-alert-warning">
<p class="markdown-alert-title">Not really a remedy</p>
<p>So, the remedy for machine error is industrial-scale human reading of the
most private content, and <strong>the EU Centre becomes a standing archive of
Europe's most sensitive flagged material: a breach target without
precedent.</strong></p>
</div>
<hr />
<h2>Purpose creep: once the infra is in place, it can be used for any type of content</h2>
<p>Once the technical ability, the legal framework and the legal procedures for
client-side scanning against a centrally distributed database exist, extending
it to other content categories is a political decision, not a technical
project.<sup class="footnote-ref"><a href="#fn-15" id="fnref-15" data-footnote-ref>15</a></sup></p>
<p>Child sexual abuse is something everyone agrees on, so it's always the first
step of such frameworks. But it is important to understand CSAR is not just a
law. It is also a technical framework and infrastructure that makes flagging
additional types of content just a database update.</p>
<p>The next target could be something more controversial, like immigration. Then
maybe something even more controversial like copyrighted material. Then a vague
definition of misinformation. Then some type of dangerous political speech.</p>
<p>You may say that most or all of these things are already covered by existing
laws. But these laws have processes and visibility, cases have to be brought to
justice, and they can be challenged, and the prosecuting authorities have to
pick where they spend their time and resources; which is a welcome limitation
that makes the system work better.</p>
<p>CSAR is automated enforcement: no friction, zero cost to scale. You can't sue
1,000,000 EU citizens in one afternoon, but you can easily flag them under the
suspicion that they sent illegal material.</p>
<hr />
<h2>The blocklist is secret and unauditable</h2>
<p>And, what <strong>is</strong> in the blocklist, anyway?</p>
<p>Ordinary users cannot inspect the hash lists the EU Centre would distribute,
and independent public auditability is inherently limited — reviewing the
source material (CSAM) used to build it is itself illegal.</p>
<p>So, we have a scanning system that we can't review what it is scanning for, and
even curation errors (not to mention intentional abuse) propagate silently to
every device in Europe.</p>
<p>If you think shadow banning in social media is bad, think about a centralized
blocklist, blessed by EU Regulation, that you can't review, and which — for
some weird reason — is blocking your content.</p>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">No need for bad actors</p>
<p>It requires no future bad actor, only ordinary bureaucratic error, for a
photo or an image to be classified as suspicious.<sup class="footnote-ref"><a href="#fn-9" id="fnref-9-4" data-footnote-ref>9</a></sup> It could be the
embarrassing photo of a politician, or a famous work of art, or just a meme.</p>
</div>
<hr />
<h2>Exporting the risk outside the EU</h2>
<p>While EU citizens may accept that they can trust their local legal framework,
messaging is global. Protocols and clients are shared.</p>
<p>If EU compliance weakens clients or protocols, it degrades the security of at
least everyone who communicates <em>with</em> EU users, including journalists,
dissidents, and abuse victims outside the EU whose threat models are far worse.</p>
<p>It also exposes EU users when they communicate with users outside the EU to
these threat models too, since other jurisdictions may flag other types of
content.</p>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">Worth mentioning</p>
<p><em>Podchasov v. Russia</em> (2024)<sup class="footnote-ref"><a href="#fn-16" id="fnref-16" data-footnote-ref>16</a></sup> is the mirror image: the European Court of
Human Rights found that forcing a provider to weaken encryption for all its
users violates the right to respect for private life — in a case where Russia's
FSB required Telegram to hand over information that would allow decrypting
users' messages.</p>
</div>
<hr />
<h2>The small-provider / fediverse problem</h2>
<p>CSAR's core obligations — risk assessment, mitigation, reporting, exposure to
detection orders — apply to hosting and communication services of any size.
There is no exemption for small providers, unlike other EU digital laws.<sup class="footnote-ref"><a href="#fn-17" id="fnref-17" data-footnote-ref>17</a></sup></p>
<p>What does a risk assessment mean for a single-admin Mastodon or Matrix
homeserver? What about a decentralized, open source project? Who serves a
detection order on software with no company behind it?</p>
<p>This leads to compliance costs only Big Tech can absorb and further
centralization of communications onto the platforms whose scanning produced the
justifying dataset.</p>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">I thought we wanted to disentangle from US-based Big Tech</p>
<p>This may be the best way to kill any EU-based startup, or open source
community, building a messaging alternative and proliferate US-based
services dominance.</p>
</div>
<hr />
<h2>Is it even worth it?</h2>
<p>Client-side scanning in mainstream apps is trivially defeated by motivated
users: pre-encryption, custom clients, self-hosted servers, non-compliant
platforms.</p>
<p>Even assuming a <em>perfect</em> classifier, the users reliably covered are
practically the ones who aren't hiding anything.</p>
<div class="markdown-alert markdown-alert-caution">
<p class="markdown-alert-title">This is the big question</p>
<p><strong>So, given all the risks to privacy, freedom of speech, the introduction of
new attack vectors, the collateral damage caused by false positives and their
mitigation mechanisms, and a system that EU citizens will rightfully
distrust, is it worth it?</strong></p>
<p><strong>Are there concrete data proving that CSAR will actually have any effect in</strong>
<strong>combating the sexual abuse and sexual exploitation of children?</strong><sup class="footnote-ref"><a href="#fn-12" id="fnref-12-2" data-footnote-ref>12</a></sup></p>
</div>
<hr />
<h2>Further reading (secondary)</h2>
<ul>
<li>EDRi, &quot;Is this the most criticised draft EU law of all time?&quot;:
<a href="https://edri.org/our-work/most-criticised-eu-law-of-all-time/">https://edri.org/our-work/most-criticised-eu-law-of-all-time/</a></li>
<li>EDRi CSA Regulation document pool (the best running index of primary
documents, incl. the Ombudsman maladministration findings and the
LIBE–Johansson–Thorn correspondence):
<a href="https://edri.org/our-work/csa-regulation-document-pool/">https://edri.org/our-work/csa-regulation-document-pool/</a></li>
<li>Balkan Insight, &quot;'Who Benefits?' Inside the EU's Fight over Scanning
for Child Sex Content&quot; (25 Sept 2023):
<a href="https://balkaninsight.com/2023/09/25/who-benefits-inside-the-eus-fight-over-scanning-for-child-sex-content/">https://balkaninsight.com/2023/09/25/who-benefits-inside-the-eus-fight-over-scanning-for-child-sex-content/</a></li>
<li>Balkan Insight, &quot;Europol Sought Unlimited Data Access in Online
Child Sexual Abuse Regulation&quot; (29 Sept 2023):
<a href="https://balkaninsight.com/2023/09/29/europol-sought-unlimited-data-access-in-online-child-sexual-abuse-regulation/">https://balkaninsight.com/2023/09/29/europol-sought-unlimited-data-access-in-online-child-sexual-abuse-regulation/</a></li>
<li>Balkan Insight, &quot;Europol 'Revising' Conflict of Interest Screening
After BIRN Investigation&quot; (11 Oct 2024):
<a href="https://balkaninsight.com/2024/10/11/europol-revising-conflict-of-interest-screening-after-birn-investigation/bi/">https://balkaninsight.com/2024/10/11/europol-revising-conflict-of-interest-screening-after-birn-investigation/bi/</a>
The related European Ombudsman decision (case 2091/2023/AML, 21 Feb 2025)
found maladministration in how Europol handled the moves of two former
staff members to Thorn:
<a href="https://www.ombudsman.europa.eu/en/decision/en/200017">https://www.ombudsman.europa.eu/en/decision/en/200017</a>
Two further maladministration findings concern the Commission (DG HOME):
the refusal to disclose its expert-group composition
(<a href="https://www.ombudsman.europa.eu/en/decision/en/176658">https://www.ombudsman.europa.eu/en/decision/en/176658</a>) and the refusal
to disclose documents about Thorn's lobbying
(<a href="https://www.ombudsman.europa.eu/en/news-document/en/189565">https://www.ombudsman.europa.eu/en/news-document/en/189565</a>).</li>
<li>NCMEC CyberTipline data:
<a href="https://www.missingkids.org/gethelpnow/cybertipline/cybertiplinedata">https://www.missingkids.org/gethelpnow/cybertipline/cybertiplinedata</a>
and 2025 first look:
<a href="https://www.missingkids.org/blog/2026/the-work-never-stops-first-look-at-ncmecs-2025-data">https://www.missingkids.org/blog/2026/the-work-never-stops-first-look-at-ncmecs-2025-data</a></li>
<li>IWF annual reports: <a href="https://www.iwf.org.uk">https://www.iwf.org.uk</a></li>
</ul>
<section class="footnotes" data-footnotes>
<ol>
<li id="fn-1">
<p>European Parliament Legislative Observatory, procedure file
2022/0155(COD), &quot;Regulation laying down rules to prevent and combat
child sexual abuse&quot;:
<a href="https://oeil.europarl.europa.eu/oeil/en/procedure-file?reference=2022/0155(COD)">https://oeil.europarl.europa.eu/oeil/en/procedure-file?reference=2022/0155(COD)</a> <a href="#fnref-1" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="1" aria-label="Back to reference 1">↩</a></p>
</li>
<li id="fn-2">
<p>Directive 2011/93/EU on combating the sexual abuse and sexual
exploitation of children — the EU's criminal-law instrument on the same
subject, currently being revised (&quot;recast&quot;) under procedure 2024/0035(COD):
<a href="https://oeil.europarl.europa.eu/oeil/en/procedure-file?reference=2024/0035(COD)">https://oeil.europarl.europa.eu/oeil/en/procedure-file?reference=2024/0035(COD)</a>
The recast proposal is COM(2024) 60:
<a href="https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=COM:2024:60:FIN">https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=COM:2024:60:FIN</a> <a href="#fnref-2" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="2" aria-label="Back to reference 2">↩</a></p>
</li>
<li id="fn-3">
<p>Regulation (EU) 2021/1232, the interim derogation from the ePrivacy
Directive (&quot;Chat Control 1.0&quot;):
<a href="https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=celex:32021R1232">https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=celex:32021R1232</a>
Parliament voted against extending it in March 2026 and it lapsed on
3 April 2026:
<a href="https://www.techradar.com/vpn/vpn-privacy-security/chat-control-eu-parliament-said-no-to-big-tech-mass-surveillance-of-your-chats-but-the-battle-for-privacy-isnt-done">https://www.techradar.com/vpn/vpn-privacy-security/chat-control-eu-parliament-said-no-to-big-tech-mass-surveillance-of-your-chats-but-the-battle-for-privacy-isnt-done</a> <a href="#fnref-3" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="3" aria-label="Back to reference 3">↩</a> <a href="#fnref-3-2" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="3-2" aria-label="Back to reference 3-2">↩<sup class="footnote-ref">2</sup></a></p>
</li>
<li id="fn-4">
<p>On the 9 July 2026 second-reading vote (314–276, 17 abstentions,
against a 361-vote absolute-majority threshold):
<a href="https://www.theregister.com/security/2026/07/09/meps-fail-to-prevent-chat-control-snoopfest-revival/5269379">https://www.theregister.com/security/2026/07/09/meps-fail-to-prevent-chat-control-snoopfest-revival/5269379</a>
Timeline of both Chat Control tracks:
<a href="https://fightchatcontrol.eu/chat-control-overview">https://fightchatcontrol.eu/chat-control-overview</a> <a href="#fnref-4" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="4" aria-label="Back to reference 4">↩</a></p>
</li>
<li id="fn-5">
<p>Latest Council Presidency state-of-play on the CSAR negotiations
(June 2026), mirrored by netzpolitik:
<a href="https://cdn.netzpolitik.org/wp-upload/2026/07/2026-06-22_Council_Presidency_CSAR_State-of-play_10906.pdf">https://cdn.netzpolitik.org/wp-upload/2026/07/2026-06-22_Council_Presidency_CSAR_State-of-play_10906.pdf</a>
An earlier June 2026 state-of-play (Council doc. 10499/26) is also
mirrored there; it confirms trilogues began 9 December 2025, records
provisional agreement on parts of the text, and notes the Parliament/
Council split over making the interim regime permanent:
<a href="https://cdn.netzpolitik.org/wp-upload/2026/06/2026-06-22_Council_Presidency_CSAR_State-of-play_10499.pdf">https://cdn.netzpolitik.org/wp-upload/2026/06/2026-06-22_Council_Presidency_CSAR_State-of-play_10499.pdf</a> <a href="#fnref-5" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="5" aria-label="Back to reference 5">↩</a></p>
</li>
<li id="fn-6">
<p>Directive 2002/58/EC (ePrivacy Directive), Art. 5 (confidentiality of
communications):
<a href="https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=celex:32002L0058">https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=celex:32002L0058</a> <a href="#fnref-6" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="6" aria-label="Back to reference 6">↩</a></p>
</li>
<li id="fn-7">
<p>Council of the EU, negotiating mandate on the CSA Regulation, doc.
15318/25 (13 Nov 2025, &quot;Danish compromise&quot;): permanent voluntary-scanning
framework, risk categories, age-verification obligations, 3-year review
clause:
<a href="https://data.consilium.europa.eu/doc/document/ST-15318-2025-INIT/en/pdf">https://data.consilium.europa.eu/doc/document/ST-15318-2025-INIT/en/pdf</a>
It was adopted on 26 November 2025 as a &quot;Partial&quot; General Approach
(partial because the seat of the EU Centre is left open), at ambassador
level without a formal public vote; Czechia, Poland, Slovakia and the
Netherlands voted against, Italy abstained. See the EDRi document pool
for the voting record:
<a href="https://edri.org/our-work/csa-regulation-document-pool/">https://edri.org/our-work/csa-regulation-document-pool/</a> <a href="#fnref-7" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="7" aria-label="Back to reference 7">↩</a></p>
</li>
<li id="fn-8">
<p>Council Legal Service opinion, doc. 8787/23, 26 April 2023 (marked
LIMITE, leaked May 2023) — generalised scanning of private
communications is incompatible with Arts. 7 and 8 of the EU Charter of
Fundamental Rights absent reasonable suspicion and prior judicial
authorisation. The opinion also states that screening encrypted
communications would leave providers three options: abandoning
end-to-end encryption, introducing a back-door, or client-side scanning
— i.e. the Council's own lawyers reached the same conclusion as this
post's &quot;on-device scanning is the only technically viable option&quot;.
Full PDF (mirrored by Statewatch):
<a href="https://www.statewatch.org/media/3901/eu-council-cls-opinion-csam-proposal-8787-23.pdf">https://www.statewatch.org/media/3901/eu-council-cls-opinion-csam-proposal-8787-23.pdf</a>
Coverage: <a href="https://techcrunch.com/2023/05/09/eu-scam-scanning-unlawful-advice/">https://techcrunch.com/2023/05/09/eu-scam-scanning-unlawful-advice/</a>
and
<a href="https://european-pirateparty.eu/leaked-eu-council-legal-analysis-chatcontrol-plans-doomed-to-fail/">https://european-pirateparty.eu/leaked-eu-council-legal-analysis-chatcontrol-plans-doomed-to-fail/</a>
The doc number is confirmed by the Council's own June 2026 state-of-play
(doc. 10499/26), which cites 8787/23 directly. A subsequent Legal
Service assessment found the Danish &quot;voluntary&quot; framework still violates
fundamental rights:
<a href="https://www.techradar.com/computing/cyber-security/a-political-blackmail-the-eu-parliament-is-pressing-for-new-mandatory-scanning-of-your-private-chats">https://www.techradar.com/computing/cyber-security/a-political-blackmail-the-eu-parliament-is-pressing-for-new-mandatory-scanning-of-your-private-chats</a> <a href="#fnref-8" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="8" aria-label="Back to reference 8">↩</a></p>
</li>
<li id="fn-9">
<p>European Commission, Proposal COM(2022) 209 final, 11 May 2022. Risk
assessment/mitigation: Arts. 3–6; detection orders: Arts. 7–11;
reporting/removal: Arts. 12–15; blocking: Arts. 16–18; EU Centre and
indicator databases: Arts. 40 ff.
<a href="https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=celex:52022PC0209">https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=celex:52022PC0209</a>
Note: these article numbers refer to the Commission's 2022 proposal. The
final text is still being negotiated in trilogues (resuming September
2026), so the numbering and content of individual articles may change
in the adopted regulation. <a href="#fnref-9" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="9" aria-label="Back to reference 9">↩</a> <a href="#fnref-9-2" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="9-2" aria-label="Back to reference 9-2">↩<sup class="footnote-ref">2</sup></a> <a href="#fnref-9-3" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="9-3" aria-label="Back to reference 9-3">↩<sup class="footnote-ref">3</sup></a> <a href="#fnref-9-4" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="9-4" aria-label="Back to reference 9-4">↩<sup class="footnote-ref">4</sup></a></p>
</li>
<li id="fn-10">
<p>National police statistics on the quality of automated referrals —
with the honest caveat that &quot;not actionable&quot; is not the same as
&quot;innocent&quot;:
<strong>Switzerland</strong>: a fedpol spokesperson told Tages-Anzeiger (regarding
2020) that of ~9,000 NCMEC reports received, &quot;just under ten percent
were criminally relevant&quot;. NCMEC publicly disputed this: its own visual
review of every 2020 report sent to Switzerland concluded at least 63%
contained CSAM under a conservative definition
(<a href="https://www.missingkids.org/blog/2020/we-are-in-danger-of-losing-the-global-battle-for-child-safety">https://www.missingkids.org/blog/2020/we-are-in-danger-of-losing-the-global-battle-for-child-safety</a>).
fedpol's Annual Report 2023 states it received 14,420 NCMEC reports and
forwarded 1,895 to cantonal authorities for prosecution (~13%):
<a href="https://fedpol.report/en/report-2023/fedpol-in-figures/paedophile-criminals-on-the-lookout/">https://fedpol.report/en/report-2023/fedpol-in-figures/paedophile-criminals-on-the-lookout/</a>
<strong>Ireland</strong>: data obtained by ICCL and Digital Rights Ireland from An
Garda Síochána (Oct 2022): of 4,192 NCMEC referrals in 2020, 409 (~10%)
were actionable and 471 (11%) were verified as <em>not</em> CSAM (e.g.
&quot;children playing on a beach&quot;); the rest were age-undetermined (506),
IP-not-progressable (940), below threshold (606), viral (333),
self-generated (75), adult (51), with 852 marked as CSAM:
<a href="https://www.iccl.ie/news/an-garda-siochana-unlawfully-retains-files-on-innocent-people-who-it-has-already-cleared-of-producing-or-sharing-of-child-sex-abuse-material/">https://www.iccl.ie/news/an-garda-siochana-unlawfully-retains-files-on-innocent-people-who-it-has-already-cleared-of-producing-or-sharing-of-child-sex-abuse-material/</a>
(The same release documents that the Gardaí retained the data of the
wrongly flagged.)
<strong>Germany</strong>: in 2024 the BKA received over 205,000 NCMEC reports;
criminal relevance was established in 106,353 cases — roughly half were
not criminal (netzpolitik, June 2026):
<a href="https://netzpolitik.org/2026/automatisierte-falschmeldungen-bka-meldet-kinderpornografie-die-keine-ist/">https://netzpolitik.org/2026/automatisierte-falschmeldungen-bka-meldet-kinderpornografie-die-keine-ist/</a> <a href="#fnref-10" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="10" aria-label="Back to reference 10">↩</a></p>
</li>
<li id="fn-11">
<p>In Germany, an NCMEC report alone routinely serves as the basis for
a house-search warrant, and the courts are split on whether that is
lawful: the Regional Court of Detmold ruled that a report without
further evidence does not establish sufficient initial suspicion (LG
Detmold, 11.4.2022 – 23 Qs 27/22, a search of a household with seven
registered residents), while the Regional Court of Bamberg considers a
report matched to an IP address sufficient (LG Bamberg, 18.12.2023 –
15 Qs 86/23), and the Federal Constitutional Court has declined to
object (BVerfG, 21.10.2024 – 1 BvR 2215/24). Overview with case
citations:
<a href="https://anwaltspraxis-magazin.de/fachbeitraege/strafrecht/2025/02/13/meldungen-des-ncmec-grundlagen-und-ideen-fuer-die-verteidigung-im-bereich-kinder-und-jugendpornografie/">https://anwaltspraxis-magazin.de/fachbeitraege/strafrecht/2025/02/13/meldungen-des-ncmec-grundlagen-und-ideen-fuer-die-verteidigung-im-bereich-kinder-und-jugendpornografie/</a>
For a documented false accusation by automated flagging, see the
Flokinet case (June 2026): the BKA formally accused a hosting provider
of distributing CSAM based on two entirely legal YouTube videos — one a
documentary about composer Hans Zimmer — without having checked the
content:
<a href="https://netzpolitik.org/2026/automatisierte-falschmeldungen-bka-meldet-kinderpornografie-die-keine-ist/">https://netzpolitik.org/2026/automatisierte-falschmeldungen-bka-meldet-kinderpornografie-die-keine-ist/</a> <a href="#fnref-11" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="11" aria-label="Back to reference 11">↩</a></p>
</li>
<li id="fn-12">
<p>European Parliamentary Research Service, &quot;Proposal for a regulation
laying down rules to prevent and combat child sexual abuse —
Complementary Impact Assessment&quot; (April 2023), which also concludes the
proposal's overall effectiveness is expected to be limited:
<a href="https://www.europarl.europa.eu/RegData/etudes/STUD/2023/740248/EPRS_STU(2023)740248_EN.pdf">https://www.europarl.europa.eu/RegData/etudes/STUD/2023/740248/EPRS_STU(2023)740248_EN.pdf</a>
See also EPRS briefing PE 753179:
<a href="https://www.europarl.europa.eu/RegData/etudes/BRIE/2023/753179/EPRS_BRI(2023)753179_EN.pdf">https://www.europarl.europa.eu/RegData/etudes/BRIE/2023/753179/EPRS_BRI(2023)753179_EN.pdf</a> <a href="#fnref-12" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="12" aria-label="Back to reference 12">↩</a> <a href="#fnref-12-2" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="12-2" aria-label="Back to reference 12-2">↩<sup class="footnote-ref">2</sup></a></p>
</li>
<li id="fn-13">
<p>H. Abelson, R. Anderson, S. Bellovin, J. Benaloh, M. Blaze, J. Callas,
W. Diffie, S. Landau, P. Neumann, R. Rivest, J. Schiller, B. Schneier,
V. Teague, C. Troncoso, &quot;Bugs in Our Pockets: The Risks of Client-Side
Scanning&quot; (2021). arXiv:2110.07450: <a href="https://arxiv.org/abs/2110.07450">https://arxiv.org/abs/2110.07450</a> <a href="#fnref-13" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="13" aria-label="Back to reference 13">↩</a></p>
</li>
<li id="fn-14">
<p>NeuralHash reverse-engineering and collision PoC (Aug 2021):
<a href="https://github.com/AsuharietYgvar/AppleNeuralHash2ONNX">https://github.com/AsuharietYgvar/AppleNeuralHash2ONNX</a> (the first
collision was reported in that repository's issues). <a href="#fnref-14" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="14" aria-label="Back to reference 14">↩</a></p>
</li>
<li id="fn-15">
<p>Joint statements of scientists and researchers on the proposed CSA
Regulation (rounds: July 2023, May 2024, Sept 2024, Sept/Oct 2025 with
807 signatories from 37 countries, plus a 2026 round), naming function
creep and abuse risk explicitly. Canonical archive:
<a href="https://csa-scientist-open-letter.org">https://csa-scientist-open-letter.org</a>
(e.g. <a href="https://csa-scientist-open-letter.org/Sep2025">https://csa-scientist-open-letter.org/Sep2025</a>). <a href="#fnref-15" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="15" aria-label="Back to reference 15">↩</a></p>
</li>
<li id="fn-16">
<p>ECtHR, <em>Podchasov v. Russia</em>, app. no. 33696/19, judgment of
13 Feb 2024, on Art. 8 ECHR (right to respect for private life and
correspondence). HUDOC: <a href="https://hudoc.echr.coe.int/fre?i=001-230854">https://hudoc.echr.coe.int/fre?i=001-230854</a>
Case background and third-party intervention:
<a href="https://privacyinternational.org/legal-action/podchasov-v-russia">https://privacyinternational.org/legal-action/podchasov-v-russia</a> <a href="#fnref-16" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="16" aria-label="Back to reference 16">↩</a></p>
</li>
<li id="fn-17">
<p>Compare Regulation (EU) 2022/2065 (Digital Services Act), Art. 19,
which excludes micro and small enterprises from certain obligations:
<a href="https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=celex:32022R2065">https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=celex:32022R2065</a> <a href="#fnref-17" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="17" aria-label="Back to reference 17">↩</a></p>
</li>
</ol>
</section>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/07/11/260711-chat-control-2-csar-a-risk-list/og.png"
   type="image/png"
   length="234962"/>

    </item>
    
    <item>
      <title>AI-generated blog post images are not cool any more</title>
      <link>https://blog.vrypan.net/2026/07/10/260710-ai-blog-post-images-are-not-cool/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/10/260710-ai-blog-post-images-are-not-cool/</guid>
      <pubDate>Fri, 10 Jul 2026 15:04:35 +0000</pubDate>
      <description>You share your blog post on X, Facebook, Bluesky, etc and you know it will be presented so much better if it has an image. Sometimes it's easy. You blog about something that has a visual element and it's easy to use a diagram, a product photo, a landscape —you would have done it ...</description>
      <content:encoded><![CDATA[
	
	<p>You share your blog post on X, Facebook, Bluesky, etc and you know it will be presented
so much better if it has an image. Sometimes it's easy. You blog about something that has
a visual element and it's easy to use a diagram, a product photo, a landscape —you
would have done it anyway.</p>
<p>But many times, you just want to share a thought or an idea, or some code.</p>
<p>In the past, the only way for most of us was to hunt for images under Creative Commons
licenses. Writting a post about <a href="/2013/07/03/the-carriage-return/">the history of <code>CR</code></a>?
Try to find an old typewritter photo that you like, and is compatible with your blog,
and blends nicely with your content.</p>
<p>A couple of years ago, we got a new tool: AI-generated images. Just tell an AI what you
want, itterate a couple of times, and done.</p>
<p>But AI-generated images in blog posts are no longer cool. Everyone does it. And with the
exception when an image is a really good fit, it looks like a 2000 PowerPoint using
animated transitions and WordArt.</p>
<p>I think the ideal solution is a card generated by your post title and abstract, the
date, the tags, your blog name, etc. In other words, present your blog post the
way you would like it to be presented, not how the third party service decides to
format your title or trim your text.</p>
<p>There are many tools that generate images like this, but most of them use node.js
and I dread the moment I run <code>npm install</code> which pulls 1,000 dependencies.</p>
<p>So I wrote my own tool, <a href="https://github.com/vrypan/og-img">og-img</a> in Rust (thank you,
Claude). A single binary, that does exactly waht I want, and it's quite fast (though
I have not done any serious optimizations yet).</p>
<p>Just install</p>
<pre><code>brew tap vrypan/tap
brew install og-img
</code></pre>
<p>and run</p>
<pre><code>og render \
  --icon vrypan.png \
  --site blog.vrypan.net \
  --title &quot;AI-generated blog post images are not cool any more&quot; \
  --text &quot;but OpenGraph empeds still look much better when there is one.&quot; \
  --tags en,blogging,howto \
  --date &quot;July 10, 2026&quot; \
  --theme github-dark \
  -o og.png
</code></pre>
<p><figure><img src="https://blog.vrypan.net/2026/07/10/260710-ai-blog-post-images-are-not-cool/og.png" alt="" title="The result of the above command" /><figcaption>The result of the above command</figcaption></figure></p>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/07/10/260710-ai-blog-post-images-are-not-cool/og.png"
   type="image/png"
   length="268371"/>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/07/08/note-58201/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/08/note-58201/</guid>
      <pubDate>Wed, 08 Jul 2026 00:03:22 +0300</pubDate>
      <description>Bloatware is not a recent invention but recently, I see so many products "just like X, but with 100 more features". Now that AI agents make it cheap to add features, removing them is the real value.</description>
      <content:encoded><![CDATA[
	
	<p>Bloatware is not a recent invention but recently, I see so many products &quot;just like
X, but with 100 more features&quot;.
Now that AI agents make it cheap to add features, removing them is the real value.</p>

]]></content:encoded>

    </item>
    
    <item>
      <title>Where Is QRdo?</title>
      <link>https://blog.vrypan.net/2026/07/07/where-is-qrdo/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/07/where-is-qrdo/</guid>
      <pubDate>Tue, 07 Jul 2026 18:31:14 +0000</pubDate>
      <description>This was fun: A "Where Is Waldo" game for QR codes.</description>
      <content:encoded><![CDATA[
	
        <img  src="https://blog.vrypan.net/2026/07/07/where-is-qrdo/qr.png">
        
	<p>This was fun: A &quot;Where Is Waldo&quot; game for QR codes.</p>
<p>Can you find it? Your phone can!</p>
<hr />
<h2>The basic idea</h2>
<p>QR scanners don't see color. The first thing a decoder does is convert the image to grayscale — collapsing each pixel to a single brightness value via roughly 0.299·R + 0.587·G + 0.114·B — then binarize and hunt for the finder patterns (the three corner squares) and module grid. Everything downstream operates on luminance alone.</p>
<p>That gap is the whole trick: luminance carries the code; hue and saturation are free to do whatever you want. You have two perceptual channels the scanner is blind to, and humans hunting for a pattern rely heavily on exactly those channels.</p>
<p>So the code is rendered as a luminance difference — &quot;dark&quot; modules low-luma, &quot;light&quot; modules high-luma — but each module is painted a vivid random color at its assigned brightness. The key subtlety is that a fully saturated color's true luminance depends on its hue: pure blue sits around 0.11, pure red around 0.30, pure yellow near 0.89. So &quot;dark&quot; modules don't have to be dark and muddy — you can use electric blues, reds, and violets that are naturally low-luminance at full saturation, and reserve the bright hues (yellow, cyan, green) for &quot;light&quot; modules.</p>
<p>To the hyman eye it's a saturated confetti, but converted to grayscale it's a scannable QR.</p>
<hr />
<h2>The code</h2>
<pre><code>#! /usr/bin/env python

import cv2
import numpy as np
PAYLOAD = &quot;https://blog.vrypan.net/2026/07/07/where-is-qrdo/&quot;
MOD = 8
GRID_W, GRID_H = 192, 96
N_DECOYS = 180
enc = cv2.QRCodeEncoder.create()
qr = enc.encode(PAYLOAD)[2:-2, 2:-2]
n = qr.shape[0]
det = cv2.QRCodeDetector()
LUMA = np.array([0.114, 0.587, 0.299])   # BGR
# --- curated DARK palette (BGR), all low-luma, balanced across families ----
# reds need V&lt;255 to stay low-luma; blues/violets are low-luma at full V.
DARK = np.array([
    [0,   0,   220],   # strong red
    [0,   0,   180],   # deep red
    [30,  0,   200],   # red-magenta
    [255, 0,   0],     # pure blue
    [200, 0,   0],     # deep blue
    [255, 40,  0],     # blue-cyan lean
    [200, 0,   120],   # indigo
    [180, 0,   180],   # violet
    [140, 0,   200],   # purple-red
], np.float32)
# --- LIGHT palette (BGR), all high-luma, vivid + bright ---
LIGHT = np.array([
    [0,   255, 255],   # yellow
    [0,   255, 150],   # yellow-green
    [0,   255, 0],     # green
    [255, 255, 0],     # cyan
    [255, 255, 150],   # pale cyan
    [150, 255, 255],   # pale yellow
    [200, 255, 200],   # mint
], np.float32)
print(&quot;dark palette lumas:&quot;, np.round(DARK @ LUMA / 255, 2))
print(&quot;light palette lumas:&quot;, np.round(LIGHT @ LUMA / 255, 2))
def build(seed):
    r = np.random.default_rng(seed)
    # --- random QR placement (keep a 1-module margin for the finder halos) ---
    qx = int(r.integers(1, GRID_W - n))
    qy = int(r.integers(1, GRID_H - n))
    is_dark = r.random((GRID_H, GRID_W)) &lt; 0.40
    for cx, cy in [(qx, qy), (qx+n-7, qy), (qx, qy+n-7)]:
        is_dark[cy-1:cy+8, cx-1:cx+8] = False
    for y in range(n):
        for x in range(n):
            is_dark[qy+y, qx+x] = qr[y, x] &lt; 128
    for _ in range(N_DECOYS):
        dx = int(r.integers(1, GRID_W-9)); dy = int(r.integers(1, GRID_H-9))
        if (qx-10 &lt; dx &lt; qx+n+1) and (qy-10 &lt; dy &lt; qy+n+1):
            continue
        s = int(r.integers(6, 9))
        is_dark[dy:dy+s, dx:dx+s] = True
        is_dark[dy+1:dy+s-1, dx+1:dx+s-1] = False
        inner = int(r.integers(1, 3))
        is_dark[dy+2:dy+s-2-(inner-1), dx+2:dx+s-2-(inner-1)] = True
    canvas = np.zeros((GRID_H, GRID_W, 3), np.float32)
    di = r.integers(0, len(DARK), (GRID_H, GRID_W))
    li = r.integers(0, len(LIGHT), (GRID_H, GRID_W))
    for y in range(GRID_H):
        for x in range(GRID_W):
            canvas[y, x] = DARK[di[y, x]] if is_dark[y, x] else LIGHT[li[y, x]]
    img = cv2.resize(canvas.astype(np.uint8), (GRID_W*MOD, GRID_H*MOD),
                     interpolation=cv2.INTER_NEAREST)
    return img, (qx, qy)
import secrets
# random search order (fresh OS entropy each run) so the QR lands in a
# different spot every time, rather than always picking the first seed that works
seeds = list(range(1, 4000))
np.random.default_rng(secrets.randbits(64)).shuffle(seeds)
for seed in seeds:
    img, (qx, qy) = build(seed)
    if det.detectAndDecode(img)[0] == PAYLOAD:
        cv2.imwrite(&quot;qr_redblue.png&quot;, img)
        print(f&quot;seed {seed}: decodes OK -&gt; qr_redblue.png  (QR at module {qx},{qy})&quot;)
        break
else:
    raise SystemExit(&quot;no working seed found&quot;)
</code></pre>
<h2>Update 08-07-2026</h2>
<p>Variation: Instead of scattering decoy finder-lookalike squares at
random, place them on a regular lattice and align the QR so its three
real finder patterns land on lattice nodes. Now the real corners are
just three nodes in a field of near-identical squares and the
easy to find &quot;three squares in an L&quot; tell disappears.</p>
<p><figure><img src="https://blog.vrypan.net/2026/07/07/where-is-qrdo/qr_lattice.png" alt="" /></figure></p>
<pre><code>#! /usr/bin/env python

import cv2
import numpy as np
import secrets

PAYLOAD = &quot;https://blog.vrypan.net&quot;
MOD = 8
GRID_W, GRID_H = 288, 144
PITCH = None          # set to n-7 after QR is encoded; = finder spacing
JITTER = 2            # decoys wander +/- this many modules off their node
CLEARANCE = 6         # lattice leaves this margin around the QR body

enc = cv2.QRCodeEncoder.create()
qr = enc.encode(PAYLOAD)[2:-2, 2:-2]
n = qr.shape[0]
PITCH = n - 7         # so QR finders at (0,0),(n-7,0),(0,n-7) fall on nodes
det = cv2.QRCodeDetector()
LUMA = np.array([0.114, 0.587, 0.299])   # BGR

DARK = np.array([
    [0,   0,   220],   # strong red
    [0,   0,   180],   # deep red
    [30,  0,   200],   # red-magenta
    [255, 0,   0],     # pure blue
    [200, 0,   0],     # deep blue
    [255, 40,  0],     # blue-cyan lean
    [200, 0,   120],   # indigo
    [180, 0,   180],   # violet
    [140, 0,   200],   # purple-red
], np.float32)
LIGHT = np.array([
    [0,   255, 255],   # yellow
    [0,   255, 150],   # yellow-green
    [0,   255, 0],     # green
    [255, 255, 0],     # cyan
    [255, 255, 150],   # pale cyan
    [150, 255, 255],   # pale yellow
    [200, 255, 200],   # mint
], np.float32)
print(&quot;dark palette lumas:&quot;, np.round(DARK @ LUMA / 255, 2))
print(&quot;light palette lumas:&quot;, np.round(LIGHT @ LUMA / 255, 2))

def draw_decoy(is_dark, x, y, size, center):
    &quot;&quot;&quot;Concentric square with a deliberately wrong ratio (scanner rejects it).&quot;&quot;&quot;
    if x &lt; 1 or y &lt; 1 or x+size &gt;= GRID_W or y+size &gt;= GRID_H:
        return
    is_dark[y:y+size, x:x+size] = True
    is_dark[y+1:y+size-1, x+1:x+size-1] = False
    c0 = (size - center) // 2
    is_dark[y+c0:y+c0+center, x+c0:x+c0+center] = True

def build(seed):
    r = np.random.default_rng(seed)
    is_dark = r.random((GRID_H, GRID_W)) &lt; 0.40
    # place QR origin on a lattice node (random node -&gt; random placement)
    nx = (GRID_W - n) // PITCH
    ny = (GRID_H - n) // PITCH
    qx = int(r.integers(0, nx)) * PITCH + 2
    qy = int(r.integers(0, ny)) * PITCH + 2
    # jittered-lattice decoys, skipping the QR's own footprint
    x0, y0, x1, y1 = qx-CLEARANCE, qy-CLEARANCE, qx+n+CLEARANCE, qy+n+CLEARANCE
    for j in range(0, GRID_H, PITCH):
        for i in range(0, GRID_W, PITCH):
            if x0 &lt;= i &lt;= x1 and y0 &lt;= j &lt;= y1:
                continue                      # inside QR area: leave for real finders
            jx = i + int(r.integers(-JITTER, JITTER+1))
            jy = j + int(r.integers(-JITTER, JITTER+1))
            size = int(r.choice([6, 8]))      # wrong sizes -&gt; wrong ratio
            center = int(r.choice([2, 4]))
            draw_decoy(is_dark, jx, jy, size, center)
    # real finder halos + stamp the QR (overwrites lattice at its 3 nodes)
    for cx, cy in [(qx, qy), (qx+n-7, qy), (qx, qy+n-7)]:
        is_dark[cy-1:cy+8, cx-1:cx+8] = False
    for y in range(n):
        for x in range(n):
            is_dark[qy+y, qx+x] = qr[y, x] &lt; 128
    # paint colours
    canvas = np.zeros((GRID_H, GRID_W, 3), np.float32)
    di = r.integers(0, len(DARK), (GRID_H, GRID_W))
    li = r.integers(0, len(LIGHT), (GRID_H, GRID_W))
    for y in range(GRID_H):
        for x in range(GRID_W):
            canvas[y, x] = DARK[di[y, x]] if is_dark[y, x] else LIGHT[li[y, x]]
    img = cv2.resize(canvas.astype(np.uint8), (GRID_W*MOD, GRID_H*MOD),
                     interpolation=cv2.INTER_NEAREST)
    return img, (qx, qy)

# randomized search order -&gt; different working seed (and placement) each run
seeds = list(range(1, 8000))
np.random.default_rng(secrets.randbits(64)).shuffle(seeds)
for seed in seeds:
    img, (qx, qy) = build(seed)
    if det.detectAndDecode(img)[0] == PAYLOAD:
        cv2.imwrite(&quot;qr_lattice.png&quot;, img)
        print(f&quot;seed {seed}: decodes OK -&gt; qr_lattice.png  (QR at module {qx},{qy})&quot;)
        break
else:
    raise SystemExit(&quot;no working seed found -- raise the seed ceiling or lower density&quot;)

</code></pre>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/07/07/where-is-qrdo/qr.png"
   type="image/png"
   length="362979"/>

   <enclosure url="https://blog.vrypan.net/2026/07/07/where-is-qrdo/qr_lattice.png"
   type="image/png"
   length="811550"/>

    </item>
    
    <item>
      <title>USA-BEL 1-4</title>
      <link>https://blog.vrypan.net/2026/07/07/260707-usa-bel-1-4/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/07/260707-usa-bel-1-4/</guid>
      <pubDate>Tue, 07 Jul 2026 10:22:36 +0000</pubDate>
      <description>It was never about Balogun, a relatively unimportant player compared to the rest of World Cup teams roasters. It was not about the game between USA and Belgium, that many of us in non-American timezones, whouldn't have sayed awake to watch.</description>
      <content:encoded><![CDATA[
	
	<p>It was never about Balogun, a relatively unimportant player compared to the rest of World Cup teams roasters.
It was not about the game between USA and Belgium, that many of us in non-American timezones, whouldn't
have sayed awake to watch.</p>
<h2>It was about the sport.</h2>
<p>A game that unites people, not in some kumbaya way, but through passion, and tears of joy and disappointment.</p>
<p>Football is life. A great baller can also be a shitty character at the same time. An average player can become a hero.
A careless moment can have irreversible consequences. Talent gets recognized regardless of origin. Human error is
recognized as part of the game.</p>
<p>There's only one thing that all football fans hate: outside interference.</p>
<h2>It was about America in 2026</h2>
<p>For most of the world, America in 2026 is a symbol of corruption, arrogance, and disrespect. It wasn't always
like this. Many of us grew up looking up to America as a symbol of opportunity, and freedom, and openness.</p>
<p>Not any more.</p>
<p>It wouldn't have been the same if a small team from Africa had somehow lobbied to bend the rules. But this was
not a random team, from a small country, with a prime minister the rest of us had never heard of.</p>
<p>It was the USA, probably the most powerful country in the world, and Trump, who has trash-talked every single nation and leader around the world.
And he made a call to the clown who is President of FIFA, asking for the rules to be bent in his favor, and got his way.</p>
<p>It was also the arrogance of Americans who may have had zero affinity with the sport, explaining to us how the
game works, how the rules work, why &quot;everyone would have done the same if they could&quot;.</p>
<h2>Like a Greek tragedy</h2>
<p>And, after all this, after the feeling of injustice, after the frustration of being talked down by arrogant people, eventually,
The Beautiful Game restored world order.</p>
<p><strong>USA-BEL 1-4</strong>.</p>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/07/07/260707-usa-bel-1-4/image1.png"
   type="image/png"
   length="3974496"/>

   <enclosure url="https://blog.vrypan.net/2026/07/07/260707-usa-bel-1-4/image2.jpeg"
   type="image/jpeg"
   length="226536"/>

   <enclosure url="https://blog.vrypan.net/2026/07/07/260707-usa-bel-1-4/image3.jpeg"
   type="image/jpeg"
   length="247334"/>

    </item>
    
    <item>
      <title>listnr is an ActivityPub bridge for static blogs</title>
      <link>https://blog.vrypan.net/2026/07/05/260705-listnr-is-an-activitypub-bridge-for-static-blogs/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/05/260705-listnr-is-an-activitypub-bridge-for-static-blogs/</guid>
      <pubDate>Sun, 05 Jul 2026 16:54:24 +0000</pubDate>
      <description>When I moved to a static blog, I also removed comments. Or maybe I did it while I still used Wordpress, I'm not sure. The point is I didn't remove them for technical reasons, I removed them because even in 2012, hosting a comments section had already started being less rewarding ...</description>
      <content:encoded><![CDATA[
	
	<p>When I moved to a static blog, I also removed comments. Or maybe I did it while I still used Wordpress, I'm not sure. The point is I didn't remove them for technical reasons, I removed them because even in 2012, hosting a comments section had already started being less rewarding than it used to be: the ratio of comments I wanted to read to comments that contributed little — disingenuous, malicious, or just clueless ones — kept getting worse.</p>
<p>But it's nice for a blog to provide feedback mechanisms. I see it when I read something on my RSS reader, and I want to like, or leave a short comment to let the author know I appreciate what they wrote, or give them feedback on the topic they cover.</p>
<hr />
<p>Enter <code>listnr</code>.</p>
<p><a href="https://github.com/vrypan/listnr">listnr</a> is a ActivityPub &quot;bridge&quot;:</p>
<ul>
<li>it maintains a Fediverse identity (an ActivityPub actor that anyone on Mastodon can follow), like <a href="https://mastodon.social/@blog@vrypan.net">@blog@vrypan.net</a></li>
<li>it watches my RSS feed and publishes new posts as they appear</li>
<li>it provides a simple API that a small js widget can use to show likes, boosts and comments under each post.</li>
</ul>
<p>It is built as a single executable (written in Go) and for a blog with moderate traffic, it can be hosted on a very small VPS <sup class="footnote-ref"><a href="#fn-1" id="fnref-1" data-footnote-ref>1</a></sup>.</p>
<hr />
<p>Why ActivityPub and not ATProto, or Farcaster or something else?</p>
<p>I want self-sovereignty. I don't want to depend on any external resource or service<sup class="footnote-ref"><a href="#fn-2" id="fnref-2" data-footnote-ref>2</a></sup>. ATProto requires me to depend on third-party relays. A Farcaster node on the other hand, requires a VPS that will cost much more<sup class="footnote-ref"><a href="#fn-3" id="fnref-3" data-footnote-ref>3</a></sup>, and my <a href="/2025/06/24/fc-2025-06-24-0xd52f185f/">RPi5/16GB at home</a> probably couldn't handle the task.</p>
<p>Using ActivityPub was the only solution I could trivially self-host at home, or on a VPS for a very small amount —something I would be ok paying for even during periods I don't post much.</p>
<p>I’ll keep exploring options, and I may expand <code>listnr</code> to support more protocols if it makes sense, but for now this is the only direction that gives me self-sovereignty I can afford.</p>
<section class="footnotes" data-footnotes>
<ol>
<li id="fn-1">
<p>I'm using a 1CPU/512MB/10GB droplet from <a href="https://m.do.co/c/1e0ea1f4ce50">DigitalOcean</a> for $4/month, but I could have also used a RaspberryPi at home using <a href="https://developers.cloudflare.com/tunnel/">Cloudflare Tunnel</a> <a href="#fnref-1" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="1" aria-label="Back to reference 1">↩</a></p>
</li>
<li id="fn-2">
<p>Yes, I know I still depend on DNS, 🤷‍♂️ <a href="#fnref-2" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="2" aria-label="Back to reference 2">↩</a></p>
</li>
<li id="fn-3">
<p>According to <a href="https://hypersnap.org/run-a-node">Hypersnap: Run part of the network yourself</a>, you will need 4CPU/16GB/1.5TB that will cost $80–$110/month. <a href="#fnref-3" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="3" aria-label="Back to reference 3">↩</a></p>
</li>
</ol>
</section>

]]></content:encoded>

    </item>
    
    <item>
      <title>Testing listnr, a fedivese bridge</title>
      <link>https://blog.vrypan.net/2026/07/04/260704-testing-listnr-a-fedivese-bridge/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/04/260704-testing-listnr-a-fedivese-bridge/</guid>
      <pubDate>Sat, 04 Jul 2026 19:26:09 +0000</pubDate>
      <description>I'll write more about listnr, a fediverse bridge for my statically generated blog.</description>
      <content:encoded><![CDATA[
	
	<p>I'll write more about <a href="https://github.com/vrypan/listnr">listnr</a>, a fediverse
bridge for my statically generated blog.</p>
<p>Yes, it's AI-coded, if you have some deep, ideological objection, don't bother;
I like my AI-coding tools, and I couldn't have built this in two days without them.</p>
<p>Help me test by commenting/liking/sharing on Fediverse. Link bellow.</p>

]]></content:encoded>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/07/04/260704-note-73190/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/04/260704-note-73190/</guid>
      <pubDate>Sat, 04 Jul 2026 16:53:11 +0300</pubDate>
      <description>Testing ActivityPub</description>
      <content:encoded><![CDATA[
	
	<p>Testing ActivityPub</p>

]]></content:encoded>

    </item>
    
    <item>
      <title>What a Forgotten 100-Year-Old Report Says About Who Americans Are</title>
      <link>https://blog.vrypan.net/2026/07/03/260703-what-a-forgotten-100-year-old-report-says-about-who-americans-are/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/03/260703-what-a-forgotten-100-year-old-report-says-about-who-americans-are/</guid>
      <pubDate>Fri, 03 Jul 2026 17:19:19 +0000</pubDate>
      <description>Derek Thompson wrote one of the most interesting things I've read lately.</description>
      <content:encoded><![CDATA[
	
	<p>Derek Thompson wrote one of the most interesting things I've read lately.</p>
<blockquote>
<p>One hundred years ago, on September 26, 1929, President Herbert Hoover gathered a group of social scientists at the White House. He asked them to begin research on the most detailed report ever produced on the state of the nation. Four years later, running more than 1,500 pages long, Recent Social Trends was published, offering an unusually granular look at life in the mid-1920s.</p>
<p><a href="https://www.derekthompson.org/p/america-1926-an-absurdly-deep-dive">America, 1926: What a Forgotten 100-Year-Old Report Says About Who We Are</a></p>
</blockquote>
<p>I'll cherry-pick, for my own archive.</p>
<hr />
<blockquote>
<p>[...] No other country rivaled America’s automotive love affair. According to the historian Bill Bryson, 1920s Kansas alone had more vehicles than France. Car ownership created an entirely new way of thinking about the self in relation to the environment—an “automobile psychology.” Social scientists spoke of the modern “gypsy family,” which seemed to spend more time inside the car than outside it.</p>
</blockquote>
<p>Something that still strikes Europeans when they visit US. Americans, and America, has a special relationship with cars. Dreams, habits, infrastructure, cities, almost
every aspect of the american life has the car at its core.</p>
<hr />
<blockquote>
<p>It is remarkable to read the report’s analysis of radio’s effect on American life, because it reads so alarmingly modern. Above all, the authors were concerned that radio encroached on individuality by mind-wiring each American to a global monolith of news and entertainment. Mass media organizations created “greater possibilities for social manipulation,” they wrote, and radio threatened to turn the individual into what the philosopher Martin Heidegger in this period called a “they-self”—a being who, failing to achieve authenticity, fully adopts the tastes, habits, and beliefs of the universal crowd.</p>
</blockquote>
<hr />
<blockquote>
<p>But the social scientists did not see these trends as altogether good. They worried that modern life, defined in equal parts by urbanization and technology, obliterated people’s values and their sense of self. Even as they gawked at the increase in patents—which grew more than 20-fold between the 1850s and the 1920s—they worried that a growing number of discoveries would bring “problems of morals, of education, of law, of leisure time, of unemployment, of speed, of uniformity and of differentiation.”</p>
</blockquote>
<blockquote>
<p>[...] In 1903, the sociologist Georg Simmel anticipated the anxieties of the Twenties—ours and theirs—when he observed that in cities “money takes the place of all the manifoldness of things” and becomes “a common denominator of all values.” Money “hollows out the core of things, their peculiarities, their specific values, and their uniqueness and incomparability in a way which is beyond repair.”</p>
</blockquote>
<hr />
<p>Go read the whole piece: <a href="https://www.derekthompson.org/p/america-1926-an-absurdly-deep-dive">https://www.derekthompson.org/p/america-1926-an-absurdly-deep-dive</a></p>

]]></content:encoded>

    </item>
    
    <item>
      <title>LLMs Should Be Common Carriers</title>
      <link>https://blog.vrypan.net/2026/07/03/260703-llms-should-be-common-carriers/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/03/260703-llms-should-be-common-carriers/</guid>
      <pubDate>Fri, 03 Jul 2026 05:11:56 +0000</pubDate>
      <description>Frontier LLMs are becoming infrastructure.</description>
      <content:encoded><![CDATA[
	
	<p>Frontier LLMs are becoming infrastructure.</p>
<p>People increasingly use them as the interface to the world's knowledge. They write software, learn, conduct research, communicate, and solve problems through them. As they become general-purpose infrastructure, they should increasingly be treated like other infrastructure.</p>
<p>The closest historical analogy is the telephone network.</p>
<p>Telephone companies made enormous upfront investments to build nationwide networks. Society allowed them to recover those investments and earn profits by charging for access. It did not allow them to decide who could make lawful phone calls or what services could be built on top of the network. They became common carriers: providers of a neutral service.</p>
<hr />
<h2>Commercial LLMs should evolve in the same direction</h2>
<p>AI providers should be free to charge whatever the market will bear for inference. They should compete on model quality, latency, reliability, and price. But once a customer pays for inference, the resulting outputs should be theirs to use for any lawful purpose.</p>
<p><strong>That includes model distillation.</strong></p>
<p>Blocking distillation is analogous to an incumbent telephone company claiming the right to prevent customers from using its network to build competing communications services. Common carrier law evolved in the opposite direction: the operator could charge for access, but not dictate what lawful services customers built on top of the network.</p>
<p>The same principle should apply to LLMs. The provider is selling computation, not control over what customers lawfully do with its results.</p>
<p>Making distillation a protected right would likely increase token prices in the short term. AI companies would need to price inference closer to its true cost, because selling below cost would no longer subsidize only customer acquisition—it would also subsidize future competitors.</p>
<p>That's how competitive markets should work. Anyone building a competing model would also pay those same market prices to distill existing models.</p>
<p>Competition would shift from contractual restrictions to engineering: better models, lower costs, and more efficient infrastructure.</p>
<hr />
<h2>The natural way to give back</h2>
<p>This is also consistent with how frontier models came to exist. They were trained on humanity's accumulated knowledge: scientific research, literature, open-source software, and countless public contributions spanning centuries. The engineering required to build these systems deserves to be rewarded. The reward should be payment for providing inference, not the power to control how customers use the outputs they have purchased.</p>
<p>As LLMs become essential infrastructure, neutrality should become part of the bargain. Society provides the knowledge these models are built on; society should retain the right to build on them.</p>
<hr />
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">Note</p>
<p>This post started as a reply to <a href="https://www.wysr.xyz/p/the-private-capture-of-public-genius">The Private Capture of Public Genius</a>. Cameron recognizes the issue with AI models distilling centuries of knowledge and keeping it for themselves, but suggests paying back the society through royalties. I think royalties is the wrong model that will create more problems, including legal and other speed bumps that would stifle innovation and competition.</p>
</div>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/07/03/260703-llms-should-be-common-carriers/image.png"
   type="image/png"
   length="2837422"/>

    </item>
    
    <item>
      <title>What's wrong with EU age verification? Part II</title>
      <link>https://blog.vrypan.net/2026/07/01/260702-whats-wrong-with-eu-age-verification/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/07/01/260702-whats-wrong-with-eu-age-verification/</guid>
      <pubDate>Wed, 01 Jul 2026 21:11:45 +0000</pubDate>
      <description>My EU age verification post triggered a long debate on lobste.rs. Here are the points I found most worth engaging with.</description>
      <content:encoded><![CDATA[
	
	<p>My EU age verification post <a href="https://lobste.rs/s/29laqs/what_s_wrong_with_eu_age_verification">triggered a long debate on lobste.rs</a>. Here are the points I found most worth engaging with.</p>
<p>I’ve probably missed some, the discussion was <strong>really</strong> long, and I’ve also received feedback though other channels. I will post again if I feel there’s more to share on the topic.</p>
<hr />
<h2>Favoring the incumbents</h2>
<p>A point I missed the first time.</p>
<p>In the EU design, an authority issues me an attestation that I'm of legal age; it lands in my digital wallet. When a site needs to check, the wallet generates a zero-knowledge proof that it holds the attestation, without disclosing it.</p>
<p>With most wallets, we assume the user protects their credential — nobody voluntarily leaks the password to a wallet holding money for example. Age verification inverts this: the user <strong>is</strong> part of the threat model. Some users, given the chance, will happily share their &quot;over 18&quot; attestation with a minor.</p>
<p>So the system can't trust the user. It has to trust the device or the wallet instead. And for the issuer to know the attestation stays locked inside, it has to control which devices (modern iOS/Android) and which wallets (those carrying a government-signed certificate) are allowed.</p>
<p>The bottom line, and it's not a bright one:</p>
<ul>
<li>In practice, let’s admit it, you'll have to use iOS or Android.</li>
<li>Probably the official government wallet.</li>
<li>Experimental mobile OSes are locked out of any site requiring age verification.</li>
<li>Alternative OSes could qualify, but only by getting EU-certified — which often defeats the point of running one.</li>
</ul>
<p>The silver lining: your desktop can be anything. Run a hand-compiled Linux distro on obscure hardware with a browser you built yourself — you'll just scan a QR code with your phone when age verification comes up.</p>
<p>How much this shifts the balance is a judgment call. For me it's a mark against the proposal, but not a fatal one.</p>
<hr />
<h2>Can I trust the official app?</h2>
<p>The EU open-sourced the wallet. But how do I know the binary my government actually shipped matches that source — that it doesn't quietly log which sites requested verification, or hand those URLs to an intelligence agency?</p>
<p>The EU <strong>must</strong> address this, and not just for these wallets, but for every piece of software that becomes European digital infrastructure. <strong>I'd strongly support a directive mandating reproducible builds and signed source/build metadata.</strong> It's the only way citizens can actually trust the stack.</p>
<hr />
<h2>Standards fragmentation</h2>
<p>Also valid. The EU has one age-verification standard, the UK another, California a third. What's a site supposed to implement? I don't have an answer.</p>
<hr />
<h2>What's built to &quot;protect the kids&quot; gets used on everyone later</h2>
<p>Today it's age. Tomorrow it could be citizenship, income, or gender.</p>
<p>The EU says age is just one use of the wallet — the same design verifies anything. Often that's a real improvement over paper: proving you're eligible to vote, that you live in a school's catchment area, or that you qualify for a youth or senior rail fare, all while revealing less than a physical document would.</p>
<p>But the same machinery turns the other way just as easily: prove your religion, your ancestry, your skin color, that you've never been convicted — to access this service.</p>
<p>I'm uneasy about all of it. Then again, a state that wants to impose such conditions needs no privacy-preserving mechanism, and wouldn't want one. Iran, Russia, China and Afghanistan restrict access to whatever they dislike just fine without it.</p>
<p>Still, the concern stands.</p>

]]></content:encoded>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/06/30/260630-note-43819/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/30/260630-note-43819/</guid>
      <pubDate>Tue, 30 Jun 2026 21:23:40 +0300</pubDate>
      <description>This is the context imho:</description>
      <content:encoded><![CDATA[
	
	<blockquote>
<p>Speaking to reporters on Tuesday in Washington, Dutch Trade Minister Sjoerd Sjoerdsma said the U.S.
and the Netherlands share common goals over preventing sensitive technology from ending up in dangerous hands.</p>
<p>However, &quot;elements in that Act seem to suggest that the United States might take control over
some of these decisions that affect our national security and the way our companies operate,&quot; he said.
If the excellent cooperative working relationship with Washington &quot;becomes cooperation by force...
that is undesirable from our point of view,&quot; he added.</p>
<p>—<a href="https://www.reuters.com/world/china/netherlands-join-us-led-pax-silica-ai-initiative-despite-asml-dispute-2026-06-23/">Reuters</a></p>
</blockquote>
<p>This is the context imho:</p>
<p>You are the Netherlands. Your neighbor and EU partner, Denmark, was just threatened with war over
Greenland. You too have a couple of islands, like Curacao, that belong to the Kingdom of the Netherlands,
and happen to be in Trump's back yard. The US administration has made clear that no treaties, no international law
has value, only power. Why would you give up control over ASML, your most powerful leverage point?</p>

]]></content:encoded>

    </item>
    
    <item>
      <title>What's wrong with EU age verification?</title>
      <link>https://blog.vrypan.net/2026/06/29/260629-whats-wrong-with-eu-age-verification/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/29/260629-whats-wrong-with-eu-age-verification/</guid>
      <pubDate>Mon, 29 Jun 2026 20:01:41 +0000</pubDate>
      <description>There is a lot of chatter about online age verification. When the criticism turns to the EU's approach, it tends to be either uninformed or deliberately misleading.</description>
      <content:encoded><![CDATA[
	
	<p>There is a lot of chatter about online age verification. When the criticism turns to the EU's approach, it tends to be either uninformed or deliberately misleading.</p>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">Note</p>
<p>Make sure you also read <a href="/2026/07/01/260702-whats-wrong-with-eu-age-verification/">What's wrong with EU age verification? Part II</a>
that describes some of the weak points of the proposal.</p>
</div>
<h2>Do we need online age verification?</h2>
<p>This is partly an ideological question.</p>
<p>If you think age verification should not exist at all, the technical details won't matter to you. No implementation will be acceptable, because the objection is to age gates themselves, not to any particular mechanism.</p>
<p>I am not in that camp.</p>
<p>A 9-, 10-, or 14-year-old is not ready to wander the open internet without limits. This isn't just a matter of parental taste. Children and teenagers are still developing the cognitive, emotional, and social skills needed to handle manipulation, addiction loops, sexual content, gambling mechanics, grooming, harassment, algorithmic radicalization, and the rest of what adults themselves often struggle with.</p>
<p>Isn't it the family's job to set the limits?</p>
<p>Yes and no.</p>
<p>When children are very young, parents can set strict boundaries. But as kids move into their teens, parents also have an obligation to loosen them. Teenagers need spaces where they can act independently, make decisions, and talk to others — where they can learn to navigate the world without a parent looking over their shoulder.</p>
<p>The question is whether we can build online spaces where that gradual freedom is possible.</p>
<p>&quot;I raised my kids to be smart and self-confident, and they would never do X.&quot;</p>
<p>Maybe. I hope I did too. But not all children are the same. They don't share the same temperament, support, confidence, parents, or protection. And even grounded, intelligent teenagers are vulnerable at times.</p>
<p>We already accept age restrictions elsewhere. Children can't drive, drink, gamble, or enter certain venues before a certain age. It is not absurd to think parts of the internet should be age-restricted too.</p>
<p>The hard question isn't whether age limits can ever be legitimate. It's how to enforce them without turning the internet into an identity checkpoint.</p>
<h2>Is age verification the first step to mass online surveillance?</h2>
<p>It can be.</p>
<p>Most people assume age verification means scanning an ID, uploading a passport, taking a selfie, or submitting to a face scan — which is exactly what many services already ask for.</p>
<p>Implemented that way, the critics are right to be alarmed.</p>
<p>To prove to a porn site, a gambling site, an online liquor store, or a religious forum that I'm an adult, I should not have to hand over my name, date of birth, ID number, face, address, or passport. That is a dangerous amount of information to give any private website, let alone one dealing with sensitive content.</p>
<p>There is another familiar pattern: sign in with a trusted third party.</p>
<p>A site might ask me to authenticate through my bank, my Google or Apple account, my mobile operator, or a government identity service. That avoids handing the site my documents, but it creates a different problem: now the identity provider learns which age-restricted sites I visit.</p>
<p>That's not much better.</p>
<p>In one version, the website learns who I am. In the other, the identity provider learns where I go. Both are scary.</p>
<p>But neither is the only way to do this.</p>
<h2>The better primitive: a signed age attestation</h2>
<p>A better design starts from a simple idea: prove only the thing that needs proving.</p>
<p>The website doesn't need my name, my date of birth, my ID number, or whether I'm 19, 37, or 74. It only needs to know whether I clear a threshold:</p>
<pre lang="text"><code>age &gt;= required_age
</code></pre>
<p>The easiest way to picture this is a digitally signed attestation.</p>
<p>Take the offline version:</p>
<ol>
<li>You go to a government office.</li>
<li>You show your passport or national ID.</li>
<li>They issue a card that says only one thing: &quot;over 18&quot;.</li>
<li>The card carries official seals, signatures, and anti-forgery features.</li>
<li>You show that card at the entrance of an age-restricted venue.</li>
<li>The venue can confirm the card is genuine without learning your name, date of birth, or ID number.</li>
</ol>
<p>The digital version is the same idea, with cryptography. An authorized issuer verifies your age once and issues a signed credential:</p>
<pre lang="json"><code>{
  &quot;claim&quot;: &quot;age_over_18&quot;,
  &quot;value&quot;: true,
  &quot;issuer&quot;: &quot;Trusted Age Attestation Provider&quot;,
  &quot;valid_until&quot;: &quot;2027-12-31&quot;
}
</code></pre>
<p>The issuer signs it:</p>
<pre lang="text"><code>signature = Sign(issuer_private_key, attestation)
</code></pre>
<p>A website can later verify it:</p>
<pre lang="text"><code>Verify(issuer_public_key, attestation, signature)
</code></pre>
<p>The key property: the website never has to contact the issuer. It only needs to know the attestation was signed by a trusted issuer and is still valid. And the issuer never learns where — or whether — you used it.</p>
<p><figure><img src="https://blog.vrypan.net/2026/06/29/260629-whats-wrong-with-eu-age-verification/image.png" alt="" /></figure></p>
<p>This is the core of the EU approach. The EU Age Verification Blueprint describes a system built on Proof of Age attestations, relying parties, attestation providers, age-verification apps, and trust lists. It is aligned with the European Digital Identity Wallet architecture and lets users prove they are over a certain age without disclosing their exact age or identity. <sup class="footnote-ref"><a href="#fn-1" id="fnref-1" data-footnote-ref>1</a></sup> <sup class="footnote-ref"><a href="#fn-3" id="fnref-3" data-footnote-ref>2</a></sup> <sup class="footnote-ref"><a href="#fn-4" id="fnref-4" data-footnote-ref>3</a></sup></p>
<h2>Selective disclosure and zero-knowledge proofs</h2>
<p>A signed attestation is already far better than uploading an ID to every site. But a subtle privacy risk remains.</p>
<p>If the wallet presents the same signed credential to many websites, those sites may be able to correlate visits. Even without my name, they could learn that the same anonymous adult visited site A, site B, and site C.</p>
<p>That's why the stronger version uses selective disclosure or zero-knowledge proofs. Instead of showing the underlying attestation, the wallet proves a statement about it:</p>
<blockquote>
<p>I hold a valid Proof of Age attestation,
signed by a trusted issuer,
that proves age &gt;= 18.</p>
</blockquote>
<p>...without revealing the attestation itself.</p>
<p>The EU technical documentation describes this as generating a zero-knowledge proof from a Proof of Age attestation: the app encodes the attestation as private input to a circuit, uses public inputs such as the attestation provider's public key, and produces a zkSNARK proof. <sup class="footnote-ref"><a href="#fn-2" id="fnref-2" data-footnote-ref>4</a></sup> <sup class="footnote-ref"><a href="#fn-5" id="fnref-5" data-footnote-ref>5</a></sup></p>
<p>That's a big deal.</p>
<p>It means the privacy-preserving version isn't:</p>
<blockquote>
<p>Here is my ID.</p>
</blockquote>
<p>It isn't:</p>
<blockquote>
<p>Here is my date of birth.</p>
</blockquote>
<p>It isn't even:</p>
<blockquote>
<p>Here is my signed age credential.</p>
</blockquote>
<p>It's closer to:</p>
<blockquote>
<p><strong>Here is cryptographic proof that I hold a valid credential proving I am over 18.</strong>
<strong>You can verify the proof, but you learn nothing about who I am.</strong></p>
</blockquote>
<p>That's the right direction, and the signs are that the EU is getting it right.</p>
<h2>What privacy advocates should focus on</h2>
<p>None of this makes the EU system automatically safe. A privacy-preserving architecture can still be ruined by bad implementation.</p>
<p>Things that would break the promise:</p>
<ul>
<li>stable identifiers reused across websites</li>
<li>the issuer being contacted on every age check</li>
<li>centralized logs of verification events</li>
<li>wallet telemetry recording which relying parties requested proofs</li>
<li>websites demanding more attributes than necessary</li>
<li>weak wallet security</li>
<li>poor UX that nudges users into oversharing</li>
<li>browser or device fingerprinting that links otherwise anonymous proofs</li>
<li>revocation checks that reveal where credentials are used</li>
<li>closed, unaudited national implementations</li>
</ul>
<p>So instead of fighting a system that is built to preserve privacy, we should be fighting to put the right checks in place — the ones that guarantee the implementation actually honors it.</p>
<section class="footnotes" data-footnotes>
<ol>
<li id="fn-1">
<p>The EU Age Verification Solution documentation describes the system as aligned with the Digital Services Act, eIDAS 2.0, and the European Digital Identity Wallet Architecture and Reference Framework, using attestation formats, protocols, and trust-model bindings for interoperability. <a href="https://ageverification.dev/">https://ageverification.dev/</a> <a href="#fnref-1" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="1" aria-label="Back to reference 1">↩</a></p>
</li>
<li id="fn-3">
<p>The Commission says the age-verification blueprint lets users prove they are over 18 for restricted adult content without revealing other personal information, and that it is based on open-source technology and designed to be interoperable with future European Digital Identity Wallets. <a href="https://digital-strategy.ec.europa.eu/en/news/commission-makes-available-age-verification-blueprint">https://digital-strategy.ec.europa.eu/en/news/commission-makes-available-age-verification-blueprint</a> <a href="#fnref-3" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="2" aria-label="Back to reference 2">↩</a></p>
</li>
<li id="fn-4">
<p>The Commission's common-approach document says the blueprint allows users to prove they are over a certain age, such as 15, 18, or 65, without disclosing exact age or identity. <a href="https://digital-strategy.ec.europa.eu/en/library/commission-sets-out-common-approach-eu-wide-age-verification-technologies">https://digital-strategy.ec.europa.eu/en/library/commission-sets-out-common-approach-eu-wide-age-verification-technologies</a> <a href="#fnref-4" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="3" aria-label="Back to reference 3">↩</a></p>
</li>
<li id="fn-2">
<p>The EU technical annex on zero-knowledge proofs describes generating a zkSNARK proof from a Proof of Age attestation by encoding the attestation as private input and exposing only public verification inputs such as the attestation provider's public key. <a href="https://ageverification.dev/Technical%20Specification/annexes/annex-B/annex-B-zkp/">https://ageverification.dev/Technical%20Specification/annexes/annex-B/annex-B-zkp/</a> <a href="#fnref-2" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="4" aria-label="Back to reference 4">↩</a></p>
</li>
<li id="fn-5">
<p>The verifier developer guide describes standard <code>mdoc</code> attestation as a signed proof confirming that the user meets the age threshold, and ZKP as an enhanced proof format providing stronger privacy and no linkable identifier. <a href="https://ageverification.dev/Getting%20started/developer_guide_verifier/">https://ageverification.dev/Getting%20started/developer_guide_verifier/</a> <a href="#fnref-5" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="5" aria-label="Back to reference 5">↩</a></p>
</li>
</ol>
</section>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/06/29/260629-whats-wrong-with-eu-age-verification/image.png"
   type="image/png"
   length="441921"/>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/06/28/260628-note-59284/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/28/260628-note-59284/</guid>
      <pubDate>Sun, 28 Jun 2026 18:08:04 +0300</pubDate>
      <description>I really enjoy The Linkfest. I suggest you subscribe too.</description>
      <content:encoded><![CDATA[
	
	<blockquote>
<p>It’s time for &quot;the opposite of doomscrolling” — a new Linkfest, in which
I sort through the planet-wide digital rummage-sale of the Internet to locate
the finest items of culture, science and technology, just for you.</p>
</blockquote>
<p>I really enjoy <a href="https://buttondown.com/clivethompson">The Linkfest</a>. I suggest
you subscribe too.</p>

]]></content:encoded>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/06/28/260628-note-57146/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/28/260628-note-57146/</guid>
      <pubDate>Sun, 28 Jun 2026 17:32:26 +0300</pubDate>
      <description>Some people are worth listening to. I'm tired of overnight influencers, and people who somehow are considered deep thinkers because they made a lot of money.</description>
      <content:encoded><![CDATA[
	
	<blockquote>
<p>Open Tab: Kevin Kelly
The radical optimist on starting Wired, stories from the early internet,
originating 1,000 True Fans, and why the only way to steer technology is to use it
<a href="https://on.substack.com/p/open-tab-kevin-kelly">https://on.substack.com/p/open-tab-kevin-kelly</a></p>
</blockquote>
<p>Some people are worth listening to. I'm tired of overnight influencers, and
people who somehow are considered deep thinkers because they made a lot of money.</p>

]]></content:encoded>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/06/28/260628-homepage-update/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/28/260628-homepage-update/</guid>
      <pubDate>Sun, 28 Jun 2026 00:16:14 +0000</pubDate>
      <description>I updated my homepage: People evolve and change, the homepage should stay in sync with the owner. In the process I digged into some of the older posts and pages (I've been maintaining this domain for decades) and took the opportunity to fix some broken links, and resurface some l...</description>
      <content:encoded><![CDATA[
	
	<p>I updated <a href="https://vrypan.net">my homepage</a>: People evolve and change,
the homepage should stay in sync with the owner. In the process I digged
into some of the older posts and pages (I've been maintaining this
domain for decades) and took the opportunity to fix some broken links,
and resurface some long lost pages :-)</p>

]]></content:encoded>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/06/28/260628-bubbles-town/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/28/260628-bubbles-town/</guid>
      <pubDate>Sun, 28 Jun 2026 00:04:41 +0000</pubDate>
      <description>bubbles.town is a beautiful way to dicover blogs and posts. It's social, but there's some friction here and there, the community is large enough, but not massive, and the blogs are hand-selected (as far as I can tell). The result is something close to a cozy corner, which I enjoy...</description>
      <content:encoded><![CDATA[
	
	<p><a href="https://bubbles.town">bubbles.town</a> is a beautiful way to dicover blogs and posts.
It's social, but there's some friction here and there, the community is large
enough, but not massive, and the blogs are hand-selected (as far as I can tell).
The result is something close to a cozy corner, which I enjoy a lot.</p>

]]></content:encoded>

    </item>
    
    <item>
      <title>We don't have an information problem</title>
      <link>https://blog.vrypan.net/2026/06/27/260627-not-an-information-problem/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/27/260627-not-an-information-problem/</guid>
      <pubDate>Sat, 27 Jun 2026 06:13:45 +0000</pubDate>
      <description>@simonmaechling wrote:</description>
      <content:encoded><![CDATA[
	
	<p><a href="https://x.com/simonmaechling/status/2070433988804587974">@simonmaechling wrote</a>:</p>
<blockquote>
<p>We don't have an information problem. We have a scientific literacy and trust problem.</p>
</blockquote>
<p>I agree. Kids learn science as a set of established facts, formulas, and historical discoveries.</p>
<p>Best case is they come away from high school aware of some sequence of wrong theories eventually replaced by the correct one. Atoms were indivisible solid spheres, then we discovered they contained electrons, then protons and neutrons, and finally quantum mechanics revealed &quot;what atoms really are&quot;.</p>
<p>They almost always miss that scientific theories are rarely discarded because they are &quot;false.&quot; Scientific theories are models with a limited domain of validity and as new evidence appears, we develop theories that explain a wider range of phenomena while preserving the successful predictions of earlier models where those models already worked.</p>
<p>Students learn the fun anecdotes (Newton was sitting under a tree), but no attention is paid to how scientific knowledge is actually produced. The scrutiny, the debates, how they were resolved, what scientists could not know at the time a debate was resolved.</p>
<p>I think it's more important for a 17-year old to finish school knowing why peer review matters, how to read a research paper, or why a single study is almost never the final word on a subject, than of being able to calculate the distance traveled by an object thrown in the air at a 45 degrees angle in the North Pole or what happens if a solution of calcium chloride is mixed with a solution of sodium carbonate or how to calculate an integral.</p>
<p>They should be taught the difference between a hypothesis, a model, a law, and a theory, and how scientific theories evolve as new evidence accumulates. That disagreement is a normal part of science: competing ideas are tested, challenged, replicated, and gradually accepted or discarded based on evidence rather than authority. To treat science as the best available model to explain the world, why we need such a model, and how to compare models.</p>
<p>Understanding this process is more valuable than memorizing isolated facts, because it equips people to evaluate new claims, recognize weak evidence, and distinguish genuine scientific consensus from speculation or misinformation.</p>

]]></content:encoded>

    </item>
    
    <item>
      <title>Om Malik, 1966-2026</title>
      <link>https://blog.vrypan.net/2026/06/26/260626-om-malik-1966-2026/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/26/260626-om-malik-1966-2026/</guid>
      <pubDate>Fri, 26 Jun 2026 16:04:55 +0000</pubDate>
      <description>GigaOm, was one of my favorite feeds in my RSS reader.</description>
      <content:encoded><![CDATA[
	
        <img  src="https://blog.vrypan.net/2026/06/26/260626-om-malik-1966-2026/om.jpeg">
        
	<p><a href="https://en.wikipedia.org/wiki/Gigaom">GigaOm</a>, was one of my favorite feeds in my RSS reader.</p>
<p>Your voice at The GigaOm Show, and your appearences in Gillmor Gang, was good company
during commute and slow weekend mornings.</p>
<p>I appreciated your grounded views, your intellectual honesty, and you were one
of the few stars of that era I still appreciated and respected.</p>
<p><a href="https://om.co/2026/06/24/1966-2026/">https://om.co/2026/06/24/1966-2026/</a></p>
<p>Farewell, Om. You will be missed.</p>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/06/26/260626-om-malik-1966-2026/om.jpeg"
   type="image/jpeg"
   length="307488"/>

    </item>
    
    <item>
      <title>The ghost girl</title>
      <link>https://blog.vrypan.net/2026/06/20/260620-the-ghost-girl/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/20/260620-the-ghost-girl/</guid>
      <pubDate>Sat, 20 Jun 2026 15:17:16 +0000</pubDate>
      <description></description>
      <content:encoded><![CDATA[
	
        <img  src="https://blog.vrypan.net/2026/06/20/260620-the-ghost-girl/image.jpeg">
        
	
]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/06/20/260620-the-ghost-girl/image.jpeg"
   type="image/jpeg"
   length="1367721"/>

    </item>
    
    <item>
      <title>We have to go back!</title>
      <link>https://blog.vrypan.net/2026/06/19/260619-we-have-to-go-back/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/19/260619-we-have-to-go-back/</guid>
      <pubDate>Fri, 19 Jun 2026 17:32:21 +0000</pubDate>
      <description>In 2024, Farcaster was the most decentralized it had ever been: a peer-to-peer network of hubs that were, at the protocol level, peers. My hub running on a Raspberry Pi at home, your hub on a VPS, Merkle’s hubs powering Warpcast, Neynar’s hubs powering various independent apps — ...</description>
      <content:encoded><![CDATA[
	
        <img  src="https://blog.vrypan.net/2026/06/19/260619-we-have-to-go-back/lost.jpeg">
        
	<p>In 2024, Farcaster was the most decentralized it had ever been: a peer-to-peer network of hubs that were, at the protocol level, peers. My hub running on a Raspberry Pi at home, your hub on a VPS, Merkle’s hubs powering Warpcast, Neynar’s hubs powering various independent apps — they were all peers. Not equal, because network position, capacity, reliability, and importance mattered, but peers.</p>
<p>A year later, in spring 2025, Farcaster migrated to Snapchain.</p>
<p>Snapchain introduced globally ordered blocks, consensus, and, most importantly, higher throughput. Throughput was the weak point of the hub approach, and Snapchain fixed it in order to allow Farcaster to grow from tens of thousands of users to millions of users. But it sacrificed the one unique feature Farcaster had: decentralization.</p>
<p>The core team’s North Star was simple: grow 1000x or die. In pursuit of high growth, they introduced a number of additional client-side features: Frames and Mini Apps, wallets, and Sign in with Farcaster. What used to be a principle — that protocol changes should not be introduced unless a feature has matured — was forgotten, and protocol changes were introduced overnight to support things that looked attractive product-wise, like Farcaster Pro. Fees were reduced to practically zero, despite the original thesis that fees are the friction required to fight spam and abuse of network resources.</p>
<p>All these features made building alternative clients harder and harder, not to mention that the lines between “the protocol” and “clients” were blurred: Farcaster is not the protocol anymore; it’s <strong>the</strong> client.</p>
<p>These were good features. Fun to use and build for. But the expected growth never came, and all we are left with is a relatively small social network of nice and interesting people, operated by a company.</p>
<p>It’s a nice place to be, and it’s where I prefer to hang out, but it’s no longer something I’m passionate about. I wouldn’t wear a Farcaster T-shirt today. I wouldn’t try to recruit new users. I removed “I’m on Farcaster” from my X account.</p>
<h2>Can we go back?</h2>
<p>Which brings me to Jack Shephard’s words in the <em>Lost</em> Season 3 finale: <em><strong>We have to go back!</strong></em></p>
<p>What if Farcaster went back to a hub-based architecture: simple, peer-to-peer, truly decentralized, where it’s easy to run a peer, easy to build self-hosted bots, and easy to build a client?</p>
<p>No bells and whistles. No Mini Apps. No assumption that every client must become a wallet, browser, app host, and social feed at the same time. Strip the network back down to the basics: identity, casts, replies, follows, reactions, and low data retention that makes home hosting feasible.</p>
<p>Then we can rethink Sign in with Farcaster, long-term archiving, scaling, storage fees, app embedding, and everything else. Maybe growth will come at some point in the future, maybe not, but as long as there are a handful of people running a hub at home, the network will be there.</p>
<p>This is not 2024. We know more now. We have seen what worked and what did not. We are not after 1000x growth at any cost. We have new tools for building software, especially when the protocols involved are simple. We can do better than the last time.</p>
<p>Personally, I would be very enthusiastic about this prospect.</p>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/06/19/260619-we-have-to-go-back/lost.jpeg"
   type="image/jpeg"
   length="260451"/>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/06/18/260619-ai-mainframe/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/18/260619-ai-mainframe/</guid>
      <pubDate>Thu, 18 Jun 2026 22:00:12 +0000</pubDate>
      <description>We are in the mainframe era of AI: A few centralized compute providers and millions of "dumb" clients connected to them.</description>
      <content:encoded><![CDATA[
	
	<p>We are in the mainframe era of AI: A few centralized compute providers and millions of &quot;dumb&quot;
clients connected to them.</p>

]]></content:encoded>

    </item>
    
    <item>
      <title>bckt v0.7.3 is out</title>
      <link>https://blog.vrypan.net/2026/06/18/260618-bckt-073-is-out/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/18/260618-bckt-073-is-out/</guid>
      <pubDate>Thu, 18 Jun 2026 16:28:50 +0000</pubDate>
      <description>bckt v0.7.3, my "opinionated but flexible static site generator for blogs", is out!</description>
      <content:encoded><![CDATA[
	
	<p><a href="https://github.com/vrypan/bckt">bckt</a> v0.7.3, my <em>&quot;opinionated but flexible static site generator for blogs&quot;</em>, is out!</p>
<p>The most notable change is the installation bundles come with a nice set of themes, as well as some demo content:</p>
<pre><code># Install
brew install vrypan/tap/bckt

# Create a test directory and initialize a new blog
# with the demo microblog posts and the micro theme
mkdir test &amp;&amp; cd test
bckt init --theme micro --demo microblog 

# Start the local http server to preview it
# (the static pages are in the html/ directory)
bckt dev
</code></pre>
<p>Use <code>--demo microblog</code> to test how it looks with short, titleless posts and
<code>--demo articles</code> to see a version with longer articles, closer to a typical
blog post. But all themes will work nicely with both types of content.</p>
<hr />
<p>You will probably want to use an AI agent to create your own theme. There is
an <code>AGENTS.md</code> that will make this very easy.</p>
<p>For example, let's use <a href="https://mxb.dev/">Max Böck</a>'s blog, which has a really nice design.</p>
<p><code>cd</code> in your blog directory and try prompt like this:</p>
<pre><code>- Read https://github.com/vrypan/bckt/blob/main/themes/AGENTS.md
- Check themes/* to see how the existing themes are structured.
- create a theme inspired by https://mxb.dev/blog/ 
</code></pre>
<p><figure><img src="https://blog.vrypan.net/2026/06/18/260618-bckt-073-is-out/mxbdev-inspired.png" alt="" title="Theme inspired by mbx.dev/blog/. I suggest you visit it to see the original." /><figcaption>Theme inspired by mbx.dev/blog/. I suggest you visit it to see the original.</figcaption></figure></p>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/06/18/260618-bckt-073-is-out/mxbdev-inspired.png"
   type="image/png"
   length="308328"/>

   <enclosure url="https://blog.vrypan.net/2026/06/18/260618-bckt-073-is-out/og.png"
   type="image/png"
   length="490020"/>

   <enclosure url="https://blog.vrypan.net/2026/06/18/260618-bckt-073-is-out/theme-bckt3.png"
   type="image/png"
   length="338966"/>

   <enclosure url="https://blog.vrypan.net/2026/06/18/260618-bckt-073-is-out/theme-micro-dark.png"
   type="image/png"
   length="385628"/>

   <enclosure url="https://blog.vrypan.net/2026/06/18/260618-bckt-073-is-out/theme-micro-light.png"
   type="image/png"
   length="384084"/>

   <enclosure url="https://blog.vrypan.net/2026/06/18/260618-bckt-073-is-out/theme-microx-dark.png"
   type="image/png"
   length="414777"/>

   <enclosure url="https://blog.vrypan.net/2026/06/18/260618-bckt-073-is-out/theme-microx-light.png"
   type="image/png"
   length="413808"/>

   <enclosure url="https://blog.vrypan.net/2026/06/18/260618-bckt-073-is-out/theme-modern.png"
   type="image/png"
   length="360507"/>

   <enclosure url="https://blog.vrypan.net/2026/06/18/260618-bckt-073-is-out/theme-rntz.png"
   type="image/png"
   length="463988"/>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/06/17/260618-football/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/17/260618-football/</guid>
      <pubDate>Wed, 17 Jun 2026 21:30:25 +0000</pubDate>
      <description>Football may be the last mass spectacle that still resists instant gratification.</description>
      <content:encoded><![CDATA[
	
	<p>Football may be the last mass spectacle that still resists instant gratification.</p>

]]></content:encoded>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/06/13/260613-note-30926/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/13/260613-note-30926/</guid>
      <pubDate>Sat, 13 Jun 2026 09:08:47 +0300</pubDate>
      <description>The US government, citing national security authorities, has issued an export control directive to suspend all access to Fable 5 and Mythos 5 by any foreign national, whether inside or outside the United States, including foreign national Anthropic employees. https://x.com/Anthro...</description>
      <content:encoded><![CDATA[
	
	<p><em>The US government, citing national security authorities, has issued an export control directive to suspend all access to Fable 5 and Mythos 5 by any foreign national, whether inside or outside the United States, including foreign national Anthropic employees.</em> <a href="https://x.com/AnthropicAI/status/2065597531644743999">https://x.com/AnthropicAI/status/2065597531644743999</a></p>
<p>This is a great example why the EU must try not to depend on AI developed/operated outside of its borders, even if it's inconvenient at times.</p>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/06/13/260613-note-30926/screenshot.png"
   type="image/png"
   length="285768"/>

    </item>
    
    <item>
      <title>A forgotten terminal trick: the host-writable status line.</title>
      <link>https://blog.vrypan.net/2026/06/13/260613-vt-host-writable-status-line/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/13/260613-vt-host-writable-status-line/</guid>
      <pubDate>Sat, 13 Jun 2026 04:59:45 +0000</pubDate>
      <description>The VT102 (1978) and VT220 (1983) had the familiar 24-line terminal screen . This the reason your terminal defaults to 80×24, or 132×24. The VT320 (1987) kept a 24-line main display for compatibility, but added something extra: a 25th line at the bottom, reserved for status infor...</description>
      <content:encoded><![CDATA[
	
	<p>The VT102 (1978) and VT220 (1983) had the familiar 24-line terminal screen <sup class="footnote-ref"><a href="#fn-1" id="fnref-1" data-footnote-ref>1</a></sup>. This the reason your terminal defaults to 80×24, or 132×24. The VT320 (1987) kept a 24-line main display for compatibility, but added something extra: a 25th line at the bottom, reserved for status information.</p>
<p>That extra line could be hidden, used by the terminal itself, or made writable by the host. In its normal “indicator” mode, the terminal used it for local state: cursor position, printer status, modem status, and similar information. But in host-writable mode, software on the host side of the terminal connection could write directly to that line. <sup class="footnote-ref"><a href="#fn-2" id="fnref-2" data-footnote-ref>2</a></sup></p>
<p>The two relevant control sequences are <code>DECSSDT</code> and <code>DECSASD</code>. <sup class="footnote-ref"><a href="#fn-3" id="fnref-3" data-footnote-ref>3</a></sup></p>
<pre lang="sh"><code># Make the status line host-writable
printf '\e[2$~'

# Send subsequent output to the status line
printf '\e[1$}Build running…'

# Return subsequent output to the main display
printf '\e[0$}'
</code></pre>
<p><code>DECSSDT</code> selects the status-display type:</p>
<pre lang="text"><code>CSI 0 $ ~   no status line
CSI 1 $ ~   indicator status line
CSI 2 $ ~   host-writable status line
</code></pre>
<p><code>DECSASD</code> selects which display receives subsequent characters:</p>
<pre lang="text"><code>CSI 0 $ }   main display
CSI 1 $ }   status line
</code></pre>
<p>It's a surprisingly elegant primitive. A program could use it for progress, mode, host name, current job, debug state, deployment environment, or a persistent “you are in production” warning, without stealing a row from the application, rewriting the prompt, or relying on a terminal multiplexer.</p>
<p><figure><img src="https://blog.vrypan.net/2026/06/13/260613-vt-host-writable-status-line/screenshot.png" alt="" /></figure></p>
<p><em>I wasn't able to find a good historical screenshot of a real application writing to the host status line, but you can see it displaying status info in this Capo's Tech video <sup class="footnote-ref"><a href="#fn-4" id="fnref-4" data-footnote-ref>4</a></sup> <sup class="footnote-ref"><a href="#fn-5" id="fnref-5" data-footnote-ref>5</a></sup></em></p>
<h2>Can we bring it back?</h2>
<p>The idea feels familiar even if these particular CSI sequences are rarely supported today. We have reinvented the same feature in tmux status lines, Zellij bars, and prompt frameworks because the shape makes sense.</p>
<p>But those are all tool-specific solutions.</p>
<p>It would be great to see modern terminals support <code>DECSSDT</code>/<code>DECSASD</code> again. I think many apps like starship, build environments, package managers and so on would take advantage of the feature if terminal emulators supported it.</p>
<hr />
<p>Image sources:</p>
<ul>
<li>Jason Scott: <a href="https://commons.wikimedia.org/w/index.php?curid=29457452">https://commons.wikimedia.org/w/index.php?curid=29457452</a></li>
<li>Tom Page: <a href="https://commons.wikimedia.org/w/index.php?curid=98843032">https://commons.wikimedia.org/w/index.php?curid=98843032</a></li>
<li>Capo’s Tech: <a href="https://youtu.be/EjJTog15Izs?si=aotLJ8Sb1QGcx2_H&amp;t=426">https://youtu.be/EjJTog15Izs?si=aotLJ8Sb1QGcx2_H&amp;t=426</a></li>
<li>LGR: <a href="https://www.youtube.com/watch?v=RuZUPpmXfT0">https://www.youtube.com/watch?v=RuZUPpmXfT0</a></li>
</ul>
<section class="footnotes" data-footnotes>
<ol>
<li id="fn-1">
<p>Ken Shirriff has a great deep dive on this: <a href="https://www.righto.com/2019/11/ibm-sonic-delay-lines-and-history-of.html">IBM, sonic delay lines, and the history of the 80×24 display</a>. <a href="#fnref-1" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="1" aria-label="Back to reference 1">↩</a></p>
</li>
<li id="fn-2">
<p>The VT320 introduced a physical 25th line reserved for status use. Normally, the main display remained the familiar 24-line terminal area, preserving compatibility with VT100/VT220-era applications. Later VT420 documentation makes the behavior explicit: when the status line is disabled, that reserved line can become an additional user-window line for normal display data. <a href="#fnref-2" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="2" aria-label="Back to reference 2">↩</a></p>
</li>
<li id="fn-3">
<p><a href="https://www.vt100.net/docs/vt320-uu/appendixe.html">VT320 Programming Summary</a> <a href="#fnref-3" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="3" aria-label="Back to reference 3">↩</a></p>
</li>
<li id="fn-4">
<p><a href="https://youtu.be/EjJTog15Izs?si=aotLJ8Sb1QGcx2_H&amp;t=426">https://youtu.be/EjJTog15Izs?si=aotLJ8Sb1QGcx2_H&amp;t=426</a> <a href="#fnref-4" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="4" aria-label="Back to reference 4">↩</a></p>
</li>
<li id="fn-5">
<p>If someone has a functioning VT320, VT330, VT340, or later DEC terminal and can capture better shots, I’d love to add them to the post. <a href="#fnref-5" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="5" aria-label="Back to reference 5">↩</a></p>
</li>
</ol>
</section>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/06/13/260613-vt-host-writable-status-line/1280px-DEC_VT100_terminal.jpg"
   type="image/jpeg"
   length="254768"/>

   <enclosure url="https://blog.vrypan.net/2026/06/13/260613-vt-host-writable-status-line/1280px-DEC_VT220_terminal.jpg"
   type="image/jpeg"
   length="384530"/>

   <enclosure url="https://blog.vrypan.net/2026/06/13/260613-vt-host-writable-status-line/og.png"
   type="image/png"
   length="103388"/>

   <enclosure url="https://blog.vrypan.net/2026/06/13/260613-vt-host-writable-status-line/screenshot.png"
   type="image/png"
   length="4566630"/>

   <enclosure url="https://blog.vrypan.net/2026/06/13/260613-vt-host-writable-status-line/vt320.png"
   type="image/png"
   length="3178813"/>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/06/11/260611-note-02559/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/11/260611-note-02559/</guid>
      <pubDate>Thu, 11 Jun 2026 21:29:19 +0300</pubDate>
      <description>I asked ChatGPT to give me Beethoven's Heroica (published 199 years ago, clearly in public domain) in ABC notation.</description>
      <content:encoded><![CDATA[
	
	<p>I asked ChatGPT to give me Beethoven's Heroica (published 199 years ago, clearly in public domain) in ABC notation.</p>
<p>This is the &quot;chilling effect&quot;, baked into a product.</p>
<p>As a side-note, if you go IMSLP, you will learn a) that the site is powered by mediawiki and CC, and b) you have to pay to download anything.</p>
<p>I hate copyrights. Copyrights limit creative people.</p>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/06/11/260611-note-02559/chatgpt-1.jpg"
   type="image/jpeg"
   length="184494"/>

   <enclosure url="https://blog.vrypan.net/2026/06/11/260611-note-02559/chatgpt-2.jpg"
   type="image/jpeg"
   length="166004"/>

    </item>
    
    <item>
      <title>Pet project: smart clock</title>
      <link>https://blog.vrypan.net/2026/06/01/pet-project-smart-clock/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/06/01/pet-project-smart-clock/</guid>
      <pubDate>Mon, 01 Jun 2026 19:43:27 +0000</pubDate>
      <description>I'm building a smart clock for my daughter. I don't want it to be a cool geek project, showing off all the things one could do with a small computer and a screen. I want it to have character and personality, with small little features built in, things that she will like.</description>
      <content:encoded><![CDATA[
	
	<p>I'm building a smart clock for my daughter. I don't want it to be a cool geek project, showing off all the things one could do with a small computer and a screen.
I want it to have character and personality, with small little features built in, things that she will like.</p>
<p>I'm using a Raspberry Pi 3B with a UnicornHD HAT. Both outdated, but fine for what I'm building, and I like the limitations they impose.</p>
<p>The clock works. The color scheme changes from bright white-ish at noon to dim blue at night. The location is determined by looking up the gateway's IP. Then, based on lat,lon and time, it calculates the position of the sun, and adjusts the color/brightness.</p>
<p>There is also an indicator on the right, showing day/night/dawn/dusk hours which also adjusts automatically.</p>
<p>It can rotate, but I'll have to connect a gyrometer for this to work automatically. Todo.</p>
<p>Works as an AirPlay receiver using SharePlay, and will display artist/title when a track starts. Will add Spotify support too.</p>
<p>I asked codex to parse all my blog posts and get quotes that I have or I could have said, and I'll display them on random times. Needs work, and more personalization, these are intended to be from me to her. Will add some internal jokes, things we say to each other, and may also make some depend on day/time/season/weather. At first it looked like a cool idea, not sure if it will make it to the final version.</p>
<p>I don't want to rely on cloud services, this has to be self-sufficient. I'm currently looking up the network gateway's IP to determine the location using ipinfo.io
but I'll find a way to either manually input it, or add a GPS module or something.</p>
<hr />
<p>While doing various experiments, I got down the rabbithole of music boxes. It would be nice if it also worked as a music box, right?</p>
<p>And I found this amazing project I had no idea about, called <a href="https://murobox.com/en/">Muro Box</a>. Wow, so cool and so beautiful.
This is the type of smart devices I would love to build some day, not consumer electronics, but things that are hand crafted, with character andattention to detail and beauty.</p>
<p><figure><img src="https://blog.vrypan.net/2026/06/01/pet-project-smart-clock/murobox.jpg" alt="Muro Box" /></figure></p>
<blockquote>
<p>Muro Box lets you compose, play, and schedule your favorite melodies. With MIDI control and a patented mechanical design, a single music box can now perform unlimited custom songs—not just short loops.</p>
</blockquote>
<hr />
<p>Anyway, with this and that, I spent the last two days building a music box player. It's a program that will read a MIDI file, and play it as if it were
a real music box, with a drum and a comb. I knew nothing about MIDI and sound engineering, but a learned quite a bit, and the result is not bad at all.
I'll put it on GitHub once it's a bit more polished.</p>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/06/01/pet-project-smart-clock/image1.jpeg"
   type="image/jpeg"
   length="88463"/>

   <enclosure url="https://blog.vrypan.net/2026/06/01/pet-project-smart-clock/image2.jpeg"
   type="image/jpeg"
   length="44496"/>

   <enclosure url="https://blog.vrypan.net/2026/06/01/pet-project-smart-clock/murobox.jpg"
   type="image/jpeg"
   length="24832"/>

    </item>
    
    <item>
      <title>note</title>
      <link>https://blog.vrypan.net/2026/05/28/260528-note-16699/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/05/28/260528-note-16699/</guid>
      <pubDate>Thu, 28 May 2026 00:18:20 +0300</pubDate>
      <description>It's been a while since I heard somene who I can relate so much with. Andrew Kelley, the creator of Zig is amazing: https://www.youtube.com/watch?v=iqddnwKF8HQ</description>
      <content:encoded><![CDATA[
	
	<p>It's been a while since I heard somene who I can relate so much with. Andrew Kelley, the creator of Zig is amazing: <a href="https://www.youtube.com/watch?v=iqddnwKF8HQ">https://www.youtube.com/watch?v=iqddnwKF8HQ</a></p>

]]></content:encoded>

    </item>
    
    <item>
      <title>note</title>
      <link>https://blog.vrypan.net/2026/05/24/260524-note-72714/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/05/24/260524-note-72714/</guid>
      <pubDate>Sun, 24 May 2026 00:45:15 +0300</pubDate>
      <description>I think AI will give birth to a new type of art/craft where objects will have attitude and personality.</description>
      <content:encoded><![CDATA[
	
	<p>I think AI will give birth to a new type of art/craft where objects will have attitude and personality.</p>

]]></content:encoded>

    </item>
    
    <item>
      <title>My Pi Zero desk clock</title>
      <link>https://blog.vrypan.net/2026/05/22/my-pi-zero-desk-clock/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/05/22/my-pi-zero-desk-clock/</guid>
      <pubDate>Fri, 22 May 2026 20:14:59 +0000</pubDate>
      <description>Years ago, I built a digital desk clock using a Pi Zero and a Micro Dot pHAT. Probably an overkill to use a whole computer as a clock, but it has turned out to be very convenient, it’s easy to read day and night, and I like the style.</description>
      <content:encoded><![CDATA[
	
        <img  src="https://blog.vrypan.net/2026/05/22/my-pi-zero-desk-clock/screenshot.png">
        
	<p>Years ago, I built a digital desk clock using a Pi Zero and a <a href="https://shop.pimoroni.com/products/microdot-phat">Micro Dot pHAT</a>. Probably an overkill to use a whole computer as a clock, but it has turned out to be very convenient, it’s easy to read day and night, and I like the style.</p>
<p>Lately, it started freezing. No idea why, my guess was the OS needed to be updated, or even better re-installed —I don’t have a clear log of what this Pi Zero has been through all these years, but for sure it was also used as a DNS (pi-hole) at some point, and as a tailscale gateway at some other.</p>
<p>The problem is my clock was a Python script using a <a href="https://github.com/pimoroni/microdot-phat">library</a> that has not been updated for at least 4 years. Would it work with a much newer version of Pi OS?</p>
<p>It did not. I could not even install it.</p>
<p>So, I cloned it, and used Claude to port it to Zig. I picked Zig because the resulting binaries are small, and I can also cross-compile on my MacBook Pro (waiting for Pi Zero to compile would not be fun). It took me an afternoon, a couple of manual interventions, and <a href="https://github.com/vrypan/microdot-phat-zig">I had my Zig port</a>. The examples compiled, and run perfectly on the latest Pi OS (32-bit).</p>
<p>And since I’m not the one writing the code, why not add some flavor? Let’s have the digits scroll up when they change.</p>
<p>Not bad for a project that took an afternoon. I love it how I can make and fix little things that wouldn’t be worth the time and effort without AI-assited coding.</p>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/05/22/my-pi-zero-desk-clock/clock.mp4"
   type="video/mp4"
   length="301552"/>

   <enclosure url="https://blog.vrypan.net/2026/05/22/my-pi-zero-desk-clock/screenshot.png"
   type="image/png"
   length="291577"/>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/05/21/260521-note-37929/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/05/21/260521-note-37929/</guid>
      <pubDate>Thu, 21 May 2026 07:32:10 +0300</pubDate>
      <description>I wrote a long piece on the history of subtitles: https://blog.vrypan.net/features/the-history-of-subtitles/</description>
      <content:encoded><![CDATA[
	
	<p>I wrote a long piece on the history of subtitles: <a href="https://blog.vrypan.net/features/the-history-of-subtitles/">https://blog.vrypan.net/features/the-history-of-subtitles/</a></p>

]]></content:encoded>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/05/16/260516-note-61697/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/05/16/260516-note-61697/</guid>
      <pubDate>Sat, 16 May 2026 23:01:37 +0300</pubDate>
      <description>QA engineers will be in high demand for the next 5 years.</description>
      <content:encoded><![CDATA[
	
	<p>QA engineers will be in high demand for the next 5 years.</p>

]]></content:encoded>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/05/16/260516-note-61609/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/05/16/260516-note-61609/</guid>
      <pubDate>Sat, 16 May 2026 23:00:10 +0300</pubDate>
      <description>Mythos and similar tools are not a threat to incumbents. They turn system attacking and hardening into a token war, and any Apple, Microsoft or Google can spend more on tokens than any attacker.</description>
      <content:encoded><![CDATA[
	
	<p>Mythos and similar tools are not a threat to incumbents. They turn system attacking and hardening into a token war, and any Apple, Microsoft or Google can spend more on tokens than any attacker.</p>

]]></content:encoded>

    </item>
    
    <item>
      <title>The Cathedral, the Bazaar and the Kitchen</title>
      <link>https://blog.vrypan.net/2026/05/11/the-cathedral-the-bazaar-and-the-kitchen/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/05/11/the-cathedral-the-bazaar-and-the-kitchen/</guid>
      <pubDate>Mon, 11 May 2026 20:36:12 +0000</pubDate>
      <description>Eric Raymond’s “The Cathedral and the Bazaar” described two fundamentally different ways of building software. The Cathedral represented centralized, carefully planned development directed by a small group of maintainers. The Bazaar represented open collaboration: large communiti...</description>
      <content:encoded><![CDATA[
	
        <img  src="https://blog.vrypan.net/2026/05/11/the-cathedral-the-bazaar-and-the-kitchen/image.png">
        
	<p>Eric Raymond’s “The Cathedral and the Bazaar”<sup class="footnote-ref"><a href="#fn-1" id="fnref-1" data-footnote-ref>1</a></sup> described two fundamentally different ways of building software. The Cathedral represented centralized, carefully planned development directed by a small group of maintainers. The Bazaar represented open collaboration: large communities, public iteration, distributed labor, and software evolving through many contributors.</p>
<p>For decades, the Bazaar became the dominant cultural myth of open source. Public repositories, pull requests, and community participation were not just practical tools, but moral ideals. Good software was expected to emerge from openness and collective effort.</p>
<hr />
<p>AI-assisted software development is changing the economics and the ergonomics behind that model. Implementation becomes cheap, and coordination becomes expensive. A single developer equipped with modern tools can now produce systems that previously required teams.</p>
<p>At the same time, software is becoming increasingly personalized: tailored to one person’s workflow, infrastructure, preferences, and habits. Instead of building generalized tools for the widest possible audience, developers increasingly build software that fits their own environment perfectly. Others may still read the code, fork it, or borrow ideas from it, but local modification often becomes cheaper than upstream coordination.</p>
<p><strong>The new model is a kitchen.</strong></p>
<p>Every kitchen evolves around the habits of its cook. Tools sit where they are convenient. Ingredients are substituted freely. Recipes are modified on instinct. Two people may start from the same dish and end up with completely different results.</p>
<p>Unlike the Bazaar, a kitchen is deeply personal. Recipes are shared freely, but kitchens rarely converge into a universal standard. Visitors may admire another cook’s techniques, yet still return home and prepare the dish their own way. In the Kitchen model, open source becomes less like public infrastructure and more like published craft: software as personal utility, openly visible, endlessly adaptable, and increasingly authored by individuals rather than communities.</p>
<hr />
<p>In the Bazaar model, openness was mainly a way to coordinate people. You opened the codebase to attract contributors, spread work across many developers, avoid duplicated effort, and slowly build shared infrastructure.</p>
<p><strong>In the Kitchen model, openness serves a different purpose.</strong> It provides visibility, learnability, and independence. Value shifts from <em>“others can help build this”</em> to <em>“others can understand, adapt, and reclaim this.”</em></p>
<p>Source code starts to resemble recipes more than public construction projects. Most people do not submit patches to a cookbook, yet recipes remain enormously valuable because they transfer techniques, preserve knowledge, and provide foundations others can adapt to their own tastes and environments. The code is open not necessarily so everyone can co-author it, but so anyone can study it, modify it, and make it their own.</p>
<p>This also changes the meaning of forks. In the Bazaar, forks were often viewed as failures of governance or coordination. In the Kitchen, forks become normal and healthy:</p>
<ul>
<li>“I adapted this for my setup”</li>
<li>“I removed features I don’t need”</li>
<li>“I rewrote this around my workflow”</li>
</ul>
<p>Forking becomes analogous to modifying a recipe<sup class="footnote-ref"><a href="#fn-2" id="fnref-2" data-footnote-ref>2</a></sup> at home.</p>
<p>Software evolves through local adaptation rather than centralized consensus. A developer will remove features, rewrite workflows, or optimize entirely around their own infrastructure because doing so is now cheaper than negotiating a generalized solution acceptable to everyone.</p>
<p>This model still depends on public circulation of ideas. Even if code contributions decline, people still copy ideas from each other constantly through imitation, recombination, critique, and inspiration.</p>
<p>Much of programming history already worked this way:</p>
<ul>
<li>Unix customization culture where ingredients were expected to be mixed in different ways</li>
<li>shell workflows and personal scripts</li>
<li>dotfiles designed to show others how a system is configured but rarely adopted one-to-one</li>
</ul>
<p>These are often highly personal systems shared publicly, not collaboratively engineered products.</p>
<p><strong>Open source remains essential because it preserves agency</strong>: the ability to inspect, repair, continue, and reshape software independently of its original author. The result is a world where software is increasingly personal, but where ideas, techniques, and tools still circulate freely between individuals, much like recipes passed from kitchen to kitchen.</p>
<hr />
<p><strong>Update (15-May-2026)</strong>: Other people have described the same or a similar mental model:</p>
<p>Drew Breunig calls it &quot;the Winchester Mystery House&quot;:</p>
<blockquote>
<p>The ideas crystallized in &quot;The Cathedral and the Bazaar&quot; helped kick off a quarter-century of open source innovation and dominance.<br />
But just as the internet made communication cheap and birthed the Bazaar, AI is making code cheap and kicking off a new era filled with idiosyncratic, sprawling, cobbled-together software.<br />
Meet the third model: the Winchester Mystery House.</p>
<p>—<em>The Cathedral, the Bazaar, and the Winchester Mystery House</em><sup class="footnote-ref"><a href="#fn-3" id="fnref-3" data-footnote-ref>3</a></sup></p>
</blockquote>
<p>And Thomas H. Ptacek, calls it &quot;the Emacsification of Software&quot;:</p>
<blockquote>
<p>First, it's personal software. Most of it will be useful only to its creator, and then forgotten,
just like the dozens of obsolete little elisp programs littering my .emacs. Personal software defines
the ethos of Emacs, which was carefully designed over decades to nurture these kinds of tools.
&quot;Emacsification&quot; clocks that everything now works this way, not just baroque text editors.</p>
<p>—<em>The Emacsification of Software</em><sup class="footnote-ref"><a href="#fn-4" id="fnref-4" data-footnote-ref>4</a></sup></p>
</blockquote>
<section class="footnotes" data-footnotes>
<ol>
<li id="fn-1">
<p>Eric S. Raymond, <em>The Cathedral and the Bazaar</em> (1997), <a href="https://en.wikipedia.org/wiki/The_Cathedral_and_the_Bazaar">https://en.wikipedia.org/wiki/The_Cathedral_and_the_Bazaar</a> <a href="#fnref-1" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="1" aria-label="Back to reference 1">↩</a></p>
</li>
<li id="fn-2">
<p>Side note: There is rarely a single canonical version of a dish. The same recipe evolves into countless variations shaped by region, available ingredients, habits, and personal taste. Instead of converging into one standard, we distinguish them by origin or authorship: <em>à la provençale</em>, <em>à la milanaise</em>, <em>à la grandma</em>, <em>à la Jacques Pépin</em>. The variation itself becomes part of the identity of the dish. GNU grep and BSD grep were different variations of the same tool, but these were more like publisher or distribution variations. The Kitchen model pushes personalization much further, toward software shaped directly around the habits and preferences of individual developers. <a href="#fnref-2" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="2" aria-label="Back to reference 2">↩</a></p>
</li>
<li id="fn-3">
<p><a href="https://www.dbreunig.com/2026/03/26/winchester-mystery-house.html">The Cathedral, the Bazaar, and the Winchester Mystery House</a> <a href="#fnref-3" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="3" aria-label="Back to reference 3">↩</a></p>
</li>
<li id="fn-4">
<p><a href="https://sockpuppet.org/blog/2026/05/12/emacsification/">The Emacsification of Software</a> <a href="#fnref-4" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="4" aria-label="Back to reference 4">↩</a></p>
</li>
</ol>
</section>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/05/11/the-cathedral-the-bazaar-and-the-kitchen/image.png"
   type="image/png"
   length="3511829"/>

    </item>
    
    <item>
      <title>shg, the SHell Guard</title>
      <link>https://blog.vrypan.net/2026/05/09/shg-the-shell-guard/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/05/09/shg-the-shell-guard/</guid>
      <pubDate>Sat, 09 May 2026 09:38:07 +0000</pubDate>
      <description>If you spend as much time as I do in the terminal, you probably depend on your shell history too. Being able to repeat a complex command that you remember you typed a month ago, is extremely valuable —no wonder why I’ve set HISTSIZE=10000.</description>
      <content:encoded><![CDATA[
	
        <img  src="https://blog.vrypan.net/2026/05/09/shg-the-shell-guard/screenshot.png">
        
	<p>If you spend as much time as I do in the terminal, you probably depend on your shell history too.
Being able to repeat a complex command that you remember you typed a month ago, is extremely
valuable —no wonder why I’ve set <code>HISTSIZE=10000</code>.</p>
<p>While it is convenient to use your shell history as part of your long term memory, the downside is things
that shouldn’t be there, end up there: A command you prepended with <code>API_KEY=...</code>, a quick and dirty
<code>curl --user &quot;name:password&quot;</code>, a <code>PGPASSWORD=... psql</code>, an <code>ALTER USER USER() IDENTIFIED BY ...</code>,
and so on. And they are all probably saved in shell or app history file.</p>
<p><strong>This is not good, obviously.</strong> Credentials should not be saved in places where a malicious
program can just read them, and they should not be preserved in backups.</p>
<p>That’s why I built <a href="https://github.com/vrypan/shg"><code>shg</code> (short for <em>SHell Guard</em>)</a>, a small program
that scans my shell history (and other history files) for entries that look like API keys, passwords,
bearer tokens, credential URLs, and private keys and alerts me.</p>
<p>I’ve set it up to do <a href="https://github.com/vrypan/shg/blob/main/INTEGRATIONS.md#shg-integrations">a quick scan when I start a new shell</a>
and (I think this is the coolest part) also check every command I enter in zsh and
<a href="https://github.com/vrypan/shg/blob/main/INTEGRATIONS.md#zsh-intercept-history-before-it-is-saved">catch secrets before they ever land in <code>~/.zsh_history</code></a>.</p>
<hr />
<p>Some tips:</p>
<ul>
<li>run <code>shg-config discover</code> to find and configure history files that are not part of the default scan list.</li>
<li>you can adjust the output and the severity level using <code>--level</code>, <code>--one-line</code>, <code>--json</code> and other options. Check <code>shg scan --help</code> for more details.</li>
<li>If you keep getting reports for entries that you don't mind, add them to <code>~/.config/shg/ignore.local.shg</code>. (See <a href="https://github.com/vrypan/shg/tree/main/src/defaults">the default ignore file</a> here).</li>
</ul>
<hr />
<p>If you use <code>shg</code> and you like it, <a href="https://github.com/vrypan/shg">give it a star on GitHub</a>! 🙏</p>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/05/09/shg-the-shell-guard/screenshot.png"
   type="image/png"
   length="143787"/>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/05/01/260501-note-65756/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/05/01/260501-note-65756/</guid>
      <pubDate>Fri, 01 May 2026 23:02:36 +0300</pubDate>
      <description>letter to the editor, 1995</description>
      <content:encoded><![CDATA[
	
	<p>letter to the editor, 1995</p>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/05/01/260501-note-65756/letter.png"
   type="image/png"
   length="2988141"/>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/05/01/260501-note-57052/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/05/01/260501-note-57052/</guid>
      <pubDate>Fri, 01 May 2026 20:37:33 +0300</pubDate>
      <description>"Satoshi's coins" is the canary in the coal mine of quantum computing.</description>
      <content:encoded><![CDATA[
	
	<p>&quot;Satoshi's coins&quot; is the canary in the coal mine of quantum computing.</p>

]]></content:encoded>

    </item>
    
    <item>
      <title>A library of agent-friendly specs</title>
      <link>https://blog.vrypan.net/2026/04/30/a-library-of-agent-friendly-specs/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/04/30/a-library-of-agent-friendly-specs/</guid>
      <pubDate>Thu, 30 Apr 2026 17:15:06 +0000</pubDate>
      <description>Recently, I've been trying to shape the cli arguments of a tool I'm building in Zig to fit zli and zig-cli.</description>
      <content:encoded><![CDATA[
	
        <img  src="https://blog.vrypan.net/2026/04/30/a-library-of-agent-friendly-specs/screenshot.png">
        
	<p>Recently, I've been trying to shape the cli arguments of a tool I'm building in Zig to fit <code>zli</code> and <code>zig-cli</code>.</p>
<p>It's not the first time. <code>cobra</code> has its own opinions in Go, <code>clap</code> in Rust, <code>docopt</code> in Python, and so on.</p>
<p>One library doesn't support repeated arguments.<br />
Another doesn't allow ordering the args alphabetically in help.<br />
This one doesn't support arguments with optional values.<br />
That one has a small UI bug.<br />
Another won't fold long descriptions.</p>
<p>I kept adding small hacks to bring the implementation closer to what I had in mind, but a) it was
not 100% there, and b) it felt like a hack.</p>
<p>Writing a cli parser is not rocket science. A coding agent like codex or claude can definitely do it,
as long as you provide it with clear specs. Which is what I did, since I already had a lot of boilerplate
code.</p>
<p>Twenty minutes later, everything works exactly the way I want it to work, and now I can tweak it
at will, and re-implement it in the next project, regardless of the language used.</p>
<hr />
<h2>A library of agent specs</h2>
<p>Instead of rediscovering the requirements every time, I put them down in a form that AI agents can use.</p>
<p>That's the idea behind <strong><a href="https://github.com/vrypan/agent-specs">github.com/vrypan/agent-specs</a></strong>. It will
probably become a small collection of specifications designed to be used by agents.</p>
<p>One can think of it as a higher-level code library. Instead of the language-level
import/include/use, you have a spec for an agent.</p>
<pre><code>&gt; Implement cli arguments and options according to specs/cli/CLI.md.
</code></pre>
<p>Each section of <a href="https://github.com/vrypan/agent-specs/blob/main/specs/cli/CLI.md">CLI.md</a>
has an identifier, like <code>CMD-SUBCOMMANDS</code>, <code>FLAGS-SHORT-VALUES</code> and <code>OPT-COLOR</code>.
This makes it easy to tell the agent, use this, don't implement this, override that. There are
also test cases, to help the agent test the implementation.</p>
<p>So, you could also ask something like this</p>
<pre><code>&gt; Use specs/cli/CLI.md as the CLI parser specification.
Implement all required sections.
Do not implement OPT-COLOR.
Translate specs/cli/tests.yaml into automated tests.
Report any deviations.
</code></pre>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/04/30/a-library-of-agent-friendly-specs/screenshot.png"
   type="image/png"
   length="82419"/>

    </item>
    
    <item>
      <title>LLM-assisted coding is not deterministic. It does it matter?</title>
      <link>https://blog.vrypan.net/2026/04/23/llm-assisted-coding-is-not-deterministic-does-it-matter/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/04/23/llm-assisted-coding-is-not-deterministic-does-it-matter/</guid>
      <pubDate>Thu, 23 Apr 2026 17:12:03 +0000</pubDate>
      <description>We often treat determinism and predictability as synonyms, but they are not the same.</description>
      <content:encoded><![CDATA[
	
        <img  src="https://blog.vrypan.net/2026/04/23/llm-assisted-coding-is-not-deterministic-does-it-matter/not-random.png">
        
	<p>We often treat determinism and predictability as synonyms, but they are not the same.</p>
<p>A system is deterministic if the same starting conditions always lead to the same result. A system is predictable if we can actually foresee that result with the tools, time, and knowledge we have.</p>
<p>Determinism is a system characteristic.</p>
<p>Predictability, on the other hand, often depends on our capabilities, and it usually exists on a spectrum. Weather is a good example. The laws of physics governing the atmosphere have not changed, and they are deterministic. Yet our ability to predict the weather has improved over decades simply because our measurements, models, and computing power improved.</p>
<p>But it's not always on us. Stephen Wolfram has described the concept of <strong>computational irreducibility</strong> <sup class="footnote-ref"><a href="#fn-1" id="fnref-1" data-footnote-ref>1</a></sup>. A system is computationally irreducible if the only way to know its future state is to simulate every step. There are also <strong>chaotic systems</strong> where tiny measurement errors grow rapidly, making them practically unpredictable.</p>
<p>Some systems are both deterministic and predictable, like planetary motion over short time scales. Others are deterministic but not predictably so in practice, like weather or turbulent flows. Conversely, some systems are not deterministic at the level of individual events but are still predictable statistically, such as casino games or population averages.</p>
<table>
<thead>
<tr>
<th>System Type</th>
<th>Deterministic</th>
<th>Predictable</th>
</tr>
</thead>
<tbody>
<tr>
<td>Planetary orbits (over finite time horizons)</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Weather</td>
<td>Yes</td>
<td>Limited</td>
</tr>
<tr>
<td>Dice roll (unknown forces)</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Radioactive decay (single event)</td>
<td>No</td>
<td>No</td>
</tr>
<tr>
<td>Casino odds (many trials)</td>
<td>No</td>
<td>Yes</td>
</tr>
</tbody>
</table>
<p><strong>In essence: determinism does not guarantee predictability, and predictability does not require determinism.</strong></p>
<hr />
<p>Back to coding…</p>
<p>From the perspective of someone who needs a piece of software, development has never been deterministic. When you ask a software developer to build something, you cannot predict exactly what code they will write, how long it will take (so many jokes about this…), or which edge cases will fail first. The same is true when you ask an AI agent. Both are problem-solving processes operating under uncertainty.</p>
<p>One can argue in favor or against the competency of humans or LLMs when it comes to coding, but determinism has never been a human trait.</p>
<p>In most cases, developers build software to satisfy other people’s needs, and what these people really care about is <strong>whether the resulting code is predictable enough to rely on</strong>: whether the system behaves correctly most of the time, whether failures are visible, and whether they can be fixed quickly.</p>
<p>This distinction is important. From a software developer's pov, asking an LLM to &quot;build a program that sorts 1000 numbers&quot; may not have a predictable result (code). But the end user only cares if the resulting program will always sort any 1000 numbers correctly.</p>
<p>And then there is the environment. Modern software runs on stacks that are far more complex than any single developer can fully reason about: hardware, kernels, drivers, libraries, network conditions, configuration files, container layers, dependency versions. So, even if, as a developer, you write the code to do excatly what you want it to do, in a totally predictable way, running the code may yield less predictable results. <strong>While deterministic, the system as a whole is so complex that its behavior cannot be predicted perfectly in advance.</strong></p>
<p>We have quietly accepted that bugs are a natural part of software development <sup class="footnote-ref"><a href="#fn-2" id="fnref-2" data-footnote-ref>2</a></sup> (i.e. there will be cases where it behaves unpredictably), because we recognize the complexity of the endeavor. Instead of expecting perfect foresight, the industry built practices around uncertainty: tests, staging environments, observability, rollbacks, reproducible builds.</p>
<table>
<thead>
<tr>
<th>Producer / System Component</th>
<th>Deterministic</th>
<th>Predictable (for the requester)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Human developer</td>
<td>No</td>
<td>Usually, within experience and process constraints <sup class="footnote-ref"><a href="#fn-3" id="fnref-3" data-footnote-ref>3</a></sup></td>
</tr>
<tr>
<td>AI coding agent</td>
<td>No</td>
<td>Increasing, with tooling and validation loops</td>
</tr>
<tr>
<td>Compiler / build system</td>
<td>Yes</td>
<td>Yes<sup class="footnote-ref"><a href="#fn-4" id="fnref-4" data-footnote-ref>4</a></sup></td>
</tr>
<tr>
<td>Tested deployment pipeline</td>
<td>Yes</td>
<td>Yes</td>
</tr>
</tbody>
</table>
<p>So, the meaningful question is not about determinism. <strong>The meaningful question is which workflow produces more predictable outcomes under real conditions, and which human or AI is a better fit at each case/stage.</strong> <sup class="footnote-ref"><a href="#fn-5" id="fnref-5" data-footnote-ref>5</a></sup></p>
<hr />
<p>Some final thoughts.</p>
<p>It’s worth looking at how we build some of the most safety-critical software. DO-178C is the “Software Considerations in Airborne Systems and Equipment Certification”. It is a key document in the aeronautic industry, providing guidelines for the development of safety-critical airborne software.</p>
<blockquote>
<p>The approach of DO-178C is based on the formulation of appropriate objectives and on the verification that these objectives are achieved. The DO-178C authors acknowledged that objectives are more essential and stable than specific procedures. The ways of achieving an objective may vary between companies, and they may vary over time with the evolution of methods, techniques, and tools. DO-178C never states that one should use design method X, coding rules Y, or tool Z. DO-178C does not even impose a specific life cycle. <sup class="footnote-ref"><a href="#fn-6" id="fnref-6" data-footnote-ref>6</a></sup></p>
</blockquote>
<p>DO-178C is <strong>objective-oriented</strong>: the focus is on formulating objectives and verification that the objectives are achieved, a framework that could work both for human coders and LLMs.</p>
<section class="footnotes" data-footnotes>
<ol>
<li id="fn-1">
<p><a href="https://en.wikipedia.org/wiki/Computational_irreducibility">Computational Irreducibility (Wikipedia)</a>. The image, <a href="https://turmites.art">Not Random, Blue</a>, is inspired by the concept. <a href="#fnref-1" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="1" aria-label="Back to reference 1">↩</a></p>
</li>
<li id="fn-2">
<p><a href="https://world.hey.com/dhh/software-has-bugs-this-is-normal-26d5fd06">Software has bugs. This is normal.</a> <a href="#fnref-2" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="2" aria-label="Back to reference 2">↩</a></p>
</li>
<li id="fn-3">
<p>also depending on mood, health, work relationships and other human factors. <a href="#fnref-3" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="3" aria-label="Back to reference 3">↩</a></p>
</li>
<li id="fn-4">
<p>Even leaving bugs aside, there is a big discussion of which aspect of a compiler is predictable. Modern compilers tend to generate <strong>binaries that behave predictably</strong>, but are implemented in ways unexpected by the vast majority of developers. For example <a href="https://xania.org/202512/24-cunning-clang">this article</a> describes a case where the compiler decides to replace an O(n) algorithm written by the developer, with an O(1) one! <a href="#fnref-4" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="4" aria-label="Back to reference 4">↩</a></p>
</li>
<li id="fn-5">
<p>Side thought: If someone <strong>proved</strong> (not observed) that humans or LLMs can generate predictable results in some non-trivial cases, that would be really interesting. Tbh, if I had to bet that such a proof exists, I'd put my money on finding one for LLMs. <a href="#fnref-5" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="5" aria-label="Back to reference 5">↩</a></p>
</li>
<li id="fn-6">
<p><a href="https://innovationspace.ansys.com/knowledge/forums/topic/an-introduction-to-do-178c/">An introduction to DO-178C</a> <a href="#fnref-6" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="6" aria-label="Back to reference 6">↩</a></p>
</li>
</ol>
</section>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/04/23/llm-assisted-coding-is-not-deterministic-does-it-matter/not-random.png"
   type="image/png"
   length="38099"/>

    </item>
    
    <item>
      
      <link>https://blog.vrypan.net/2026/01/04/fc-2026-01-04-0xd3e5e531/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/01/04/fc-2026-01-04-0xd3e5e531/</guid>
      <pubDate>Sun, 04 Jan 2026 16:11:22 +0000</pubDate>
      <description>I was trying to build a PRNG based on cellular automata rules. I had the illusion that a few clever ideas, and some empirical knowledge would be enough.</description>
      <content:encoded><![CDATA[
	
	<p>I was trying to build a PRNG based on cellular automata rules. I had the illusion that a few clever ideas, and some empirical knowledge would be enough.</p>
<p>Nothing could be further than this, when it comes to random number generators. Smart people with deep and broad knowledge have been studying the field for centuries, you can't just show up and expect you will build something better, or even decently good, just like this.</p>
<p>But during the last 10 days that I've (obsessively) been on this journey I've learned so much. (ChatGPT and Claude have allowed me to learn and experiment at a pace that would be unimaginable a year ago.)</p>
<p>Yesterday I started reading again (last time I tried, I stopped after the second chapter or so) Wolfram's &quot;New Kind of Science&quot;. It will take me some time, but there are so many concepts and ideas I've long been fascinated about, but never went to the depths Wolfram goes.</p>
<p>On the side, I'm studying a 1984 paper, on &quot;Algebraic Properties of Cellular Automata&quot; now. Algebras were one of my favorite topics when I was studying math, and I feel lucky to have the tools, to (try to) understand it: <a href="https://content.wolfram.com/sw-publications/2020/07/algebraic-properties-cellular-automata.pdf">https://content.wolfram.com/sw-publications/2020/07/algebraic-properties-cellular-automata.pdf</a></p>
<p>(It's sad that so few people get to learn what Mathematics really are: a tool to describe and study concepts that often push the boundaries of our minds.)</p>

]]></content:encoded>

    </item>
    
    <item>
      <title>Down the RNG rabbit hole - Part II</title>
      <link>https://blog.vrypan.net/2026/01/01/down-the-rng-rabbit-hole-part-ii/</link>
      <guid isPermaLink="true">https://blog.vrypan.net/2026/01/01/down-the-rng-rabbit-hole-part-ii/</guid>
      <pubDate>Thu, 01 Jan 2026 20:46:44 +0000</pubDate>
      <description>So, Rule30RND was ok as a fun experiment, but not a PRNG that someone could take seriously.</description>
      <content:encoded><![CDATA[
	
        <img  src="https://blog.vrypan.net/2026/01/01/down-the-rng-rabbit-hole-part-ii/r30r2.png">
        
	<p>So, <a href="/2025/12/30/down-the-rng-rabbit-hole/">Rule30RND was ok as a fun experiment</a>, but not a PRNG that someone could take seriously.</p>
<p>I kept tinkering, as if I was playing a game: change this and that, run the tests, check my score.</p>
<p><em>Side note: after I published the last post, I realized that my test scripts had fundamental flaws, like adding text headers in the randomly generated bytes.</em></p>
<h2>Radius-2</h2>
<p>No matter how hard I tried, it was obvious that a Rule 30-based algorithm would not pass BigCrush unless I changed it so much that would become something else with a sprinkle of CA.</p>
<p>Now, Rule 30 works like this: If <code>C</code> is our bit, <code>L</code> is the one on its left and <code>R</code> the one on its right:<br />
<code>new_bit = L XOR (C OR R)</code></p>
<p>That’s a &quot;radius-1 CA&quot;. What if I tried with a radius-2 CA, that uses two bits on the left and two on the right?</p>
<p>Unfortunately, there is much less literature for radius-2 than radius-1 CAs. After some search that did not return a documented good candidate for what I wanted to build, and a few failed experiments, I decided to extend Rule 30 in a “symmetric” way.</p>
<p><code>new_bit = (L2 XOR L1) XOR (C OR (R1 OR R2))</code></p>
<p>This gave significantly better results.</p>
<p>Not exceptional. The new algorithm failed 5/15 SmallCrush tests (Gap, SimpPoker, CouponCollector, WeightDistrib, HammingIndep).</p>
<p>But these are the tests I might be able to fix with a diffusion function.</p>
<h2>Searching for a diffusion fucntion</h2>
<p>I wanted a diffusion function, so I let Claude test 7 different functions, run the tests, collect the results, and let me know when it was done.</p>
<table>
<thead>
<tr>
<th>Mixing/Diffusion Approach</th>
<th>SmallCrush</th>
<th>Performance vs math/rand</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>Rotation (5,14,27)</td>
<td>12/15</td>
<td>1.08×</td>
<td>❌</td>
</tr>
<tr>
<td>5 XOR Rotations</td>
<td>13/15</td>
<td>0.94×</td>
<td>❌</td>
</tr>
<tr>
<td>xoshiro256++ Style</td>
<td>11/15</td>
<td>1.13×</td>
<td>❌</td>
</tr>
<tr>
<td>MurmurHash</td>
<td><strong>15/15</strong></td>
<td>0.95×</td>
<td>✅</td>
</tr>
<tr>
<td>SplitMix64</td>
<td><strong>15/15</strong></td>
<td>0.93×</td>
<td>✅</td>
</tr>
<tr>
<td><strong>Hybrid</strong></td>
<td><strong>15/15</strong></td>
<td><strong>1.03×</strong></td>
<td>✅</td>
</tr>
<tr>
<td>Per-Word Varying</td>
<td><strong>15/15</strong></td>
<td>0.90×</td>
<td>✅</td>
</tr>
</tbody>
</table>
<p>The winner was the “Hybrid” approach that combines rotation, multiplication by the golden ratio, and shift-XOR:</p>
<pre lang="golang"><code>func mix(x uint64) uint64 {
    x ^= bits.RotateLeft64(x, 13)
    x *= 0x9e3779b97f4a7c15  // Golden ratio constant
    x ^= x &gt;&gt; 27
    return x
}
</code></pre>
<p>SmallCrush is the first step, but I needed it to pass BigCrush.</p>
<p>And, YES! <strong>My rule-30-radius-2-diffused algo scored 160/160 on BigCrush</strong> for the first time!</p>
<h2>Staying true</h2>
<p>Now, this worked, but <strong>it was also cheating</strong>: I applied <code>mix()</code> to every state evolution: <code>s1, s2=mix(rule30(s1)), s3=mix(rule30(s2)), ...</code>, so this was no longer a CA algorithm.</p>
<p>What if I applied <code>mix()</code> only to the output? I.e. keep a pure Rule 30, radius-2 algorithm that generates states <code>s1, s2, ...</code>, but return <code>mix(s1), mix(s2), ...</code>. Would this work?</p>
<p><strong>IT WORKED!</strong> <strong>IT WORKED!</strong></p>
<p>And it was also <strong>6% faster</strong>.</p>
<h2>I present you “R30R2”</h2>
<p>Along the way, I fixed some annoying Makefile issues, optimized how <code>Read()</code> reads data from the state, and tweaked a few things here and there.</p>
<p>I was ready to release it, but when “Rule 30” is mentioned in literature, it means a radius-1 CA.</p>
<p>I decided to rename the project, the documentation, the function names, etc. And it's name is... <strong>R30R2</strong>.</p>
<blockquote>
<p><strong>R30R2 is a radius-2 Rule 30-based PRNG with a diffusion function applied on output. It scores 160/160 on BigCrush, and is 2× faster than <code>math/rand/v2</code>.</strong></p>
<p>You can find it at <a href="https://github.com/vrypan/r30r2/">https://github.com/vrypan/r30r2/</a></p>
</blockquote>
<table>
<thead>
<tr>
<th>RNG</th>
<th>BigCrush Score</th>
<th>Performance vs math/rand</th>
</tr>
</thead>
<tbody>
<tr>
<td>math/rand</td>
<td>159/160</td>
<td>1.00× (baseline)</td>
</tr>
<tr>
<td>math/rand/v2</td>
<td>160/160 ✓</td>
<td>0.56×</td>
</tr>
<tr>
<td><strong>Rule 30</strong></td>
<td><strong>160/160</strong> ✓</td>
<td><strong>1.03×</strong></td>
</tr>
</tbody>
</table>
<h2>🎉🍾🍻</h2>

]]></content:encoded>

   <enclosure url="https://blog.vrypan.net/2026/01/01/down-the-rng-rabbit-hole-part-ii/r30r2.png"
   type="image/png"
   length="190176"/>

    </item>
    
  </channel>
</rss>
