{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Posts",
  "home_page_url": "https://dbuho.me/",
  "feed_url": "https://dbuho.me/posts.json",
  "description": "Articles and notes on software engineering, DevOps, and open-source.",
  "authors": [
    {
      "name": "Damián Búho"
    }
  ],
  "items": [
    {
      "id": "01M71WTZ45Y79APJ0TDDC4MKTZ",
      "url": "https://dbuho.me/post/cli-checklist/?utm_source=posts&utm_medium=rss",
      "title": "CLI Checklist",
      "content_html": "<p>Sharing here my checklist that I use for CLI apps (was assembled manually and suffered by real usage).\nYou can feed it to your agent (it will probably forget half of the rules, so use multiple passes or split it).</p>\n<p>LLM was used only to fix ESL mistakes.</p>\n<p>154 rules.</p>\n<p>The key words MUST, MUST NOT, SHOULD and MAY are to be read as described in RFC 2119 and RFC 8174. A rule about a feature binds only a tool that has that feature: a tool without remote accounts needs no <code>login</code>.</p>\n<h2>Discovery and help</h2>\n<ul>\n<li>MUST have <code>--help</code> and <code>-h</code> on the top level and on every subcommand</li>\n<li>MUST NOT overload <code>-h</code> with anything but help</li>\n<li>MUST accept <code>--help</code> appended to any invocation, ignoring the rest of the arguments</li>\n<li>MUST show a concise summary when invoked with no arguments, unless the tool reads stdin by default (<code>cat</code>, <code>jq</code>)</li>\n<li>MUST keep the concise help to a one-line description of what the tool does, one or two example invocations, the most common flags, and a pointer to <code>--help</code> for the rest</li>\n<li>MUST open the help text with the one-line description, then examples (with the real output when it clarifies), then the most common flags and commands</li>\n<li>MUST group commands logically, each group in alphabetical order</li>\n<li>MUST link to the web version of the documentation from the top-level help text, and link the exact page or anchor per subcommand</li>\n<li>MUST link to the issue tracker or website so feedback and bug reports have a path</li>\n<li>MUST NOT check for updates implicitly on a normal run, and MUST NOT block on it</li>\n<li>MUST have a <code>check-updates</code> command the user invokes explicitly, reporting the current version against the latest released one, unless a package manager owns the tool’s updates</li>\n<li>SHOULD also expose help as <code>myapp help</code> and <code>myapp help &lt;subcommand&gt;</code></li>\n<li>SHOULD provide tab completion for bash and zsh, documented in a format the shell understands</li>\n<li>SHOULD keep exhaustive or advanced examples in a dedicated cheat-sheet command or web page instead of the help text</li>\n</ul>\n<h2>Output</h2>\n<ul>\n<li>MUST send primary and machine-readable output to stdout</li>\n<li>MUST send logs, progress and errors to stderr</li>\n<li>MUST return zero on success and non-zero on failure</li>\n<li>MUST keep human-readable output paramount: humans first, machines second</li>\n<li>MUST print output on success, but keep it brief</li>\n<li>MUST tell the user when a command changed state, and show the current state</li>\n<li>MUST NOT print developer-only information by default: it belongs behind verbose mode</li>\n<li>MUST NOT use stderr as a log file: level labels (<code>ERR</code>, <code>WARN</code>) and extra context appear only in verbose mode</li>\n<li>MUST have mutually exclusive <code>--verbose</code> and <code>--quiet</code> flags</li>\n<li>MUST make <code>-q</code> suppress all non-essential output, so scripts need no <code>2&gt;/dev/null</code></li>\n<li>MUST offer <code>--plain</code> (one record per line, no wrapping or decoration: banners, ASCII art, table borders, divider rows, <code>[+]</code>/<code>[-]</code> status prefixes) whenever human-readable formatting breaks machine parsing or a screen reader</li>\n<li>MUST display indented JSON when <code>--json</code> is passed, or one JSON object per line when the output is a stream of records</li>\n<li>MUST NOT pack several records or sentences onto one long line</li>\n<li>MUST make a TTY check per stream: colour and human decoration on stdout may go when piped while stderr keeps them</li>\n<li>MUST respect <code>NO_COLOR</code>, <code>FORCE_COLOR</code>, <code>TERM=dumb</code> and <code>--color=auto|always|never</code>, and provide a <code>&lt;TOOL&gt;_NO_COLOR</code> override</li>\n<li>MUST NOT carry meaning in colour alone, and MUST NOT impose a custom colour scheme over the terminal’s own palette</li>\n<li>MUST have progress bars for long/network operations, with one bar per parallel task when possible</li>\n<li>MUST NOT emit animations or progress bars on a stream that is not a TTY</li>\n<li>MUST print the logs behind a progress bar when the run fails</li>\n<li>MUST report the time spent and the external requests made (HTTP and others) at the end of a long or network operation</li>\n<li>SHOULD keep the default output short enough to listen to through a screen reader, with filler behind <code>--verbose</code></li>\n<li>SHOULD use colour with intention and sparingly, since colour everywhere means colour nowhere</li>\n<li>SHOULD suggest the next command to run when several commands form a workflow</li>\n<li>SHOULD increase information density so a glance suffices, growing into detail on study</li>\n<li>SHOULD offer a flag that prints only hits when a run reports one line per checked item</li>\n<li>SHOULD page long output through <code>$PAGER</code>, defaulting to <code>less -FIRX</code>, only when stdin and stdout are both TTYs</li>\n<li>SHOULD parallelise work where it helps, keeping concurrent output from interleaving confusingly</li>\n<li>SHOULD show an estimated time remaining or at least an animated component on long operations</li>\n<li>SHOULD use symbols where they clarify, without turning the output into a toy</li>\n</ul>\n<h2>Errors</h2>\n<ul>\n<li>MUST rewrite expected errors for humans, in plain language, with the exact fix (<code>Can’t write to file.txt. Run chmod +w file.txt.</code>)</li>\n<li>MUST NOT print scary stack traces for expected failures</li>\n<li>MUST give debug output, a traceback and bug-report instructions on an unexpected error, preferably writing the detail to a file rather than the terminal</li>\n<li>MUST use a distinct exit code for each important failure mode: checking an exit code is always easier than parsing output</li>\n<li>MUST put the most important information last, where the eye lands</li>\n<li>MUST group repeated errors of the same kind under one explanatory header instead of printing many similar lines</li>\n<li>MUST NOT silently rewrite the user’s input: when it accepts an alternative syntax, it says so and documents both</li>\n<li>SHOULD suggest the correction when user input is wrong and the intent is guessable</li>\n<li>SHOULD ask before running the suggestion rather than forcing it</li>\n<li>SHOULD make bug reports effortless by pre-filling as much context as possible into the issue URL</li>\n</ul>\n<h2>Arguments and flags</h2>\n<ul>\n<li>MUST use an argument-parsing library, the language built-in one or a good third-party one, instead of hand-rolling the parser</li>\n<li>MUST validate every input early and bail out before anything bad happens</li>\n<li>MUST give every flag a full-length form, and reserve single-letter flags for the common ones so the short namespace stays free</li>\n<li>MUST use the conventional names for the well-known flags a tool has: <code>-a/--all</code>, <code>-d/--debug</code>, <code>-f/--force</code>, <code>-h/--help</code>, <code>-n/--dry-run</code>, <code>-o/--output</code>, <code>-p/--port</code>, <code>-q/--quiet</code>, <code>-u/--user</code>, <code>-V/--version</code></li>\n<li>MUST support a <code>--version</code> flag, and MUST NOT let <code>-v</code> mean both verbose and version</li>\n<li>MUST allow <code>-</code> to read from stdin or write to stdout when the input or output is a file</li>\n<li>SHOULD prefer flags to positional args: they read clearly and leave room to add input later</li>\n<li>SHOULD make arguments, flags and subcommands order-independent, so appending a flag to a recalled line still works</li>\n<li>SHOULD keep multiple positional args to one meaning, e.g. a list of same-typed files such as <code>rm *.txt</code>; the exception is a memorable primary action like <code>cp &lt;source&gt; &lt;destination&gt;</code></li>\n<li>SHOULD prompt for a missing argument when interactive, and never make the prompt mandatory</li>\n<li>SHOULD accept an explicit <code>none</code> when a flag takes an optional value, rather than a blank value</li>\n</ul>\n<h2>Interactivity and destructive actions</h2>\n<ul>\n<li>MUST let the user escape: Ctrl-C always works, and a wrapper around a program that swallows Ctrl-C says how to escape</li>\n<li>MUST only prompt when stdin is a TTY</li>\n<li>MUST have <code>--no-input</code> to disable every prompt, and MUST fail with the flag to pass when input is required in that mode</li>\n<li>MUST NOT print a password as it is typed</li>\n<li>MUST confirm destructive actions interactively with <code>y</code>/<code>yes</code>, and require <code>-f</code>/<code>--force</code> when not interactive</li>\n<li>MUST make severe operations (deleting a whole remote application or server) hard to confirm by accident, asking for the name of the thing being deleted or accepting <code>--confirm=&quot;name&quot;</code></li>\n<li>MUST offer <code>--dry-run</code> for moderate operations (deleting a directory or a remote resource, a bulk change) so the user can see what would happen</li>\n<li>SHOULD treat a config change that implicitly destroys something as severe</li>\n</ul>\n<h2>Subcommands</h2>\n<ul>\n<li>MUST be consistent across subcommands: same flag names for the same thing, similar output formatting</li>\n<li>MUST use the same two-level <code>noun verb</code> shape across object types when the tool manages several, and the same verbs for the same operations</li>\n<li>MUST NOT have a catch-all subcommand that swallows unknown input, since that makes any future subcommand name a breaking change</li>\n<li>MUST NOT accept arbitrary subcommand prefixes as abbreviations; aliases are fine but must be explicit and stable</li>\n<li>MUST NOT have ambiguous or similarly named subcommands: <code>update</code> and <code>upgrade</code> side by side is confusing</li>\n<li>SHOULD split a complex tool into subcommands, and merge closely related tools into one command</li>\n</ul>\n<h2>Performance and robustness</h2>\n<ul>\n<li>MUST print something to the user within 100 ms; responsive beats fast</li>\n<li>MUST print before a network request so the command never looks broken while waiting</li>\n<li>MUST apply a timeout to every network operation, configurable, with a sane default</li>\n<li>MUST say something immediately on Ctrl-C and exit as soon as possible; when a second Ctrl-C would be destructive, say what it will do</li>\n<li>MUST be idempotent where possible, and MUST assume a previous run died before its clean-up</li>\n<li>MUST be recoverable: after a transient failure the user re-runs the same command and it resumes where it left off</li>\n<li>MUST apply a timeout to any clean-up code, so it cannot hang forever</li>\n<li>SHOULD assume misuse: scripted invocation, bad connections, concurrent instances, untested environments</li>\n<li>SHOULD retry transient failures with backoff and jitter</li>\n<li>SHOULD be crash-only: defer clean-up to the next run or design it out, so a failure can exit immediately</li>\n</ul>\n<h2>Configuration and environment variables</h2>\n<ul>\n<li>MUST make the default the right thing for most users; a setting nobody knows to pass has failed</li>\n<li>MUST apply configuration in precedence order: flags → running shell environment → project config (such as <code>.env</code>) → user config → system config</li>\n<li>MUST follow the XDG Base Directory spec for user config (<code>~/.config/&lt;tool&gt;/</code>) rather than scattering dotfiles in <code>$HOME</code></li>\n<li>MUST let any default be overridden in a config file, so an argument is not repeated on every call</li>\n<li>MUST ask for consent before automatically modifying configuration the tool does not own, and say exactly what it does</li>\n<li>MUST NOT commandeer widely used variable names</li>\n<li>MUST name environment variables with uppercase letters, digits and underscores only, never starting with a digit</li>\n<li>SHOULD use a flag for per-invocation settings (debug level, dry run) and a version-controlled file for settings stable across a project</li>\n<li>SHOULD honour the general-purpose variables: <code>DEBUG</code>, <code>EDITOR</code>, <code>HTTP_PROXY</code>/<code>HTTPS_PROXY</code>/<code>ALL_PROXY</code>/<code>NO_PROXY</code>, <code>TERM</code>/<code>TERMINFO</code>/<code>TERMCAP</code>, <code>TMPDIR</code>, <code>HOME</code>, <code>PAGER</code>, <code>LINES</code>/<code>COLUMNS</code></li>\n<li>SHOULD read <code>.env</code> where appropriate for settings tied to a directory</li>\n<li>SHOULD create a new config file rather than appending to an existing one, and delimit additions with a dated comment</li>\n<li>SHOULD aim for single-line environment variable values</li>\n</ul>\n<h2>Security and privacy</h2>\n<ul>\n<li>MUST NOT read secrets from flags, where they leak into <code>ps</code> and shell history</li>\n<li>MUST NOT rely on environment variables for secrets</li>\n<li>MUST accept secrets from a file (<code>--password-file</code>) or from stdin</li>\n<li>MUST NOT read or write files the user did not pass as arguments (internal state such as a cache excepted), or call a remote server, without saying so</li>\n<li>MUST offer a <code>login</code> command for a tool with remote accounts, and treat the CLI’s API calls with the same security practices as the rest of the application</li>\n<li>MUST store a token or key, never raw credentials, in the OS keyring or a <code>0600</code> file under <code>~/.config/&lt;tool&gt;/</code>, so it can be revoked without touching the user’s primary login</li>\n</ul>\n<h2>Telemetry</h2>\n<ul>\n<li>MUST NOT phone home usage or crash data without consent</li>\n<li>MUST respect <code>DO_NOT_TRACK</code></li>\n<li>MUST state what is collected, why, how it is anonymised and how long it is retained</li>\n<li>MUST NOT block on analytics</li>\n<li>SHOULD prefer instrumenting the docs and the downloads over instrumenting the binary</li>\n<li>SHOULD ask for consent once, at first run, and make withdrawing it as easy as giving it</li>\n</ul>\n<h2>Naming and distribution</h2>\n<ul>\n<li>MUST give the program a short, memorable, lowercase name that is not too generic and does not collide with an existing command</li>\n<li>MUST be easy to uninstall, with the instruction at the bottom of the install instructions</li>\n<li>SHOULD keep the name easy to type for something people run all day</li>\n<li>SHOULD distribute as a single binary when the language allows it</li>\n<li>SHOULD otherwise use the platform’s native package installer over scattering files that cannot easily be removed</li>\n</ul>\n<h2>Compatibility and future-proofing</h2>\n<ul>\n<li>MUST treat subcommands, args, flags, config files and env vars as interfaces, and keep changes additive where possible</li>\n<li>MUST warn in the program itself before a non-additive change, telling the user how to make their usage future-proof, and stop warning once they have</li>\n<li>MUST document deprecations with a version number and a replacement</li>\n<li>MUST test on every platform the tool claims to support, and list them in the docs</li>\n<li>MUST note platform-specific install steps and behavioural differences rather than leaving users to guess</li>\n<li>SHOULD pin what the tool depends on</li>\n<li>MAY change human-readable output freely; that is why <code>--plain</code> and <code>--json</code> exist for scripts</li>\n</ul>\n<h2>Documentation</h2>\n<ul>\n<li>MUST define the CLI’s purpose in one sentence</li>\n<li>MUST list every command with a one-line explanation</li>\n<li>MUST give each command a syntax block before anything else</li>\n<li>MUST give every key command at least one copy-paste-ready example</li>\n<li>MUST explain every flag: what it does, when to use it, what happens if it is skipped</li>\n<li>MUST state default values and show the output format with a sample</li>\n<li>MUST document the errors users will hit and the exact command that fixes each</li>\n<li>MUST outline the setup requirements: authentication, environment variables, system prerequisites, minimum versions</li>\n<li>MUST cross-reference related commands, pointing to the next logical step</li>\n<li>MUST use one term per concept and keep it identical to the product’s own wording</li>\n<li>MUST show commands and their output as text, never as images</li>\n<li>MUST run every example command in the docs as written before publishing</li>\n<li>MUST state the CLI version the docs match, and note features that only exist from a given version</li>\n<li>MUST keep the docs in version control and update them in the same commit as the CLI change</li>\n<li>MUST use the same public docs for internal developers</li>\n<li>SHOULD outline real-world use cases, not just the command list</li>\n</ul>\n<h2>Testing and feedback</h2>\n<ul>\n<li>MUST get the CLI in front of developers who did not write it, including outsiders and newcomers</li>\n<li>MUST have someone outside the team read the docs for the first time and follow them without guessing</li>\n<li>MUST listen to the default and <code>--plain</code> output end to end through a screen reader (Orca, or <code>espeak-ng</code> at the least)</li>\n<li>SHOULD let users report usability problems easily, and invite usability feedback from newcomers</li>\n<li>SHOULD be explicit with testers about what feedback is wanted, and accept the spontaneous kind</li>\n</ul>\n<h2>Sources</h2>\n<ul>\n<li><a href=\"https://clig.dev/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Command Line Interface Guidelines</a> by Aanand Prasad, Ben Firshman, Carl Tashian and Eva Parish, CC BY-SA 4.0: many rules here are adapted from it</li>\n<li><a href=\"https://devcenter.heroku.com/articles/cli-style-guide\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Heroku CLI Style Guide</a></li>\n<li><a href=\"https://medium.com/@jdxcode/12-factor-cli-apps-dd3c227a0e46\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">12 Factor CLI Apps</a> by Jeff Dickey</li>\n<li><a href=\"https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">GNU Coding Standards: Standards for Command Line Interfaces</a></li>\n<li><a href=\"https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap12.html\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">POSIX Utility Conventions</a></li>\n<li><a href=\"https://www.rfc-editor.org/rfc/rfc2119\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">RFC 2119</a> and <a href=\"https://www.rfc-editor.org/rfc/rfc8174\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">RFC 8174</a>: requirement key words</li>\n<li><a href=\"https://specifications.freedesktop.org/basedir-spec/latest/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">XDG Base Directory Specification</a></li>\n<li><a href=\"https://no-color.org/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">NO_COLOR</a> and <a href=\"https://force-color.org/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">FORCE_COLOR</a></li>\n<li><a href=\"https://consoledonottrack.com/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Console Do Not Track</a>: the <code>DO_NOT_TRACK</code> variable</li>\n</ul>",
      "summary": "Sharing here my checklist that I use for CLI apps (was assembled manually and suffered by real usage). You can feed it to your agent (it will probably forget half of the rules, so use multiple passes or split it). LLM was used only to fix ESL mistakes. 154 rules.",
      "date_published": "2026-07-22T02:59:00.000Z",
      "tags": [
        "Standards",
        "CLI"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M71VHB6XHAW0E5NENYBH802M",
      "url": "https://dbuho.me/post/released-ignorelint/?utm_source=posts&utm_medium=rss",
      "title": "Released Ignorelint",
      "content_html": "<p>I promised myself to not joke about how ignorefiles were ignored by linters.</p>\n<p>Details at the <a href=\"https://dbuho.me/project/ignorelint/\">project page</a></p>",
      "summary": "I promised myself to not joke about how ignorefiles were ignored by linters. Details at the project page",
      "date_published": "2026-09-17T02:59:00.000Z",
      "tags": [
        "Continuous Integration"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M71TVRJWX8GNNXE303665RRF",
      "url": "https://dbuho.me/post/released-doomsday-clock-exporter/?utm_source=posts&utm_medium=rss",
      "title": "Released Doomsday Clock Exporter",
      "content_html": "<p>It works in theory, but we will have the first real life test in around January.\nIt&#39;s not the Bulleting Of Hardworking Atomic Scientists, you know.\nDon&#39;t wire your household turrets to it yet.</p>\n<p>More on <a href=\"https://dbuho.me/project/doomsday-clock-exporter/\">project page</a>.</p>",
      "summary": "It works in theory, but we will have the first real life test in around January. It's not the Bulleting Of Hardworking Atomic Scientists, you know. Don't wire your household turrets to it yet. More on project page .",
      "date_published": "2026-07-16T02:59:00.000Z",
      "tags": [
        "Observability",
        "Nuclear Non-Proliferation"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M71SE4V8EXMC82ZTXGPGY5YG",
      "url": "https://dbuho.me/post/how-to-sing-to-caged-birds/?utm_source=posts&utm_medium=rss",
      "title": "How to sing to caged birds",
      "content_html": "<p>I got the idea for this post when I noticed a couple of issues on the site of one of the resistance groups operating in ruzzia (credit where it’s due: they fixed them fast, and I will not name them).\nThis little guide could be useful for opposition media too.</p>\n<p>If you’re making something that is frowned upon by your local Authoritarianism™ franchise, you have a moral obligation to protect not only your cell | group | band | merry wagon, but your visitors too.</p>\n<p>Maybe even them first, because, not unlike you, they are not prepared and not vigilant enough.</p>\n<p>Some dictatorships will prosecute them for the visit itself; others will use finesse and make an “aggravating circumstance” out of it when they bake another “high treason” case.</p>\n<p>You can slap Cloudflare over your site and give yourself a gold star for security, but that’s only the beginning of your saboteur journey.</p>\n<h2>User device is a threat to the user</h2>\n<p>Or, to be precise, a cop scouring through it.\nYou don’t have to learn what modern forensic hardware can or cannot do, just treat the user’s device as fully transparent to the authorities. Especially those practicing <a href=\"https://en.wikipedia.org/wiki/Rubber-hose_cryptanalysis\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">cryptanalysis</a> with the help of a car battery attached to your genitals.</p>\n<h3>Mind the cache</h3>\n<ul>\n<li>Use <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\"><code>Cache-Control: no-store</code></a>.</li>\n<li><code>no-store</code> also stops a CDN from caching, and the CDN is your DDoS shield.</li>\n<li>Keep it caching with the targeted header <code>CDN-Cache-Control: max-age=60</code> (<a href=\"https://www.rfc-editor.org/rfc/rfc9213\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">RFC 9213</a>), which the browser ignores.</li>\n<li>If you worry about slow and metered connections, just remove the 5 different font files you included and cut down on JS/CSS.</li>\n</ul>\n<h3>You are not making the next Facebook here</h3>\n<ul>\n<li>No <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">service worker</a>: its registration persists and is listed in <code>chrome://serviceworker-internals</code>.</li>\n<li>No web app manifest, no “install this app” prompt, no push notifications.</li>\n<li>No <code>localStorage</code>, <code>IndexedDB</code> or long-lived cookies.</li>\n<li>If a cookie is unavoidable: session-only, <code>Secure</code>, <code>HttpOnly</code>, <code>SameSite=Strict</code> (<a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\"><code>Set-Cookie</code></a>).</li>\n</ul>\n<h3>A “clear traces” link</h3>\n<ul>\n<li>A URL that answers with <code>Clear-Site-Data: &quot;cache&quot;, &quot;cookies&quot;, &quot;storage&quot;</code>.</li>\n<li>This header wipes everything the site left in the browser, in one click.</li>\n<li>Read the <a href=\"https://w3c.github.io/webappsec-clear-site-data/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">specification</a> and <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Clear-Site-Data\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">MDN</a>.</li>\n<li>It doesn’t clear browser history. Remind your users to do it.</li>\n</ul>\n<h2>The Net is dark and full of terrors</h2>\n<h3>DNSSEC is not optional</h3>\n<ul>\n<li>DNS is always one of the first victims of <a href=\"https://en.wikipedia.org/wiki/Sovereign_Internet_Law\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">“sovereign internet”</a> endeavours.</li>\n<li>It’s not a panacea: malevolent actors can still force users through unsafe paths.</li>\n<li>They can also shoot users in their user faces, what can I say, we control what we can control.</li>\n<li>Do your part, enable damn <a href=\"https://en.wikipedia.org/wiki/Domain_Name_System_Security_Extensions\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">DNSSEC</a>, then check the chain with <a href=\"https://dnsviz.net/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">DNSViz</a>.</li>\n</ul>\n<h3>HSTS is not optional. HSTS preload too.</h3>\n<ul>\n<li>Without <a href=\"https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">HSTS</a>, any visit that <em>starts</em> on plain HTTP can be <a href=\"https://en.wikipedia.org/wiki/Downgrade_attack\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">stripped</a>: the ISP answers the HTTP request itself, fetches the site over HTTPS, and serves it back over HTTP. The redirect on the server never reaches the reader.</li>\n<li>Typed domains, old <code>http://</code> links, QR codes and messenger links all start on HTTP.</li>\n<li>HSTS ends the stripping from the second visit on; preload ends it from the first, because the list ships inside the browser.</li>\n<li>Add a header: <code>Strict-Transport-Security: max-age=63072000; includeSubDomains; preload</code> (<a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Strict-Transport-Security\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">MDN</a>, <a href=\"https://www.rfc-editor.org/rfc/rfc6797\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">RFC 6797</a>), then submit at <a href=\"https://hstspreload.org/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">hstspreload.org</a>.</li>\n<li>A visit that starts on <code>https://</code> cannot be stripped: the attacker needs a valid certificate, and gets a full-page warning without one. Unless the device trusts a …</li>\n</ul>\n<h3>State Root CA</h3>\n<ul>\n<li>ruzzia has one already. One day your country will have one too (<a href=\"https://en.wikipedia.org/wiki/Kazakhstan_man-in-the-middle_attack\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Kazakhstan tried</a>). To protect kids, dogs or national something.</li>\n<li>A <a href=\"https://en.wikipedia.org/wiki/DNS_Certification_Authority_Authorization\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">CAA record</a> will not save you, because a rogue government actor won’t respect it. That doesn’t mean you should ignore it.</li>\n<li><a href=\"https://en.wikipedia.org/wiki/Certificate_Transparency\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Certificate Transparency</a> logs show certificates issued for your domain by public CAs, so monitor them.</li>\n<li>Some of your users will MITM themselves for access to banking. They will forget to switch browsers or profiles.</li>\n<li>Just accept it and keep it in mind. Some of your users will see your site fully controlled by the dark side.</li>\n</ul>\n<h3>Encrypted Client Hello (ECH)</h3>\n<ul>\n<li>Encrypts the SNI, so the ISP sees only the CDN’s IP, shared with thousands of other sites (<a href=\"https://datatracker.ietf.org/doc/rfc9849/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">RFC 9849</a>).</li>\n<li>Needs DNS-over-HTTPS on the reader’s side and a CDN that supports it (<a href=\"https://developers.cloudflare.com/ssl/edge-certificates/ech/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Cloudflare does</a>).</li>\n</ul>\n<h3>TLS 1.3</h3>\n<ul>\n<li>Use <a href=\"https://www.rfc-editor.org/rfc/rfc8446\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">TLS 1.3</a>. Keep TLS 1.2 only with a stripped cipher list, or better, don’t use it at all. It’s not worth it.</li>\n<li>Mozilla’s <a href=\"https://ssl-config.mozilla.org/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">SSL Configuration Generator</a> writes the server config for you.</li>\n</ul>\n<h3>Any external resource is a liability</h3>\n<ul>\n<li>Remove Google Analytics, Meta Pixel, ad networks, Google Fonts, external CDNs for scripts. You don’t control them, and they don’t care about you or your audience.</li>\n<li>Obviously, never use a service run from the hostile country (e.g. Yandex.Metrica).</li>\n<li>The same goes for NPM packages and other <a href=\"https://en.wikipedia.org/wiki/Supply_chain_attack\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">supply chains</a>.</li>\n<li>If you need statistics: self-hosted, IP-less analytics (<a href=\"https://matomo.org/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Matomo</a>, <a href=\"https://plausible.io/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Plausible</a>, <a href=\"https://www.goatcounter.com/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">GoatCounter</a>) or process the web server logs.</li>\n<li>Self-host fonts, scripts and images. If you can’t, pin them with <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Subresource_Integrity\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Subresource Integrity</a>.</li>\n<li>Add <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referrer-Policy\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\"><code>Referrer-Policy: no-referrer</code></a>, so linked sites never learn the reader came from here.</li>\n<li>A strict <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">CSP</a> keeps a tracker from being added back by accident.</li>\n</ul>\n<h2>Educate your users</h2>\n<ul>\n<li>Steer them to <a href=\"https://www.torproject.org/download/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Tor Browser</a>, or at least a private window.</li>\n<li>Remind them that phones get searched at checkpoints and borders: clear history and app data, remove subscriptions before travel.</li>\n<li>Warn them about the risks of malware like Yandex.Browser, and of certificates or “security profiles” a provider or an official asks them to install.</li>\n</ul>\n<h2>Protect sources and readers who write in</h2>\n<ul>\n<li>Use <a href=\"https://securedrop.org/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">SecureDrop</a> for documents.</li>\n<li>Use Signal for tips and contact, with a <a href=\"https://signal.org/blog/phone-number-privacy-usernames/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">username instead of the phone number</a> (supported since 2024).</li>\n<li>Publish <a href=\"https://gnupg.org/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">GPG</a> keys for those who want to message you.</li>\n</ul>\n<h2>Add mirrors</h2>\n<ul>\n<li>Have an <a href=\"https://community.torproject.org/onion-services/setup/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Onion</a> / <a href=\"https://i2p.net/en/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">I2P</a> mirror and urge people to use it.</li>\n<li>Announce them with the <a href=\"https://community.torproject.org/onion-services/advanced/onion-location/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\"><code>Onion-Location</code></a> and <code>X-I2P-Location</code> headers on the HTTPS site; Tor Browser then offers the onion address by itself.</li>\n<li>The darknet copy should not leak the clearnet origin: canonical URLs, RSS and absolute links pointing at the clearnet domain push the reader out of Tor.</li>\n<li>Host the clearnet mirrors on infrastructure the censor cannot block without breaking things it needs (large clouds, large CDNs). This is called <a href=\"https://en.wikipedia.org/wiki/Collateral_freedom\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">collateral freedom</a>.</li>\n<li>Keep a list of mirror domains ready, and rotate them when one is blocked.</li>\n<li>Fake mirrors carrying malware are a standard attack on blocked outlets.</li>\n<li>Publish a signed list of official mirrors, and link it from everywhere.</li>\n</ul>\n<h2>Protect ya neck</h2>\n<ul>\n<li>DDoS protection, free for independent media:\n<ul>\n<li><a href=\"https://deflect.ca/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">eQualitie Deflect</a></li>\n<li><a href=\"https://www.cloudflare.com/galileo/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Cloudflare Project Galileo</a></li>\n<li><a href=\"https://projectshield.withgoogle.com/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Google Project Shield</a></li>\n</ul>\n</li>\n<li>Domain: never the hostile country’s ccTLD (it can be seized), a registrar outside its reach, <a href=\"https://www.icann.org/resources/pages/epp-status-codes-2014-06-16-en\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">registrar lock</a> on.</li>\n<li>Staff accounts: <a href=\"https://en.wikipedia.org/wiki/WebAuthn\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">hardware security keys</a> everywhere, the registrar and the DNS host first.</li>\n<li>Server logs: no IP addresses, short retention, a host outside the regime’s legal reach. A log that does not exist cannot be subpoenaed or seized.</li>\n<li>App stores remove apps on government demand: offer an APK directly or through <a href=\"https://f-droid.org/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">F-Droid</a>.</li>\n<li>Don’t forget to close the origin to non-Cloudflare addresses (<a href=\"https://www.cloudflare.com/ips/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">IP ranges</a>, <a href=\"https://developers.cloudflare.com/ssl/origin-configuration/authenticated-origin-pull/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Authenticated Origin Pulls</a>).</li>\n<li>If your host responds with the same content to a direct-IP request, an attacker can find it by brute-forcing the whole IPv4 space.</li>\n</ul>",
      "summary": "I got the idea for this post when I noticed a couple of issues on the site of one of the resistance groups operating in ruzzia (credit where it’s due: they fixed them fast, and I will not name them). This little guide could be useful for opposition media too. If you’re making something that is frowned upon by your local Authoritarianism™ franchise, you have a moral obligation to protect not only your cell | group | band | merry wagon, but your visitors too. Maybe even them first, because, not unlike you, they are not prepared and not vigilant enough. Some dictatorships will prosecute them for the visit itself; others will use finesse and make an “aggravating circumstance” out of it when they bake another “high treason” case. You can slap Cloudflare over your site and give yourself a gold star for security, but that’s only the beginning of your saboteur journey.",
      "date_published": "2026-11-10T02:59:00.000Z",
      "tags": [
        "Censorship",
        "Resilience",
        "Decentralization"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M6VEPK0KWRBF1ECS9HK78N4W",
      "url": "https://dbuho.me/post/released-support-ukraine-widget/?utm_source=posts&utm_medium=rss",
      "title": "Released support-ukraine widget",
      "content_html": "<p>We’ve all seen the blue-and-yellow #StandWithUkraine ribbons on various sites. While they look nice, I wanted something more actionable.</p>\n<p>I created a small script that displays links to random relevant charities based on the visitor&#39;s language, pointing to localized versions whenever available. It also tracks seen charities so visitors never see the same one twice (off by default, yay to GDPR). Highly tuneable.</p>\n<p>I like projects with a small scope and a single primary metric (CTR in this case), every feature or fix feels more impactful and challenging.</p>\n<p>More details on the <a href=\"https://dbuho.me/project/support-ukraine\">project page</a>, or check out the demo live on this site.</p>",
      "summary": "We’ve all seen the blue-and-yellow #StandWithUkraine ribbons on various sites. While they look nice, I wanted something more actionable. I created a small script that displays links to random relevant charities based on the visitor's language, pointing to localized versions whenever available. It also tracks seen charities so visitors never see the same one twice (off by default, yay to GDPR). Highly tuneable. I like projects with a small scope and a single primary metric (CTR in this case), every feature or fix feels more impactful and challenging. More details on the project page , or check out the demo live on this site.",
      "date_published": "2026-07-09T02:59:00.000Z",
      "tags": [
        "Ukraine"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M6VBEZV1ANZ88ZNKPNN756DN",
      "url": "https://dbuho.me/post/released-fork-of-repolinter/?utm_source=posts&utm_medium=rss",
      "title": "Released fork of repolinter",
      "content_html": "<p>Released a fork of <a href=\"https://github.com/todogroup/repolinter\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">todogroup/repolinter</a>.\nMigrated it to TypeScript, updated the tests and dependencies, and cleaned up the workflows.\nMajority of the issues of the original were fixed.</p>\n<p>My first experience with such necromancy. I&#39;m sure there are good reasons they dropped it, but it costs little to maintain, and I think the &quot;facts/rules&quot; scheme is actually very neat.</p>\n<p>Details on <a href=\"https://dbuho.me/project/repolinter/\">the project page</a>.</p>",
      "summary": "Released a fork of todogroup/repolinter . Migrated it to TypeScript, updated the tests and dependencies, and cleaned up the workflows. Majority of the issues of the original were fixed. My first experience with such necromancy. I'm sure there are good reasons they dropped it, but it costs little to maintain, and I think the \"facts/rules\" scheme is actually very neat. Details on the project page .",
      "date_published": "2026-07-09T02:59:00.000Z",
      "tags": [
        "Continuous Integration"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M5XR17NV4BGYBH7B2K1033ZB",
      "url": "https://dbuho.me/post/why-you-should-care-about-risc-v/?utm_source=posts&utm_medium=rss",
      "title": "Why You Should Care About RISC-V",
      "content_html": "<p>I try to support RISC-V everywhere I can, and you should too.</p>\n<p>Chances are (around 60%, actually) that you live in a country without an independent CPU/MCU industry. The consequences of the russian invasion in 2022 showed us that you can&#39;t really stop high-tech imports into a country that went batshit crazy, but you can make chips somewhat harder to get.</p>\n<p>I obviously don&#39;t have a problem with sanctions against the axis of evil. But one of these days, some orange clown (or clowns of any color) could decide that Uruguay, Chile, or New Zealand are as bad as North Korea or Iran—because they are too friendly to China, too gay, or something like that. And your country could be next.</p>\n<p><a href=\"https://cset.georgetown.edu/article/risc-v-what-it-is-and-why-it-matters/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">One Jacob Feldgoise already whines about RISC-V hindering good old world policing.</a></p>\n<p>RISC-V is not the simplest, but it is the most logical way to get closer to an &quot;Uruguayan Xeon&quot; or a &quot;Nigerian STM32.&quot; And it doesn&#39;t have to be an entirely national project: regional alliances could help lower costs and achieve scale faster. Getting an ARM license is probably easier, but it&#39;s another compliance leash from a country that loves exiting things without thinking them through.</p>\n<p>Without a stable supply of chips, you will have issues with everything from ATMs to train networks. It won&#39;t be immediate—modern silicon is quite durable—but things will break down and require replacement. If you don&#39;t have the benefit of a &quot;death cult of living in squalor and constantly overcoming unnecessary self-imposed problems&quot; in your population (like Russia has), it will become an issue very fast. An issue that both internal and external malevolent actors will exploit. Be less gay, have a chip.</p>\n<p>Sovereign RISC-V production is something that feels unnecessary, expensive, and alarmist now. But when it becomes &quot;today&#39;s problem,&quot; it will be too late.</p>\n<p>And even if you have 30+ CPU suppliers, you can still be enough of an asshole that all of them refuse to sell to you (though things won&#39;t change much for the real bad actors).</p>\n<h2>What Can You Do About It?</h2>\n<p>RISC-V is on the rise, but it still suffers from low adoption across both software and hardware — a classic chicken-and-egg issue.</p>\n<h2>If You Are a Developer</h2>\n<ul>\n<li>You can add cross-compilation to your project. It&#39;s very easy to do with C, Go, and Rust. In the case of platforms with less support, your interest and pressure can help speed up the process (e.g., Node, Crystal, PHP).</li>\n<li>There is a project called <strong>RISE</strong> that <a href=\"https://github.com/apps/rise-risc-v-runners-personal\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">offers RISC-V runners for GitHub CI</a>.\nAlthough they use hardware without vector extensions, which makes running some distros (like Ubuntu 26.04) impossible, they can still be useful.</li>\n<li>Also consider <strong>Cloud-V</strong>, <a href=\"https://cloud-v.co/risc-v-cicd\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">they provide CI runners</a>.</li>\n</ul>\n<h3>If You Are a User (Especially Corporate)</h3>\n<ul>\n<li>Ask maintainers of your favorite software to add RISC-V support. Vote for existing issues in issue trackers, or create new ones if they don&#39;t exist.</li>\n<li>Get yourself some hardware or cloud units and research how ready your infrastructure is for a potential migration.</li>\n</ul>\n<h2>If You Are an Activist Type</h2>\n<ul>\n<li>Check if local universities have RISC-V programs; support them if you can.</li>\n<li>Check if your jurisdiction has digital/semiconductor sovereignty programs. If not, ask your authorities why not.</li>\n<li>Spread the word: go door-to-door and ask people if they want to hear the good news about RISC-V. Dress appropriately; don&#39;t be weird.</li>\n</ul>\n<p>Having a strong alternative architecture will, in the end, benefit all of us.</p>\n<p><em>// And after the end too, when we scavenge for RISC-V chips in the wasteland.</em></p>\n<h2>Where to Get One</h2>\n<p>This is very dynamic market with some supply chain issues, links will go stale faster than I hit publish.</p>\n<p>Good keywords to start your search are SiFive and SpacemiT. Or look at the <a href=\"https://browser.geekbench.com/search?utf8=%E2%9C%93&amp;q=RISC\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Geekbench</a></p>",
      "summary": "I try to support RISC-V everywhere I can, and you should too. Chances are (around 60%, actually) that you live in a country without an independent CPU/MCU industry. The consequences of the russian invasion in 2022 showed us that you can't really stop high-tech imports into a country that went batshit crazy, but you can make chips somewhat harder to get. I obviously don't have a problem with sanctions against the axis of evil. But one of these days, some orange clown (or clowns of any color) could decide that Uruguay, Chile, or New Zealand are as bad as North Korea or Iran—because they are too friendly to China, too gay, or something like that. And your country could be next. One Jacob Feldgoise already whines about RISC-V hindering good old world policing. RISC-V is not the simplest, but it is the most logical way to get closer to an \"Uruguayan Xeon\" or a \"Nigerian STM32.\" And it doesn't have to be an entirely national project: regional alliances could help lower costs and achieve scale faster. Getting an ARM license is probably easier, but it's another compliance leash from a country that loves exiting things without thinking them through. Without a stable supply of chips, you will have issues with everything from ATMs to train networks. It won't be immediate—modern silicon is quite durable—but things will break down and require replacement. If you don't have the benefit of a \"death cult of living in squalor and constantly overcoming unnecessary self-imposed problems\" in your population (like Russia has), it will become an issue very fast. An issue that both internal and external malevolent actors will exploit. Be less gay, have a chip. Sovereign RISC-V production is something that feels unnecessary, expensive, and alarmist now. But when it becomes \"today's problem,\" it will be too late. And even if you have 30+ CPU suppliers, you can still be enough of an asshole that all of them refuse to sell to you (though things won't change much for the real bad actors). What Can You Do About It?",
      "date_published": "2026-06-30T02:59:00.000Z",
      "tags": [
        "RISC-V",
        "Hardware",
        "Digital Sovereignty",
        "Continuous Integration"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M5G12P950QYP3MH4XDZCS0JB",
      "url": "https://dbuho.me/post/another-phpocalypse-cve-2026-109618-and-cve-2026-109619/?utm_source=posts&utm_medium=rss",
      "title": "Another PHPocalypse: CVE-2026-109618 and CVE-2026-109619",
      "content_html": "<p><a href=\"https://www.wiz.io/blog/php2shell-php-rce\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">PHP2Shell: AI uncovers critical RCE vulnerabilities affecting 70% of the Internet</a>.</p>\n<p>In short, if you didn&#39;t update your PHP on the 21st, you&#39;re in danger.</p>\n<p>You can check if you&#39;re vulnerable via a <a href=\"https://github.com/CyberRoute/php2shell-scanner\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">self-hosted script</a> or the <a href=\"https://atlas.brandefense.io/php-vulnerability-check\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Brandefense ATLAS checker</a>.</p>\n<p>Other light reading in attached links.</p>\n<h2>Personal Experience</h2>\n<p>I had only one vulnerable service: Matomo Analytics (and Matomo&#39;s reaction to most of their users getting screwed is quite underwhelming). Custom image on a custom PHP distribution.</p>\n<p>And even though it&#39;s a sad day for the internet, I am quite amused by my OCD paying off:</p>\n<ul>\n<li>Matomo was inside a rootless container</li>\n<li>On a rootless container runtime</li>\n<li>With an isolated network</li>\n<li>Container had <code>cap_drop</code> and <code>no-new-privileges</code></li>\n<li>Separate database and cache</li>\n<li>All consumers of the analytics script had it locally pinned or used <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Subresource_Integrity\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Subresource Integrity</a></li>\n<li>CI blocked any attempts by nightly builds to publish an image with an old PHP interpreter</li>\n</ul>\n<p>There are some things that definitely need improvement, but overall, it&#39;s nice not to be in a panic.</p>\n<h2>Shameless Plug</h2>\n<p>Ironically, my second specialization after CI/CD/Platform Engineering is the modernization of PHP applications (I&#39;ve spent 20 years in PHP-related development).</p>\n<p>If you need something pulled out of the 7.x bog, help with fixing modern deployments, or assistance in preventing/minimizing damage in the future, <a href=\"mailto:damian.buho@proton.me\">send me an email</a>.</p>\n<p>I would also point out that this won&#39;t be the last drama of its kind, so embrace it.</p>\n<p>Brave new world: people without skills or responsibility wielding one of the most powerful tools in human history.</p>",
      "summary": "PHP2Shell: AI uncovers critical RCE vulnerabilities affecting 70% of the Internet . In short, if you didn't update your PHP on the 21st, you're in danger. You can check if you're vulnerable via a self-hosted script or the Brandefense ATLAS checker . Other light reading in attached links. Personal Experience I had only one vulnerable service: Matomo Analytics (and Matomo's reaction to most of their users getting screwed is quite underwhelming). Custom image on a custom PHP distribution. And even though it's a sad day for the internet, I am quite amused by my OCD paying off:",
      "date_published": "2026-10-21T19:24:47.163Z",
      "tags": [
        "Language: PHP",
        "Vulnerabilities",
        "Security"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M4EXHHXNN36X1HZCTY463WQY",
      "url": "https://dbuho.me/post/ai-stance/?utm_source=posts&utm_medium=rss",
      "title": "AI Stance",
      "content_html": "<p>Inspired by <a href=\"https://lucumr.pocoo.org/ai-transparency/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Armin Ronacher&#39;s AI Transparency Statement</a></p>\n<p>I use coding agents every day, but never use LLMs for writing or anything that&#39;s meant to be read by humans.\nI occasionally make a throwaway picture (like an avatar).</p>\n<p>I do use them to fix idiotic <abbr title=\"English as a second or foreign language\">ESL</abbr> mistakes I sometimes make. I use … and — via the Compose key in Linux since the 2010s.\nI have a background in traditional publishing and believe that three dots (...) are not an ellipsis (…) and never will be.</p>\n<h2>Ground rules</h2>\n<h3>Disclose for humans</h3>\n<p>Every human deserves to know the source of your information, and has the right to ignore an LLM answer without reading it. It&#39;s a good rule even for non-AI applications; I am not reading your &quot;russia today&quot; quote, mate.</p>\n<p>So, an LLM response should be marked with the same effort as &quot;trigger warnings&quot; or at least <a href=\"https://apastyle.apa.org/style-grammar-guidelines/citations/quotations\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">use some quoting standard like APA</a>.</p>\n<p>I make an exception for code, because the clanker that did some menial task doesn&#39;t deserve a &quot;Co-Authored&quot; byline.\nAlso, no one reads your code anyway (sad, but true).</p>\n<h3>Accepting full responsibility</h3>\n<p>An agent/model didn&#39;t fuck up. You did.</p>\n<h3>Never give up control</h3>\n<p>Don&#39;t trust them fully; always have a deterministic pipeline to check their work.</p>\n<h2>More personally</h2>\n<p>I dislike the misleading &quot;AI&quot; term and people who are <em>really</em> into it.</p>\n<p>Although, I believe any technology can produce obnoxious zealots. Just look at crypto.</p>\n<p>Maybe people should look into who raised all of those assholes, instead of screaming at a chain of hashes or matrix multipliers.</p>\n<p>I accept LLMs as a genie that can&#39;t be put back into the bottle. Holy wars about LLMs are boring.</p>\n<p>I do believe that companies behind so-called &quot;SOTA models&quot; are not to be trusted and LLMs should be self-hosted and sovereign.</p>\n<p>At the same time, the whole &quot;they drink all our water&quot; trope is a good way to detect virtue-signalling idiots.\nMost of them can&#39;t distinguish an artesian well from an Abyssinian one and have zero interest in the state of local aquifers.</p>\n<h2>Projectfile</h2>\n<p>I use the <a href=\"https://projectfile.org/extensions/org/projectfile/ai/\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">&quot;org.projectfile.ai&quot; extension</a> in my projectfile schema, which generates an &quot;AI POLICY.md&quot; document in all my projects.</p>",
      "summary": "Inspired by Armin Ronacher's AI Transparency Statement I use coding agents every day, but never use LLMs for writing or anything that's meant to be read by humans. I occasionally make a throwaway picture (like an avatar). I do use them to fix idiotic ESL mistakes I sometimes make. I use … and — via the Compose key in Linux since the 2010s. I have a background in traditional publishing and believe that three dots (...) are not an ellipsis (…) and never will be.",
      "date_published": "2026-06-23T02:59:00.000Z",
      "tags": [
        "LLM",
        "Meta",
        "Standards"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M3DBTSRD9PXWT45MSZS2E5NH",
      "url": "https://dbuho.me/post/unfeature-request/?utm_source=posts&utm_medium=rss",
      "title": "Unfeature Request: feature creep is the new code smell",
      "content_html": "<p>I believe we can add a new item to the list of &quot;code smells / project smells&quot;:</p>\n<p><strong>Too many features</strong></p>\n<p>Most modern <del>vibe-coded</del> agent-engineered CLI tools have 20, 30, or 40 verbs and internal commands in their TUI.\nOften, these feature duplicate or overlap existing functionality. They include things that could just be a flag on an existing command, things that could be hidden three keypresses away because 1% of users use them 1% of the time, or features that should never have been built in the first place because you could achieve the same result with Unix pipes and a smart use of core tools.</p>\n<h2>Why is it a problem?</h2>\n<h3>Cognitive overload</h3>\n<p>If you are making a tool for real humans, you should consider human nature: a short memory and a limited attention span.\nHumans get tired and bored. Well-established UI principles still hold true: give users fewer options to make choices easier, guide them, keep the learning curve in mind, and treat their attention as a limited resource.</p>\n<p>Of course, you can also build a tool intended solely for agents. If you&#39;re honest about that goal, why not?\nTranslate your help output to Claudespeak, drop all the formatting, and they will be happy.\nJust don&#39;t leave your tool stuck in an intermediate state where it is disrespectfully terse and confusing to a human user, yet not structured enough for a clanker.</p>\n<h3>The illusion of power</h3>\n<p>You may think an intimidating list of two dozen commands makes your tool look sophisticated and professional.\nYet the tools that truly make the world go round usually follow the <a href=\"https://en.wikipedia.org/wiki/Unix_philosophy\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Unix Way</a>. Look at anything from <code>cp</code> to <code>make</code> for reference.</p>\n<h3>Less code, please</h3>\n<p>More code means a bigger attack surface and more things to cover with tests (and let&#39;s be honest, even with cheap LLM-generated tests, people still forget or don&#39;t care enough to maintain them).\nIn other words: more moving parts to pay attention to, and more things that can go wrong.</p>\n<h2>What to do instead</h2>\n<h3>Split</h3>\n<p>A pair of smaller, focused tools will almost always be better than one monolithic beast. If you need them to work together, use pipes, the filesystem, or even gRPC.</p>\n<h3>Sometimes documentation is the feature</h3>\n<p>If your tool benefits from being piped through <code>grep</code>, <a href=\"https://github.com/burntsushi/ripgrep\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">rg</a>, <a href=\"https://github.com/mikefarah/yq\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">yq</a>, <a href=\"https://jqlang.org\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">jq</a>, or <a href=\"https://github.com/tomwright/dasel\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">dasel</a>, just tell the user how to do it in the docs.</p>\n<h3>The &quot;unfeature&quot; request</h3>\n<p>Ask yourself: <em>What can I delete from my project today?</em></p>\n<p>What will never be used? What lowers my signal-to-noise ratio? What could be done with the help of another tool? (Yes, you will have to set your ego aside.) Maybe when you originally built a feature, a better external solution didn&#39;t exist, but it&#39;s here now.</p>\n<p>Start depending on it (with proper precautions). An interlinked ecosystem of focused projects is much healthier than a dozen egotistical developers trying to reinvent the wheel from scratch for 2.5 users.\nYou won&#39;t become the next Daniel Stenberg or Fabrice Bellard anyway.</p>\n<p>I recommend asking yourself this question weekly. Ask your audience, too.\nIf nobody cries about a feature being removed, neither should you.</p>\n<p>Almost every discussion about &quot;taste&quot; in software development touches on the mantra of &quot;knowing what not to build.&quot;</p>\n<p>But once you&#39;ve already failed with that, the right move is learning how to cut.</p>",
      "summary": "I believe we can add a new item to the list of \"code smells / project smells\": Too many features Most modern vibe-coded agent-engineered CLI tools have 20, 30, or 40 verbs and internal commands in their TUI. Often, these feature duplicate or overlap existing functionality. They include things that could just be a flag on an existing command, things that could be hidden three keypresses away because 1% of users use them 1% of the time, or features that should never have been built in the first place because you could achieve the same result with Unix pipes and a smart use of core tools.",
      "date_published": "2026-06-02T02:59:00.000Z",
      "tags": [
        "Architecture",
        "Agents",
        "LLM",
        "Practices"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M3DBT8KSGE56K90ATV87MDZH",
      "url": "https://dbuho.me/post/what-is-image-distillation/?utm_source=posts&utm_medium=rss",
      "title": "What is image distillation?",
      "content_html": "<p>There are at least four ways to go about building container images:</p>\n<ul>\n<li>Small distributions, like Alpine</li>\n<li>Cut-down server versions (e.g., <code>debian-slim</code>)</li>\n<li>Whatever tf Ubuntu Chiseled is</li>\n<li>Distroless</li>\n</ul>\n<p>I use a fifth one.</p>\n<h2>Pain</h2>\n<p>In my experience, no one really likes building Alpine images, but because they&#39;re so lean, people feel forced to. MUSL, busybox, ash, python issues, package names often differ, command flags vary slightly, and so on.</p>\n<p>I&#39;ve used Ubuntu/Debian as an image base since 2018 because my developers were Debian-oriented and felt much more confident whenever they had to operate inside containers to debug.</p>\n<p>In production, though, Debian-based images are far from perfect. Debian has long confused &#39;paleolithic&#39; with &#39;stability.&#39; Meanwhile, Canonical adds useless thingamajigs like Pebble—complete with a bunch of Go stdlib vulnerabilities—or ruins RISC-V compatibility with an unnecessary move to vector extensions.</p>\n<p>Distroless images are pure and perfect — and also feel like a war crime when you&#39;re actually building them. Every single dependency has to be hand-crafted, and you constantly feel phantom pain from missing basic shell tools.</p>\n<p>Don&#39;t get me wrong: I love it, but it&#39;s a kink.</p>\n<h2>Distillation</h2>\n<p>To keep a cozy environment while working on a project, while simultaneously stripping down the attack surface of the production image, I use a technique I call <strong>image distillation</strong>.</p>\n<p>It&#39;s fairly obvious (I&#39;m pretty sure many people do this, I didn&#39;t invent anything), but I haven&#39;t found an established term for it yet (I&#39;d be glad to be proven wrong if you know one).</p>\n<ol>\n<li>Build in whatever base image you like: Alpine, Ubuntu, or whatever Red Hat is calling their server distro this week.</li>\n<li>At the end of that build stage, <code>COPY</code> everything you actually need (binary + libraries + runtime data) into a single folder (mine is usually called <code>/export</code>).</li>\n<li>Add a final stage with <code>FROM scratch</code> or <code>FROM distroless</code>, with a single <code>COPY --from=builder /export /</code>.</li>\n</ol>\n<p>You get the fat-free security of distroless in production with the developer pleasantries of a full OS during build time.</p>\n<p>You won&#39;t get any progress in understanding the meaning of life or finding true love, though.</p>\n<p>It&#39;s just a slim container image, what did you expect?</p>",
      "summary": "There are at least four ways to go about building container images: Small distributions, like Alpine Cut-down server versions (e.g., debian-slim ) Whatever tf Ubuntu Chiseled is Distroless I use a fifth one.",
      "date_published": "2026-06-16T02:59:00.000Z",
      "tags": [
        "Developer Experience",
        "Containerization",
        "Debian",
        "Ubuntu",
        "Docker"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M3DBSDTEQ9SDFATFNBFXEQ7B",
      "url": "https://dbuho.me/post/what-is-b19/?utm_source=posts&utm_medium=rss",
      "title": "What is B19 images and how I wrap things in containers",
      "content_html": "<p>A big part of my portfolio consists of so-called b19-style images.<br />\n<em>(b19 means nothing—you just can&#39;t get a proper namespace these days.)</em></p>\n<p>It started as an internal effort for my personal and commercial projects, but ended up as a set of conventions unified across 100+ various images (both wrappers and originals). I wanted every container to behave in a predictable way, agnostic of the tech stack, whether it&#39;s a source-based or upstream-based project.</p>\n<p>I&#39;m not urging anyone to adopt this directly yet—I need some time in public to polish everything—but if you&#39;re tasked with a similar unification job or manage a fleet, feel free to steal the conventions.</p>\n<p>The conventions are anchored in <a href=\"https://dbuho.me/project/b19-ubuntu/\">B19/Ubuntu</a> and flow organically into all downstream projects.<br />\nThe conventions are unopinionated (though the defaults can be).</p>\n<p>What&#39;s inside?</p>\n<h2>Basic container decency</h2>\n<ul>\n<li>Follows 12-factor app guidelines</li>\n<li>Reasonable, secure, and safe defaults with minimal user input required</li>\n<li>Images should be immutable; all state lives in volumes</li>\n<li>Support for XDG and other industry standards</li>\n<li>Proper telemetry everywhere</li>\n<li>File operations use locks, always keeping concurrency in mind</li>\n</ul>\n<h2>The Dockerfile orchestrates</h2>\n<ul>\n<li>No shell sagas</li>\n<li>No mile-long <code>RUN</code> commands—one <code>RUN</code> per stage</li>\n<li>Minimal layers</li>\n</ul>\n<h2>Switches, toggles, handles</h2>\n<p>B19-style images expose switches for all kinds of behavior: there&#39;s no such thing as &quot;too many switches.&quot; Where possible, switches are set via environment variables (relying on various mounts and binds is messy).<br />\nConfiguration files are generated via Jinja2 templates driven by environment variables.</p>\n<h2>Everything is <code>.d</code></h2>\n<p>Numbered hooks everywhere. Everything can be extended or overridden via simple drop-in shell scripts in downstream projects. Hooks can be inherited:</p>\n<ul>\n<li>Build is <code>build.d</code></li>\n<li>Healthcheck is <code>healthcheck.d</code></li>\n<li>Entrypoint is <code>entrypoint.d</code></li>\n</ul>\n<h2>Designed with off-grid in mind</h2>\n<ul>\n<li>Do not assume connectivity is guaranteed</li>\n<li>Do not assume fast or stable connectivity</li>\n<li>Timeouts, backoffs, and retries everywhere</li>\n</ul>\n<p>In other words, a b19 distribution comes with all the things you would end up adding to an official image anyway—right out of the box.</p>",
      "summary": "A big part of my portfolio consists of so-called b19-style images. (b19 means nothing—you just can't get a proper namespace these days.) It started as an internal effort for my personal and commercial projects, but ended up as a set of conventions unified across 100+ various images (both wrappers and originals). I wanted every container to behave in a predictable way, agnostic of the tech stack, whether it's a source-based or upstream-based project. I'm not urging anyone to adopt this directly yet—I need some time in public to polish everything—but if you're tasked with a similar unification job or manage a fleet, feel free to steal the conventions. The conventions are anchored in B19/Ubuntu and flow organically into all downstream projects. The conventions are unopinionated (though the defaults can be). What's inside?",
      "date_published": "2026-06-09T02:59:00.000Z",
      "tags": [
        "Meta",
        "Containerization",
        "Docker",
        "Continuous Integration",
        "Continuous Deployment"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M38MR8KZMWZW5AFX2WPV1WB6",
      "url": "https://dbuho.me/post/maintainer-pledge/?utm_source=posts&utm_medium=rss",
      "title": "About maintenance",
      "content_html": "<p>This post sets some clear expectations regarding projects I develop and maintain.</p>\n<p>Let&#39;s call it Commitment Pledge.</p>\n<p>In short, I don&#39;t abandon shit easily and appreciate people&#39;s trust.</p>\n<h2>Lifecycle &amp; Exit Policy</h2>\n<h3>Transparent Status</h3>\n<p>The repository will clearly display its maintenance state via standard badges (e.g., &quot;Maintained&quot;, &quot;Archived&quot;, &quot;Open For Adoption&quot;, &quot;Sundown&quot;).</p>\n<h3>Graceful Exit</h3>\n<p>If I reach burnout, lose interest, or can no longer maintain the codebase, the project will not be silently abandoned. I will update the project&#39;s lifecycle badge to &quot;Sundown&quot; or &quot;Open For Adoption&quot; and post instructions about forking or hand-off with at least a month of advance warning.</p>\n<h3>Deadman Switch</h3>\n<p>If a man or an animal or a huge meteor gets the best of me and I become dead, designated person will deal with inheritance and hand-offs.</p>\n<h2>Integrity, security, privacy</h2>\n<h3>No Protestware or Sabotage</h3>\n<p>Code updates will strictly serve the project&#39;s stated technical function. I will never intentionally inject malware, protestware, console activism, political popups, or code designed to break environments. Although, I will openly and transparently make your life harder if you support evil shit.</p>\n<h3>No Unsolicited Telemetry</h3>\n<p>I will not include tracking, phone-home features, user profiling, or analytics without explicit, transparent, opt-in consent. Not sure if I do it even with consent.</p>\n<h3>No Relicensing Rug-Pulls</h3>\n<p>Published releases will remain open source. Future releases will not be retroactively switched to restrictive, commercial, or non-OSI-approved source-available licenses.</p>\n<h3>No Package Deletions</h3>\n<p>Published releases or package registry artifacts will not be arbitrarily deleted or yanked, preventing broken downstream builds, except when required for critical security or legal reasons.</p>\n<h3>Transparent Stewardship Transfers</h3>\n<p>Administrative control, repo ownership, and package-publishing keys will never be sold or handed off to unverified third parties. Any transfer of maintainership will be publicly announced and verified.</p>\n<h2>Obvious</h2>\n<h3>Right to Refuse</h3>\n<p>I reserve the right to decline features, code contributions, or pull requests that do not align with the project’s scope, design philosophy, or maintenance bandwidth.</p>\n<h3>No Obligation of Service</h3>\n<p>Maintenance is conducted on a best-effort basis; I am not an on-call support team or corporate service desk (but we can discuss it)</p>\n<p>I do it because I like making and sharing stuff I find useful.</p>",
      "summary": "This post sets some clear expectations regarding projects I develop and maintain. Let's call it Commitment Pledge. In short, I don't abandon shit easily and appreciate people's trust.",
      "date_published": "2026-05-02T02:59:00.000Z",
      "tags": [
        "Meta"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M293G9HFA4ASQY02ZR5M8009",
      "url": "https://dbuho.me/post/spotlight-humanjson/?utm_source=posts&utm_medium=rss",
      "title": "Spotlight: human.json",
      "content_html": "<p>I have <a href=\"https://dbuho.me/human.json\">human.json</a> on this site.</p>\n<blockquote>\n<p>human.json is a lightweight protocol for humans to assert authorship of their site content and\nvouch for the humanity of others. It uses URL ownership as identity, and trust propagates through a\ncrawlable web of vouches between sites.\n<a href=\"https://codeberg.org/robida/human.json\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link bare-url\">https://codeberg.org/robida/human.json</a></p>\n</blockquote>\n<p>I like the idea of establishing trust between people in this slop era.</p>\n<p>I always felt like such conventions and standards are something that only a bunch of important\ngrayscrotums in very important commitees can do, and human.json actually inspired me to design\n<a href=\"https://projectfile.org\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">projectfile</a>, first and foremost for myself, but also to put it out there.</p>\n<p>Although, I believe that any kind of commentless JSON should be only used by Catholic nuns as\npunishment tool, I also believe we need more initiatives like this one. Not only because it&#39;s a\nlegitimate attempt to solve a problem, but also because we lately gave up a lot of ground to\ncommittees of aforementioned grayscrotums (corporate or pseudocorporate), and the world kinda sucks\nbecause of it.</p>\n<p>You can fuck the right off with your new vibe-coded habit tracker, but if you have an idea for a\nprotocol or a scheme that empowers other people to build, I salute you.\nEven if it&#39;s weird or obscure, any such idea is an open invitation to work together, and that&#39;s\ncool.</p>\n<p>P.S. Technically it was just a &quot;Vouch&quot; collection in my Payload CMS backend, and a little renderer\nfor my Astro frontend, so I am not sure that I&#39;ll open a human.json consulting agency anytime soon.</p>",
      "summary": "I have human.json on this site. human.json is a lightweight protocol for humans to assert authorship of their site content and vouch for the humanity of others. It uses URL ownership as identity, and trust propagates through a crawlable web of vouches between sites. https://codeberg.org/robida/human.json I like the idea of establishing trust between people in this slop era. I always felt like such conventions and standards are something that only a bunch of important grayscrotums in very important commitees can do, and human.json actually inspired me to design projectfile , first and foremost for myself, but also to put it out there.",
      "date_published": "2026-05-25T02:59:00.000Z",
      "tags": [
        "Standards",
        "Small Web"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M293G9HA0RNMP03GD6F1CP0Q",
      "url": "https://dbuho.me/post/released-textlint-rule-ukraine/?utm_source=posts&utm_medium=rss",
      "title": "Released textlint-rule-ukraine",
      "content_html": "<p>I am a big fan of textlint, correct quotes, long dashes, and seeing &quot;0 errors&quot; in my logs.</p>\n<p>And I noticed how often Western media mess up correct transliterations of Ukrainian identifiers,\nusing russian-to-English transliterations instead of proper Ukrainian-to-English ones.</p>\n<p>There is a <a href=\"https://spellingukraine.com\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">nice web app with bots tackling this topic</a> — check it out.\nI used their vocabulary to make a ruleset for <a href=\"https://textlint.org\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">Textlint</a> — pretty sure they\nare not interested in doing it themselves.</p>\n<p>Published as <a href=\"https://www.npmjs.com/package/textlint-rule-ukraine\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">my first npm package</a>.\nNothing complex or particularly cool — just wanted to experience npm from the other side.</p>\n<p>Excited to become another supply chain attack vector in the future!</p>",
      "summary": "I am a big fan of textlint, correct quotes, long dashes, and seeing \"0 errors\" in my logs. And I noticed how often Western media mess up correct transliterations of Ukrainian identifiers, using russian-to-English transliterations instead of proper Ukrainian-to-English ones. There is a nice web app with bots tackling this topic — check it out. I used their vocabulary to make a ruleset for Textlint — pretty sure they are not interested in doing it themselves. Published as my first npm package . Nothing complex or particularly cool — just wanted to experience npm from the other side. Excited to become another supply chain attack vector in the future!",
      "date_published": "2026-05-17T02:59:00.000Z",
      "tags": [
        "Continuous Integration",
        "Ukraine"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M293G9H7T5EGXC59FRWE5A8X",
      "url": "https://dbuho.me/post/hello-world/?utm_source=posts&utm_medium=rss",
      "title": "Hello world",
      "content_html": "<blockquote>\n<p>This is my blog. There are many like it, but this one is mine.</p>\n</blockquote>\n<p>I am going to write about my <a href=\"https://dbuho.me/about\">interests</a>. And sometimes about my experiences.\nAnd about what I do as an independent developer.</p>",
      "summary": "This is my blog. There are many like it, but this one is mine. I am going to write about my interests . And sometimes about my experiences. And about what I do as an independent developer.",
      "date_published": "2026-05-02T02:59:00.000Z",
      "tags": [
        "Meta"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M293G9H2KCQRPKNW808W5TPW",
      "url": "https://dbuho.me/post/first-pr/?utm_source=posts&utm_medium=rss",
      "title": "Officialy made it",
      "content_html": "<p>For the first time in my life, my modest pull request was accepted into a mainstream project\n(AppArmor). That was also my first attempt.</p>\n<p>All because of intense hatred for warnings in system logs. And because I read dmesg for fun.</p>\n<p>The &quot;who&quot; utility from Rust-based coreutils was triggering AppArmor warnings on my Kubuntu box while\ntrying to access its own localizations. The fix was trivial, and the AppArmor maintainers are\nblazing fast.</p>\n<p>Now I officially refuse to talk to plebs without comparable legacy in open source.</p>",
      "summary": "For the first time in my life, my modest pull request was accepted into a mainstream project (AppArmor). That was also my first attempt. All because of intense hatred for warnings in system logs. And because I read dmesg for fun. The \"who\" utility from Rust-based coreutils was triggering AppArmor warnings on my Kubuntu box while trying to access its own localizations. The fix was trivial, and the AppArmor maintainers are blazing fast. Now I officially refuse to talk to plebs without comparable legacy in open source.",
      "date_published": "2026-05-12T02:59:00.000Z",
      "tags": [
        "Open Source"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    },
    {
      "id": "01M293G9GXAA3B39G0HNK87ARR",
      "url": "https://dbuho.me/post/deployed-reseed-server-for-i2p-on-kiota/?utm_source=posts&utm_medium=rss",
      "title": "Deployed I2P Reseed server on Kiota",
      "content_html": "<p>Made my own <a href=\"https://reseed.kiota.ch\" rel=\"noopener noreferrer\" target=\"_blank\" class=\"external-link\">reseed server for I2P</a>.</p>\n<blockquote>\n<p>I2P is a peer-to-peer network which uses “Garlic Routing” to maintain privacy. Reseed nodes help you get connected to I2P for the first time, and even though you should only have to use them once in a great while, they are very important services.</p>\n</blockquote>\n<p>I have my own <a href=\"https://dbuho.me/project/f5m-i2p-reseed/\">distribution/image</a> that automates some fuss with installation.</p>\n<p>Feeling like a very niche Atlas.</p>",
      "summary": "Made my own reseed server for I2P . I2P is a peer-to-peer network which uses “Garlic Routing” to maintain privacy. Reseed nodes help you get connected to I2P for the first time, and even though you should only have to use them once in a great while, they are very important services. I have my own distribution/image that automates some fuss with installation. Feeling like a very niche Atlas.",
      "date_published": "2026-09-02T02:59:00.000Z",
      "tags": [
        "Containerization",
        "Networking",
        "I2P",
        "Censorship"
      ],
      "authors": [
        {
          "name": "Damián Búho"
        }
      ]
    }
  ]
}