{
    "version": "https://jsonfeed.org/version/1",
    "title": "Tyler Cipriani: blog",
    "home_page_url": "https://tylercipriani.com/blog/",
    "feed_url": "https://tylercipriani.com/blog/index.json",
    "items": [
        {

    "id": "https://tylercipriani.com/blog/2026/05/28/chuwi-minibook-x/",

    "title": "Chuwi Minibook&#xA0;X: the netbook we deserve",
    "url": "https://tylercipriani.com/blog/2026/05/28/chuwi-minibook-x/",

    "author": {
        "name": "Tyler Cipriani"
    },


    "tags": [

     "computing"

    ],

    "date_published": "2026-05-28T00:05:21Z",
    "date_modified": "2026-05-31T22:55:48Z",


    "content_html": "<style>\n.title {text-wrap:balance;}\n#content > p:first-child {text-wrap:balance;}\n.gallery {\n  display:flex;\n  justify-content:center;\n}\nfigure a:link,\nfigure a:visited,\nfigure a:hover,\nfigure a:active {color:inherit; text-decoration: underline;}\n</style>\n<p>Netbooks are dead, but the Chuwi Minibook X scratches the same\nitch.</p>\n<p>The Minibook X is a 10.5″ x86_64 sub-ultrabook with 16GB RAM, a 512GB\nNVMe drive, and only one majorly annyoing Linux quirk.</p>\n<p>I needed a knock-around laptop, so I bought myself a Minibook for my\nbirthday last year. The more I tote it around, the more fun I’m having\nwith this ridiculous little computer.</p>\n<figure>\n  <a href=\"https://photos.tylercipriani.com/thumbs/58/ec9d31f64e7d203a6d40603d8ec352/xlarge.jpg\">\n    <picture>\n      <source type=\"image/avif\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/58/ec9d31f64e7d203a6d40603d8ec352/small.avif 240w,\n                      https://photos.tylercipriani.com/thumbs/58/ec9d31f64e7d203a6d40603d8ec352/medium.avif 640w,\n                      https://photos.tylercipriani.com/thumbs/58/ec9d31f64e7d203a6d40603d8ec352/large.avif 1024w,\n                      https://photos.tylercipriani.com/thumbs/58/ec9d31f64e7d203a6d40603d8ec352/larger.avif 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 70vw\">\n      <source type=\"image/webp\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/58/ec9d31f64e7d203a6d40603d8ec352/small.webp 240w,\n                      https://photos.tylercipriani.com/thumbs/58/ec9d31f64e7d203a6d40603d8ec352/medium.webp 640w,\n                      https://photos.tylercipriani.com/thumbs/58/ec9d31f64e7d203a6d40603d8ec352/large.webp 1024w,\n                      https://photos.tylercipriani.com/thumbs/58/ec9d31f64e7d203a6d40603d8ec352/larger.webp 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 70vw\">\n      <img src=\"https://photos.tylercipriani.com/thumbs/58/ec9d31f64e7d203a6d40603d8ec352/medium.jpg\"\n           srcset=\"https://photos.tylercipriani.com/thumbs/58/ec9d31f64e7d203a6d40603d8ec352/small.jpg 240w,\n                   https://photos.tylercipriani.com/thumbs/58/ec9d31f64e7d203a6d40603d8ec352/medium.jpg 640w,\n                   https://photos.tylercipriani.com/thumbs/58/ec9d31f64e7d203a6d40603d8ec352/large.jpg 1024w,\n                   https://photos.tylercipriani.com/thumbs/58/ec9d31f64e7d203a6d40603d8ec352/larger.jpg 1200w\"\n           sizes=\"(max-width: 54em) 85vw, 70vw\"\n           alt=\"Chuwi Minibook X, KDE lock screen\"\n           loading=\"lazy\"\n           decoding=\"async\">\n    </picture>\n    <figcaption aria-hidden=\"true\">Chuwi Minibook X</figcaption>\n  </a>\n</figure>\n<section id=\"quick-specs\" class=\"level2\">\n<h2>Quick specs</h2>\n<p>Much like the netbooks of yore, the Minibook is a budget machine. But\nit’s 2026, so even budget machines pack more oomph than I need from a\nutility laptop.</p>\n<ul>\n<li>CPU 4-core/4-thread 3.6GHz Intel N150 Twin Lake</li>\n<li>16 GB RAM – LPDDR5-6400 – soldered 😿</li>\n<li>512GB NVMe – upgradable</li>\n<li>10.51” IPS 2K 16:10 screen</li>\n<li>28.88Wh Li-Ion battery</li>\n<li>Weight: 911g</li>\n<li>Ports: 2×USB-C (1×PD charging)</li>\n<li>Cost: $350</li>\n</ul>\n<figure>\n  <a href=\"https://photos.tylercipriani.com/thumbs/a1/cbe8bd56a1085ab2e3c7a6388bed5f/xlarge.jpg\">\n    <picture>\n      <source type=\"image/avif\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/a1/cbe8bd56a1085ab2e3c7a6388bed5f/small.avif 240w,\n                      https://photos.tylercipriani.com/thumbs/a1/cbe8bd56a1085ab2e3c7a6388bed5f/medium.avif 640w,\n                      https://photos.tylercipriani.com/thumbs/a1/cbe8bd56a1085ab2e3c7a6388bed5f/large.avif 1024w,\n                      https://photos.tylercipriani.com/thumbs/a1/cbe8bd56a1085ab2e3c7a6388bed5f/larger.avif 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 50vw\">\n      <source type=\"image/webp\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/a1/cbe8bd56a1085ab2e3c7a6388bed5f/small.webp 240w,\n                      https://photos.tylercipriani.com/thumbs/a1/cbe8bd56a1085ab2e3c7a6388bed5f/medium.webp 640w,\n                      https://photos.tylercipriani.com/thumbs/a1/cbe8bd56a1085ab2e3c7a6388bed5f/large.webp 1024w,\n                      https://photos.tylercipriani.com/thumbs/a1/cbe8bd56a1085ab2e3c7a6388bed5f/larger.webp 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 50vw\">\n      <img src=\"https://photos.tylercipriani.com/thumbs/a1/cbe8bd56a1085ab2e3c7a6388bed5f/medium.jpg\"\n           srcset=\"https://photos.tylercipriani.com/thumbs/a1/cbe8bd56a1085ab2e3c7a6388bed5f/small.jpg 240w,\n                   https://photos.tylercipriani.com/thumbs/a1/cbe8bd56a1085ab2e3c7a6388bed5f/medium.jpg 640w,\n                   https://photos.tylercipriani.com/thumbs/a1/cbe8bd56a1085ab2e3c7a6388bed5f/large.jpg 1024w,\n                   https://photos.tylercipriani.com/thumbs/a1/cbe8bd56a1085ab2e3c7a6388bed5f/larger.jpg 1200w\"\n           sizes=\"(max-width: 54em) 85vw, 50vw\"\n           alt=\"Chuwi Minibook with back cover removed, exposing battery and NVMe drive\"\n           loading=\"lazy\"\n           decoding=\"async\">\n    </picture>\n    <figcaption aria-hidden=\"true\">Chuwi Minibook Guts</figcaption>\n  </a>\n</figure>\n<p>One oddity is that the Minibook comes bundled with a 12V/2A USB-C\ncharger. I chucked the charger; I worried I’d fry some 5V SoC someday.\nThe Minibook works fine with a PD charger.</p>\n<figure>\n  <a href=\"https://photos.tylercipriani.com/thumbs/18/7a50ffe3248627bddf4ca435c047a5/xlarge.jpg\">\n    <picture>\n      <source type=\"image/avif\" srcset=\"https://photos.tylercipriani.com/thumbs/18/7a50ffe3248627bddf4ca435c047a5/small.avif 240w,\n                                        https://photos.tylercipriani.com/thumbs/18/7a50ffe3248627bddf4ca435c047a5/medium.avif 640w\"\n              sizes=\"(max-width: 54em) 70vw, 20vw\">\n      <source type=\"image/webp\" srcset=\"https://photos.tylercipriani.com/thumbs/18/7a50ffe3248627bddf4ca435c047a5/small.webp 240w,\n                                        https://photos.tylercipriani.com/thumbs/18/7a50ffe3248627bddf4ca435c047a5/medium.webp 640w\"\n              sizes=\"(max-width: 54em) 70vw, 20vw\">\n      <img src=\"https://photos.tylercipriani.com/thumbs/18/7a50ffe3248627bddf4ca435c047a5/medium.jpg\"\n           srcset=\"https://photos.tylercipriani.com/thumbs/18/7a50ffe3248627bddf4ca435c047a5/small.jpg 240w,\n                   https://photos.tylercipriani.com/thumbs/18/7a50ffe3248627bddf4ca435c047a5/medium.jpg 640w\"\n           sizes=\"(max-width: 54em) 70vw, 20vw\"\n           alt=\"Minibook X using a PD Charger at 20V\"\n           loading=\"lazy\"\n           decoding=\"async\">\n    </picture>\n    <figcaption aria-hidden=\"true\">Minibook X using a PD Charger at 20V</figcaption>\n  </a>\n</figure>\n<p>I’d assume the 12V charger was a cost-saving choice, but it also\ncreates some weird possibilities for DC/off-grid setups.</p>\n</section>\n<section id=\"linux-and-weirdness-sideways-panels-and-kernel-parameters\"\nclass=\"level2\">\n<h2>Linux and weirdness: sideways panels and kernel parameters</h2>\n<figure>\n<img src=\"https://photos.tylercipriani.com/2026-05-17_cstross-chuwi-minibook.png\" alt=\"Charlie Stross, one of my favorite SciFi authors, recommended the Chuwi Minibook X on Mastodon\">\n<figcaption aria-hidden=\"true\">\n  <a class=\"citation\" data-cites=\"@cstross\" href=\"https://wandering.shop/@cstross\">Charlie Stross</a>, a favorite SciFi author, talks up the Chuwi Minibook X on <a href=\"https://wandering.shop/@cstross/113279670998603629\">Mastodon</a></figcaption>\n</figcaption>\n</figure>\n<p>The fediverse told me that Minibook runs Linux “boringly well,” which\nwas <em>almost</em> true.</p>\n<p>I tried Debian, then jumped to NixOS for kicks.</p>\n<p>What works:</p>\n<ul>\n<li>Camera/Microphone/Speakers</li>\n<li>Touchscreen</li>\n<li>Sleep/Suspend</li>\n<li>Hibernate</li>\n<li>Keyboard backlight</li>\n<li>USB-C HDMI</li>\n<li>Bluetooth (non-free blobs – Intel)</li>\n<li>Wi-Fi 6 (non-free blobs – Intel)</li>\n</ul>\n<p>But on first boot, the screen orientation is 270° clockwise:</p>\n<figure>\n  <a href=\"https://photos.tylercipriani.com/thumbs/fd/618ab5184903c7a8a69428d4df70e0/xlarge.jpg\">\n    <picture>\n      <source type=\"image/avif\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/fd/618ab5184903c7a8a69428d4df70e0/small.avif 240w,\n                      https://photos.tylercipriani.com/thumbs/fd/618ab5184903c7a8a69428d4df70e0/medium.avif 640w,\n                      https://photos.tylercipriani.com/thumbs/fd/618ab5184903c7a8a69428d4df70e0/large.avif 1024w,\n                      https://photos.tylercipriani.com/thumbs/fd/618ab5184903c7a8a69428d4df70e0/larger.avif 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 50vw\">\n      <source type=\"image/webp\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/fd/618ab5184903c7a8a69428d4df70e0/small.webp 240w,\n                      https://photos.tylercipriani.com/thumbs/fd/618ab5184903c7a8a69428d4df70e0/medium.webp 640w,\n                      https://photos.tylercipriani.com/thumbs/fd/618ab5184903c7a8a69428d4df70e0/large.webp 1024w,\n                      https://photos.tylercipriani.com/thumbs/fd/618ab5184903c7a8a69428d4df70e0/larger.webp 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 50vw\">\n      <img src=\"https://photos.tylercipriani.com/thumbs/fd/618ab5184903c7a8a69428d4df70e0/medium.jpg\"\n           srcset=\"https://photos.tylercipriani.com/thumbs/fd/618ab5184903c7a8a69428d4df70e0/small.jpg 240w,\n                   https://photos.tylercipriani.com/thumbs/fd/618ab5184903c7a8a69428d4df70e0/medium.jpg 640w,\n                   https://photos.tylercipriani.com/thumbs/fd/618ab5184903c7a8a69428d4df70e0/large.jpg 1024w,\n                   https://photos.tylercipriani.com/thumbs/fd/618ab5184903c7a8a69428d4df70e0/larger.jpg 1200w\"\n           sizes=\"(max-width: 54em) 85vw, 50vw\"\n           alt=\"Tails setup screen rotated\"\n           loading=\"lazy\"\n           decoding=\"async\">\n    </picture>\n  </a>\n  <figcaption aria-hidden=\"true\">Linux setup screen rotated 270°.</figcaption>\n</figure>\n<p>The Chuwi’s screen is a panel from a cheap tablet; the screen\nrotation issue is a hardware problem (the screen is mounted sideways).\nTo fix the screen’s rotation, I had to tweak screen orientation at every\nsoftware layer. Fixing this problem was a journey:</p>\n<ol type=\"1\">\n<li>Bootloader – Switched from <code>systemd-boot</code> to\n<code>grub</code>, carrying some <a\nhref=\"https://github.com/iggyZiggy/chuwi-grub-rotation-nix-patch\">unmerged\nGRUB rotation patches</a> on top.</li>\n<li>Initrd – Tell the Intel display driver about the panel orientation\nvia a kernel parameter, and force the Intel driver to load in the\ninitramfs. On NixOS:\n<code>boot.kernelParams = [\"video=DSI-1:panel_orientation=right_side_up\"];</code>\nand <code>boot.initrd.kernelModules = [\"i915\"];</code> (see Kernel docs\nfor <a href=\"https://docs.kernel.org/fb/modedb.html\">modedb default\nvideo mode support</a>)</li>\n<li>Desktop environment – For X11, good ole\n<code>xrandr --output DSI-1 --rotate right</code>. Wayland picked this\nup from the DRM connector. This one was easy.</li>\n<li>Framebuffer – Ensure all TTYs have the proper orientation by adding\n<code>fbcon=rotate:1</code> to kernel parameters\n<code>boot.kernelParams = [\"fbcon=rotate:1\"];</code> (see Kernel docs\nfor <a\nhref=\"https://docs.kernel.org/fb/fbcon.html#c-boot-options\">framebuffer\nconsole boot options</a>)</li>\n</ol>\n<p>Behold, the final result in all its glory:</p>\n<figure>\n<video autoplay loop muted loading=\"lazy\"><source src=\"https://photos.tylercipriani.com/2026-05-31_chuwi-boot-smol.mp4\" type=\"video/mp4\"></video>\n<figcaption>Non-rotated system boot. Zero Cool\'s bootscreen courtesy of <a href=\"https://github.com/mainframed/Hackers-Plymouth\"><code>mainframed/Hackers-Plymouth</code></a></figcaption>\n</figure>\n</section>\n<section id=\"size-weight-and-build\" class=\"level2\">\n<h2>Size, weight, and build</h2>\n<p>This computer is mind-bogglingly small. The build is sturdy and\ntotable; it’ll hold up to a backpack jostling.</p>\n<figure>\n  <a href=\"https://photos.tylercipriani.com/thumbs/20/2a52c0ab8facc8cb6d6bb6f81fe137/xlarge.jpg\">\n    <picture>\n      <source type=\"image/avif\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/20/2a52c0ab8facc8cb6d6bb6f81fe137/small.avif 240w,\n                      https://photos.tylercipriani.com/thumbs/20/2a52c0ab8facc8cb6d6bb6f81fe137/medium.avif 640w,\n                      https://photos.tylercipriani.com/thumbs/20/2a52c0ab8facc8cb6d6bb6f81fe137/large.avif 1024w,\n                      https://photos.tylercipriani.com/thumbs/20/2a52c0ab8facc8cb6d6bb6f81fe137/larger.avif 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 50vw\">\n      <source type=\"image/webp\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/20/2a52c0ab8facc8cb6d6bb6f81fe137/small.webp 240w,\n                      https://photos.tylercipriani.com/thumbs/20/2a52c0ab8facc8cb6d6bb6f81fe137/medium.webp 640w,\n                      https://photos.tylercipriani.com/thumbs/20/2a52c0ab8facc8cb6d6bb6f81fe137/large.webp 1024w,\n                      https://photos.tylercipriani.com/thumbs/20/2a52c0ab8facc8cb6d6bb6f81fe137/larger.webp 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 50vw\">\n      <img src=\"https://photos.tylercipriani.com/thumbs/20/2a52c0ab8facc8cb6d6bb6f81fe137/medium.jpg\"\n           srcset=\"https://photos.tylercipriani.com/thumbs/20/2a52c0ab8facc8cb6d6bb6f81fe137/small.jpg 240w,\n                   https://photos.tylercipriani.com/thumbs/20/2a52c0ab8facc8cb6d6bb6f81fe137/medium.jpg 640w,\n                   https://photos.tylercipriani.com/thumbs/20/2a52c0ab8facc8cb6d6bb6f81fe137/large.jpg 1024w,\n                   https://photos.tylercipriani.com/thumbs/20/2a52c0ab8facc8cb6d6bb6f81fe137/larger.jpg 1200w\"\n           sizes=\"(max-width: 54em) 85vw, 50vw\"\n           alt=\"Chuwi Minibook X with banana for scale\"\n           loading=\"lazy\"\n           decoding=\"async\">\n    </picture>\n    <figcaption aria-hidden=\"true\">Chuwi Minibook X with \"banana\" for scale</figcaption>\n  </a>\n</figure>\n<p>The laptop’s case is MacBook-esque: aluminum and good-looking. The\nMacBook Air’s dimensions dwarf the Chuwi’s, but the two laptops are\nabout the same thickness.</p>\n<figure class=\"gallery\">\n  <a href=\"https://photos.tylercipriani.com/thumbs/47/64546616494468e5901dc5289f0de7/xlarge.jpg\">\n    <picture>\n      <source type=\"image/avif\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/47/64546616494468e5901dc5289f0de7/small.avif 240w,\n                      https://photos.tylercipriani.com/thumbs/47/64546616494468e5901dc5289f0de7/medium.avif 640w,\n                      https://photos.tylercipriani.com/thumbs/47/64546616494468e5901dc5289f0de7/large.avif 1024w,\n                      https://photos.tylercipriani.com/thumbs/47/64546616494468e5901dc5289f0de7/larger.avif 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 25vw\">\n      <source type=\"image/webp\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/47/64546616494468e5901dc5289f0de7/small.webp 240w,\n                      https://photos.tylercipriani.com/thumbs/47/64546616494468e5901dc5289f0de7/medium.webp 640w,\n                      https://photos.tylercipriani.com/thumbs/47/64546616494468e5901dc5289f0de7/large.webp 1024w,\n                      https://photos.tylercipriani.com/thumbs/47/64546616494468e5901dc5289f0de7/larger.webp 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 25vw\">\n      <img src=\"https://photos.tylercipriani.com/thumbs/47/64546616494468e5901dc5289f0de7/medium.jpg\"\n           srcset=\"https://photos.tylercipriani.com/thumbs/47/64546616494468e5901dc5289f0de7/small.jpg 240w,\n                   https://photos.tylercipriani.com/thumbs/47/64546616494468e5901dc5289f0de7/medium.jpg 640w,\n                   https://photos.tylercipriani.com/thumbs/47/64546616494468e5901dc5289f0de7/large.jpg 1024w,\n                   https://photos.tylercipriani.com/thumbs/47/64546616494468e5901dc5289f0de7/larger.jpg 1200w\"\n           sizes=\"(max-width: 54em) 85vw, 25vw\"\n           alt=\"Chuwi Minibook X alongside Macbook Air\"\n           loading=\"lazy\"\n           decoding=\"async\">\n    </picture>\n    <figcaption aria-hidden=\"true\">Chuwi Minibook X alongside the Macbook Air</figcaption>\n  </a>\n  <a href=\"https://photos.tylercipriani.com/thumbs/fc/9571e3d8763d1f635423a00a5ad549/xlarge.jpg\">\n    <picture>\n      <source type=\"image/avif\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/fc/9571e3d8763d1f635423a00a5ad549/small.avif 240w,\n                      https://photos.tylercipriani.com/thumbs/fc/9571e3d8763d1f635423a00a5ad549/medium.avif 640w,\n                      https://photos.tylercipriani.com/thumbs/fc/9571e3d8763d1f635423a00a5ad549/large.avif 1024w,\n                      https://photos.tylercipriani.com/thumbs/fc/9571e3d8763d1f635423a00a5ad549/larger.avif 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 25vw\">\n      <source type=\"image/webp\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/fc/9571e3d8763d1f635423a00a5ad549/small.webp 240w,\n                      https://photos.tylercipriani.com/thumbs/fc/9571e3d8763d1f635423a00a5ad549/medium.webp 640w,\n                      https://photos.tylercipriani.com/thumbs/fc/9571e3d8763d1f635423a00a5ad549/large.webp 1024w,\n                      https://photos.tylercipriani.com/thumbs/fc/9571e3d8763d1f635423a00a5ad549/larger.webp 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 25vw\">\n      <img src=\"https://photos.tylercipriani.com/thumbs/fc/9571e3d8763d1f635423a00a5ad549/medium.jpg\"\n           srcset=\"https://photos.tylercipriani.com/thumbs/fc/9571e3d8763d1f635423a00a5ad549/small.jpg 240w,\n                   https://photos.tylercipriani.com/thumbs/fc/9571e3d8763d1f635423a00a5ad549/medium.jpg 640w,\n                   https://photos.tylercipriani.com/thumbs/fc/9571e3d8763d1f635423a00a5ad549/large.jpg 1024w,\n                   https://photos.tylercipriani.com/thumbs/fc/9571e3d8763d1f635423a00a5ad549/larger.jpg 1200w\"\n           sizes=\"(max-width: 54em) 85vw, 25vw\"\n           alt=\"Chuwi Minibook X with banana for scale\"\n           loading=\"lazy\"\n           decoding=\"async\">\n    </picture>\n    <figcaption aria-hidden=\"true\">Chuwi Minibook atop the Macbook Air</figcaption>\n  </a>\n</figure>\n<blockquote>\n<p>A notebook that weighs more than a kilo is simply not a good\nthing</p>\n<p>– <a\nhref=\"https://techcrunch.com/2012/04/19/an-interview-with-millenium-technology-prize-finalist-linus-torvalds/\">Linus\nTorvalds</a></p>\n</blockquote>\n<p>The Minibook weighs in just shy of a kilo at 912 grams.</p>\n<figure>\n  <a href=\"https://photos.tylercipriani.com/thumbs/43/3dd984db05dddda91ec2f1e4691652/xlarge.jpg\">\n    <picture>\n      <source type=\"image/avif\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/43/3dd984db05dddda91ec2f1e4691652/small.avif 240w,\n                      https://photos.tylercipriani.com/thumbs/43/3dd984db05dddda91ec2f1e4691652/medium.avif 640w,\n                      https://photos.tylercipriani.com/thumbs/43/3dd984db05dddda91ec2f1e4691652/large.avif 1024w,\n                      https://photos.tylercipriani.com/thumbs/43/3dd984db05dddda91ec2f1e4691652/larger.avif 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 50vw\">\n      <source type=\"image/webp\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/43/3dd984db05dddda91ec2f1e4691652/small.webp 240w,\n                      https://photos.tylercipriani.com/thumbs/43/3dd984db05dddda91ec2f1e4691652/medium.webp 640w,\n                      https://photos.tylercipriani.com/thumbs/43/3dd984db05dddda91ec2f1e4691652/large.webp 1024w,\n                      https://photos.tylercipriani.com/thumbs/43/3dd984db05dddda91ec2f1e4691652/larger.webp 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 50vw\">\n      <img src=\"https://photos.tylercipriani.com/thumbs/43/3dd984db05dddda91ec2f1e4691652/medium.jpg\"\n           srcset=\"https://photos.tylercipriani.com/thumbs/43/3dd984db05dddda91ec2f1e4691652/small.jpg 240w,\n                   https://photos.tylercipriani.com/thumbs/43/3dd984db05dddda91ec2f1e4691652/medium.jpg 640w,\n                   https://photos.tylercipriani.com/thumbs/43/3dd984db05dddda91ec2f1e4691652/large.jpg 1024w,\n                   https://photos.tylercipriani.com/thumbs/43/3dd984db05dddda91ec2f1e4691652/larger.jpg 1200w\"\n           sizes=\"(max-width: 54em) 85vw, 50vw\"\n           alt=\"Chuwi Minibook X weights 912g\"\n           loading=\"lazy\"\n           decoding=\"async\"\n           style=\"filter: brightness(0.9);\"><!-- hard to read scale in darkmode -->\n    </picture>\n    <figcaption aria-hidden=\"true\">My Minibook X weighs 912g</figcaption>\n  </a>\n</figure>\n</section>\n<section id=\"perf-thermals-and-power\" class=\"level2\">\n<h2>Perf, thermals, and power</h2>\n<p>tl;dr: you get what you pay for. But battery life and cooling are\nbetter than I’d have guessed.</p>\n<p>The Minibook X was never going to compile the Linux kernel in record\ntime. But the performance matches the specs, it stays cool, and it has\nenough battery life to run a movie marathon.</p>\n<p>Numbers:</p>\n<ul>\n<li>Geekbench6 (a fun side-quest to get running on NixOS), better than I\n<a href=\"https://browser.geekbench.com/search?q=n150\">expected</a>.\n<ul>\n<li>Single-core: 1295</li>\n<li>Multi-core: 3332</li>\n</ul></li>\n<li>Wi-Fi 6 speed: 424 Mbps, more than enough to stream a 4K movie.</li>\n<li>Power\n<ul>\n<li>Idle: 3.8W</li>\n<li>During benchmark: ~15W</li>\n</ul></li>\n</ul>\n<p>Battery: When I left the 1995 classic film “Hackers” looping in VLC,\nthe battery lasted about 6 hours.</p>\n<p>Heat: Running <code>stress-ng</code> for 10 minutes, the hottest part\nof the laptop chassis remained below 90°F (32°C):</p>\n<figure>\n  <a href=\"https://photos.tylercipriani.com/thumbs/e1/ece6929d3378c0b9bff4e42c4f687d/xlarge.jpg\">\n    <picture>\n      <source type=\"image/avif\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/e1/ece6929d3378c0b9bff4e42c4f687d/small.avif 240w,\n                      https://photos.tylercipriani.com/thumbs/e1/ece6929d3378c0b9bff4e42c4f687d/medium.avif 640w,\n                      https://photos.tylercipriani.com/thumbs/e1/ece6929d3378c0b9bff4e42c4f687d/large.avif 1024w,\n                      https://photos.tylercipriani.com/thumbs/e1/ece6929d3378c0b9bff4e42c4f687d/larger.avif 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 50vw\">\n      <source type=\"image/webp\"\n              srcset=\"https://photos.tylercipriani.com/thumbs/e1/ece6929d3378c0b9bff4e42c4f687d/small.webp 240w,\n                      https://photos.tylercipriani.com/thumbs/e1/ece6929d3378c0b9bff4e42c4f687d/medium.webp 640w,\n                      https://photos.tylercipriani.com/thumbs/e1/ece6929d3378c0b9bff4e42c4f687d/large.webp 1024w,\n                      https://photos.tylercipriani.com/thumbs/e1/ece6929d3378c0b9bff4e42c4f687d/larger.webp 1200w\"\n              sizes=\"(max-width: 54em) 85vw, 50vw\">\n      <img src=\"https://photos.tylercipriani.com/thumbs/e1/ece6929d3378c0b9bff4e42c4f687d/medium.jpg\"\n           srcset=\"https://photos.tylercipriani.com/thumbs/e1/ece6929d3378c0b9bff4e42c4f687d/small.jpg 240w,\n                   https://photos.tylercipriani.com/thumbs/e1/ece6929d3378c0b9bff4e42c4f687d/medium.jpg 640w,\n                   https://photos.tylercipriani.com/thumbs/e1/ece6929d3378c0b9bff4e42c4f687d/large.jpg 1024w,\n                   https://photos.tylercipriani.com/thumbs/e1/ece6929d3378c0b9bff4e42c4f687d/larger.jpg 1200w\"\n           sizes=\"(max-width: 54em) 85vw, 50vw\"\n           alt=\"Chuwi Minibook X running stress-ng on thermal camera. Thermal camera reads 88.4°F\"\n           loading=\"lazy\"\n           decoding=\"async\">\n    </picture>\n    <figcaption aria-hidden=\"true\">Thermal camera view of Chuwi Minibook X running <code>stress-ng</code></figcaption>\n  </a>\n</figure>\n</section>\n<section id=\"what-i-dislike\" class=\"level2\">\n<h2>What I dislike</h2>\n<p>There’s so much to dislike about this laptop:</p>\n<ul>\n<li>Screen is terrible – 2K? 50Hz refresh rate? Why!?</li>\n<li>Keyboard is terrible – it only registers keystrokes when you hit the\nexact center of each key.</li>\n<li>Touchpad is terrible – It’s a diving board-style, without physical\nbuttons.</li>\n<li>Sound is meh – I can hear the tinny laptop speaker fine, but it’s\nunderwhelming. I’ve never tried tweaking it in Pipewire, though; it’s\npossible it could be better.</li>\n</ul>\n<p>But “terrible” is in comparison to the nicest modern laptops in\nexistence. Everything I listed here works fine. I’m honestly blown away\nwhen I tune my expectations to the sub-$400 laptop range.</p>\n</section>\n<section id=\"verdict\" class=\"level2\">\n<h2>Verdict</h2>\n<p>In <a\nhref=\"https://en.wikipedia.org/wiki/The_Death_and_Life_of_Great_American_Cities\"><em>The\nDeath and Life of Great American Cities</em></a>, Jane Jacobs wrote,\n“new ideas require old buildings”: cheap spaces let people try risky\nideas.</p>\n<p>The Chuwi Minibook X is an old building.</p>\n<p>I can brick the Minibook and have a normal Monday on my serious work\nlaptop. Nothing has to work, which makes it perfect to try out new Linux\ndesktop stuff:</p>\n<ul>\n<li>NixOS – I’ve been using Debian for 15 years+, figured I’d try\njoining the NixOS cult for a while.</li>\n<li>RiverWM – I’m on a quest to find the Wayland version of XMonad; <a\nhref=\"https://github.com/riverwm/river\">River</a> is pretty close.</li>\n<li>KDE Plasma – I’ve used a tiling window manager for over a decade.\nWhat’s it like to use a desktop that Just Works™?</li>\n<li>Steam – Never been much into games, but I decided to give Steam a\ntry since, well, why not?</li>\n</ul>\n<p>Cheap, weird computers like the Chuwi make it safe to play. And\nplaying with computers is still fun.</p>\n<figure>\n<video autoplay loop muted loading=\"lazy\"><source src=\"https://photos.tylercipriani.com/2026-05-19_chuwi-melatonin-smol.mp4\" type=\"video/mp4\"></video>\n<figcaption>Playing <a href=\"https://store.steampowered.com/app/1585220/Melatonin/\">Melatonin</a> on Steam on the Chuwi</figcaption>\n</figure>\n</section>\n"

},
{

    "id": "https://tylercipriani.com/blog/2026/04/24/on-the-software-supply-chain-doom-spiral/",

    "title": "GitHub Actions is a trap",
    "url": "https://tylercipriani.com/blog/2026/04/24/on-the-software-supply-chain-doom-spiral/",

    "author": {
        "name": "Tyler Cipriani"
    },


    "tags": [

     "computing"

    ],

    "date_published": "2026-04-24T20:54:04Z",
    "date_modified": "2026-05-22T23:58:08Z",


    "content_html": "<style>.title {text-wrap:balance;} #content > p:first-child {text-wrap:balance;}</style>\n<p>Hackers are pwning packages at an exhausting clip, and GitHub Actions\nfeatures in every postmortem.</p>\n<p>In late February, a hackerbot AI<a href=\"https://tylercipriani.com/blog/#fn1\" class=\"footnote-ref\"\nid=\"fnref1\" role=\"doc-noteref\"><sup>1</sup></a> yoinked the release key\nfor a single project. Within a month, fifty-ish other projects had cred\nstealers. Each infected repo swiped credentials for the next.</p>\n<p>This spate of supply-chain hacks started from a GitHub Actions trap;\na simple, bad default with a five-year-old proposed fix.</p>\n<section id=\"github-actions-and-consequences\" class=\"level2\">\n<h2>GitHub Actions and consequences</h2>\n<figure>\n<img\nsrc=\"https://photos.tylercipriani.com/thumbs/6f/f8ccb8fabb7987a2a2f8ae17e45d4f/large.jpg\"\nalt=\"Admiral Ackbar warning about the trap in GitHub Actions\" />\n<figcaption aria-hidden=\"true\">Admiral Ackbar warning about the trap in\nGitHub Actions</figcaption>\n</figure>\n<p>Trivy is an open-source security scanner. But if you used Trivy in\nlate March, you had a bad time.</p>\n<p>On March 19th, hackers pushed a version of Trivy that tried to\nsmuggle secrets from anywhere it ran. Trivy cited a “misconfiguration”\nin their continuous integration (CI) system, GitHub Actions.</p>\n<p>But the exploit was less a misconfiguration and more a GitHub Actions\ntrap.</p>\n<p>Here’s a simplified version of how Trivy got pwnd<a href=\"https://tylercipriani.com/blog/#fn2\"\nclass=\"footnote-ref\" id=\"fnref2\"\nrole=\"doc-noteref\"><sup>2</sup></a>:</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre\nclass=\"sourceCode yaml\"><code class=\"sourceCode yaml\"><span id=\"cb1-1\"><a href=\"https://tylercipriani.com/blog/#cb1-1\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"co\"># INSECURE. DO NOT USE.</span></span>\n<span id=\"cb1-2\"><a href=\"https://tylercipriani.com/blog/#cb1-2\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"fu\">on</span><span class=\"kw\">:</span></span>\n<span id=\"cb1-3\"><a href=\"https://tylercipriani.com/blog/#cb1-3\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"at\">  pull_request_target</span></span>\n<span id=\"cb1-4\"><a href=\"https://tylercipriani.com/blog/#cb1-4\" aria-hidden=\"true\" tabindex=\"-1\"></a></span>\n<span id=\"cb1-5\"><a href=\"https://tylercipriani.com/blog/#cb1-5\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"fu\">jobs</span><span class=\"kw\">:</span></span>\n<span id=\"cb1-6\"><a href=\"https://tylercipriani.com/blog/#cb1-6\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"at\">  </span><span class=\"fu\">check</span><span class=\"kw\">:</span></span>\n<span id=\"cb1-7\"><a href=\"https://tylercipriani.com/blog/#cb1-7\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"at\">    </span><span class=\"fu\">steps</span><span class=\"kw\">:</span></span>\n<span id=\"cb1-8\"><a href=\"https://tylercipriani.com/blog/#cb1-8\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"at\">      </span><span class=\"kw\">-</span><span class=\"at\"> </span><span class=\"fu\">uses</span><span class=\"kw\">:</span><span class=\"at\"> action/checkout@deadbeefdeadbeefdeadbeefdeadbeefdeadbeef</span></span>\n<span id=\"cb1-9\"><a href=\"https://tylercipriani.com/blog/#cb1-9\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"at\">        </span><span class=\"fu\">with</span><span class=\"kw\">:</span></span>\n<span id=\"cb1-10\"><a href=\"https://tylercipriani.com/blog/#cb1-10\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"at\">          </span><span class=\"fu\">ref</span><span class=\"kw\">:</span><span class=\"at\"> refs/pull/${{ github.event.pull_request.number }}/merge</span></span>\n<span id=\"cb1-11\"><a href=\"https://tylercipriani.com/blog/#cb1-11\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"at\">      </span><span class=\"kw\">-</span><span class=\"at\"> </span><span class=\"fu\">uses</span><span class=\"kw\">:</span><span class=\"at\"> ./.github/actions/setup-go</span></span>\n<span id=\"cb1-12\"><a href=\"https://tylercipriani.com/blog/#cb1-12\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"at\">      </span><span class=\"kw\">-</span><span class=\"at\"> </span><span class=\"fu\">uses</span><span class=\"kw\">:</span><span class=\"at\"> some/go-static-analysis@c0ffeec0ffeec0ffeec0ffeec0ffeec0ffeec0ff</span></span></code></pre></div>\n<p>At first glance, this code looks fine:</p>\n<ul>\n<li>No secrets referenced.</li>\n<li>Third-party actions pinned to an immutable hash.</li>\n<li>Check out a pull request. Perform some static analysis.</li>\n</ul>\n<p>But this code is a verbatim antipattern from a 2021 GitHub blog post\ntitled “<a\nhref=\"https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/\">preventing\npwn requests</a>”:</p>\n<blockquote>\n<p>if the <code>pull_request_target</code> workflow only […] runs\nuntrusted code but doesn’t reference any secrets, is it still\nvulnerable?</p>\n<p>Yes it is</p>\n<p>– <a\nhref=\"https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/\">GitHub\nSecurity Lab</a></p>\n</blockquote>\n<p>The problem is <code>pull_request_target</code>:</p>\n<ul>\n<li><code>pull_request_target</code> – plunks a nice, juicy\n<code>GITHUB_TOKEN</code> into the environment.</li>\n<li><code>actions/checkout</code> – takes an optional parameter\n<code>persist-credentials</code>, which removes secrets if set to\n<code>false</code>. But the default for the parameter is\n<code>true</code>.</li>\n</ul>\n<p>Setting the <code>persist-credentials</code> parameter to\n<code>false</code> has been an open issue in GitHub Actions <a\nhref=\"https://github.com/actions/checkout/issues/485\">since\n2021</a>.</p>\n</section>\n<section id=\"your-home-is-a-crime-scene\" class=\"level2\">\n<h2>Your <code>$HOME</code> is a crime scene</h2>\n<p>Once hackers had Trivy’s keys, they published a new version of Trivy\nto steal more keys.</p>\n<p>LiteLLM used Trivy in their CI. The same CI they used to publish code\nto PyPI, the Python software registry. When LiteLLM’s CI ran the\ncompromised Trivy, hackers nabbed their publishing key.</p>\n<p>And on March 24th, when Callum McMahon fired up his IDE, his MacBook\nfroze. And that’s how he discovered <a\nhref=\"https://futuresearch.ai/blog/no-prompt-injection-required/\">the\nLiteLLM hijack</a>.</p>\n<p>McMahon’s MacBook was flailing at bad code that hackers snuck into\nLiteLLM. And the bad code trying to steal credentials:</p>\n<ul>\n<li><code>~/.netrc</code></li>\n<li><code>~/.aws/credentials</code></li>\n<li><code>~/.config/gcloud</code></li>\n<li><code>~/.config/gh</code></li>\n<li><code>~/.azure</code></li>\n<li><code>~/.docker/config.json</code></li>\n<li><code>~/.npmrc</code></li>\n<li><code>~/.git-credentials</code></li>\n<li><code>~/.kube/</code></li>\n</ul>\n<p>Files that are typically strewn around <code>$HOME</code>\ndirectories, full of tokens and keys, often unencrypted.</p>\n</section>\n<section id=\"ai-and-the-supply-chain-doom-spiral\" class=\"level2\">\n<h2>AI and the supply chain doom spiral</h2>\n<p>We’ve dealt with problems like unencrypted credentials, unpinned\ndependencies, and CI footguns forever.</p>\n<p>But AI has accelerated <em>everything</em>, including repeating\nsecurity mistakes.</p>\n<p>On the day of the Trivy compromise, I asked Claude, “how do I scan\ndocker registry images for security vulnerabilities?”</p>\n<p>The reply, in part:</p>\n<pre><code>CI/CD Integration Example (GitHub Actions with Trivy)\n\n    - name: Scan image for vulnerabilities\n      uses: aquasecurity/trivy-action@master</code></pre>\n<p>Broken in two ways:</p>\n<ol type=\"1\">\n<li>Unpinned references – <code>master</code> is a reference that\nchanges all the time. If hackers zombify the repo, I’d be the first\nvictim.</li>\n<li>Active vulnerability – No mention whatsoever of the <a\nhref=\"https://nvd.nist.gov/vuln/detail/CVE-2026-33634\">CVE</a> posted\nthat day. I never asked, so Claude never checked.</li>\n</ol>\n<p>Meanwhile, Vercel’s CEO has attributed his company’s recent data\nbreach to a hacker that was “<a\nhref=\"https://nitter.net/rauchg/status/2045995362499076169\">accelerated\nby AI</a>.” And Anthropic’s latest hype tour includes <a\nhref=\"https://www.theguardian.com/technology/2026/apr/10/us-summoned-bank-bosses-to-discuss-cyber-risks-posed-by-anthropic-latest-ai-model\">briefing\nthe US Federal Reserve Chair</a> about vulnerabilities unearthed by\ntheir frontier model.</p>\n<p>Bad guys with LLMs get superpowers. Good guys with LLMs fall prey to\nmid-2010’s CI problems.</p>\n<p>And the same tool that can root out <a\nhref=\"https://red.anthropic.com/2026/mythos-preview/#ftnt_ref4\">27-year-old\nsecurity problems in OpenBSD</a>, will still tell you to pin your GitHub\nactions to <code>@master</code>.</p>\n</section>\n<section class=\"footnotes footnotes-end-of-document\"\nrole=\"doc-endnotes\">\n<hr />\n<ol>\n<li id=\"fn1\" role=\"doc-endnote\"><p>Or somone calling themselves\n<code>hackerbot-claw</code>, at any rate.<a href=\"https://tylercipriani.com/blog/#fnref1\"\nclass=\"footnote-back\" role=\"doc-backlink\">↩︎</a></p></li>\n<li id=\"fn2\" role=\"doc-endnote\"><p>My GitHub Actions example is a\nsimpler verison of the action removed in <a\nhref=\"https://github.com/aquasecurity/trivy/pull/10259\">aquasecurity/trivy\n#10259</a>.<a href=\"https://tylercipriani.com/blog/#fnref2\" class=\"footnote-back\"\nrole=\"doc-backlink\">↩︎</a></p></li>\n</ol>\n</section>\n"

},
{

    "id": "https://tylercipriani.com/blog/2025/08/15/git-lfs/",

    "title": "The future of large files in Git is Git",
    "url": "https://tylercipriani.com/blog/2025/08/15/git-lfs/",

    "author": {
        "name": "Tyler Cipriani"
    },


    "tags": [

     "computing"

    ],

    "date_published": "2025-08-15T19:55:33Z",
    "date_modified": "2025-08-15T20:05:00Z",


    "content_html": "<style>.title {text-wrap:balance;} #content > p:first-child {text-wrap:balance;}</style>\n<p>If Git had a nemesis, it’d be large files.</p>\n<p>Large files bloat Git’s storage, slow down <code>git clone</code>,\nand wreak havoc on Git forges.</p>\n<p>In 2015, GitHub released Git LFS—a Git extension that hacked around\nproblems with large files. But Git LFS added new complications and\nstorage costs.</p>\n<p>Meanwhile, the Git project has been quietly working on large files.\nAnd while LFS ain’t dead yet, the latest Git release shows the path\ntowards a future where LFS is, finally, obsolete.</p>\n<section\nid=\"what-you-can-do-today-replace-git-lfs-with-git-partial-clone\"\nclass=\"level2\">\n<h2>What you can do today: replace Git LFS with Git partial clone</h2>\n<p>Git LFS works by storing large files outside your repo.</p>\n<p>When you clone a project via LFS, you get the repo’s history and\nsmall files, but skip large files. Instead, Git LFS downloads only the\nlarge files you need for your working copy.</p>\n<p>In 2017, the Git project introduced <strong>partial clones</strong>\nthat provide the same benefits as Git LFS:</p>\n<blockquote>\n<p>Partial clone allows us to avoid downloading [large binary assets]\n<em>in advance</em> during clone and fetch operations and thereby reduce\ndownload times and disk usage.</p>\n<p>– Partial Clone Design Notes, <a\nhref=\"https://git-scm.com/docs/partial-clone\">git-scm.com</a></p>\n</blockquote>\n<p>Git’s partial clone and LFS both make for:</p>\n<ol type=\"1\">\n<li><strong>Small checkouts</strong> – On clone, you get the latest copy\nof big files instead of <strong>every</strong> copy.</li>\n<li><strong>Fast clones</strong> – Because you avoid downloading large\nfiles, each clone is fast.</li>\n<li><strong>Quick setup</strong> – Unlike shallow clones, you get the\nentire history of the project—you can get to work right away.</li>\n</ol>\n<p><strong>What is a partial clone?</strong></p>\n<p>A Git partial clone is a clone with a <code>--filter</code>.</p>\n<p>For example, to avoid downloading files bigger than 100KB, you’d\nuse:</p>\n<pre><code>git clone --filter=&#39;blobs:size=100k&#39; &lt;repo&gt;</code></pre>\n<p>Later, Git will lazily download any files over 100KB you need for\nyour checkout.</p>\n<p>By default, if I <code>git clone</code> a repo with many revisions of\na noisome 25 MB PNG file, then cloning is slow and the checkout is\nobnoxiously large:</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre\nclass=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb2-1\"><a href=\"https://tylercipriani.com/blog/#cb2-1\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">$</span> time git clone https://github.com/thcipriani/noise-over-git</span>\n<span id=\"cb2-2\"><a href=\"https://tylercipriani.com/blog/#cb2-2\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">Cloning</span> into <span class=\"st\">&#39;/tmp/noise-over-git&#39;</span>...</span>\n<span id=\"cb2-3\"><a href=\"https://tylercipriani.com/blog/#cb2-3\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">...</span></span>\n<span id=\"cb2-4\"><a href=\"https://tylercipriani.com/blog/#cb2-4\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">Receiving</span> objects: 100% <span class=\"er\">(</span><span class=\"ex\">153/153</span><span class=\"kw\">)</span><span class=\"ex\">,</span> 1.19 GiB</span>\n<span id=\"cb2-5\"><a href=\"https://tylercipriani.com/blog/#cb2-5\" aria-hidden=\"true\" tabindex=\"-1\"></a></span>\n<span id=\"cb2-6\"><a href=\"https://tylercipriani.com/blog/#cb2-6\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">real</span>    3m49.052s</span></code></pre></div>\n<p>Almost four minutes to check out a single 25MB file!</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre\nclass=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb3-1\"><a href=\"https://tylercipriani.com/blog/#cb3-1\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">$</span> du <span class=\"at\">--max-depth</span><span class=\"op\">=</span>0 <span class=\"at\">--human-readable</span> noise-over-git/.</span>\n<span id=\"cb3-2\"><a href=\"https://tylercipriani.com/blog/#cb3-2\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">1.3G</span>    noise-over-git/.</span>\n<span id=\"cb3-3\"><a href=\"https://tylercipriani.com/blog/#cb3-3\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">$</span> ^ 🤬</span></code></pre></div>\n<p>And 50 revisions of that single 25MB file eat 1.3GB of space.</p>\n<p>But a partial clone side-steps these problems:</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre\nclass=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb4-1\"><a href=\"https://tylercipriani.com/blog/#cb4-1\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">$</span> git config <span class=\"at\">--global</span> alias.pclone <span class=\"st\">&#39;clone --filter=blob:limit=100k&#39;</span></span>\n<span id=\"cb4-2\"><a href=\"https://tylercipriani.com/blog/#cb4-2\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">$</span> time git pclone https://github.com/thcipriani/noise-over-git</span>\n<span id=\"cb4-3\"><a href=\"https://tylercipriani.com/blog/#cb4-3\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">Cloning</span> into <span class=\"st\">&#39;/tmp/noise-over-git&#39;</span>...</span>\n<span id=\"cb4-4\"><a href=\"https://tylercipriani.com/blog/#cb4-4\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">...</span></span>\n<span id=\"cb4-5\"><a href=\"https://tylercipriani.com/blog/#cb4-5\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">Receiving</span> objects: 100% <span class=\"er\">(</span><span class=\"ex\">1/1</span><span class=\"kw\">)</span><span class=\"ex\">,</span> 24.03 MiB</span>\n<span id=\"cb4-6\"><a href=\"https://tylercipriani.com/blog/#cb4-6\" aria-hidden=\"true\" tabindex=\"-1\"></a></span>\n<span id=\"cb4-7\"><a href=\"https://tylercipriani.com/blog/#cb4-7\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">real</span>    0m6.132s</span>\n<span id=\"cb4-8\"><a href=\"https://tylercipriani.com/blog/#cb4-8\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">$</span> du <span class=\"at\">--max-depth</span><span class=\"op\">=</span>0 <span class=\"at\">--human-readable</span> noise-over-git/.</span>\n<span id=\"cb4-9\"><a href=\"https://tylercipriani.com/blog/#cb4-9\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">49M</span>     noise-over-git/</span>\n<span id=\"cb4-10\"><a href=\"https://tylercipriani.com/blog/#cb4-10\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">$</span> ^ 😻 <span class=\"er\">(</span><span class=\"ex\">the</span> same size as a git lfs checkout<span class=\"kw\">)</span></span></code></pre></div>\n<p>My filter made cloning 97% faster (3m 49s → 6s), and it reduced my\ncheckout size by 96% (1.3GB → 49M)!</p>\n<p>But there are still some caveats here.</p>\n<p>If you run a command that needs data you filtered out, Git will need\nto make a trip to the server to get it. So, commands like\n<code>git diff</code>, <code>git blame</code>, and\n<code>git checkout</code> will require a trip to your Git host to\nrun.</p>\n<p>But, for large files, this is the same behavior as Git LFS.</p>\n<p>Plus, I can’t remember the last time I ran <code>git blame</code> on\na PNG 🙃.</p>\n</section>\n<section id=\"why-go-to-the-trouble-whats-wrong-with-git-lfs\"\nclass=\"level2\">\n<h2>Why go to the trouble? What’s wrong with Git LFS?</h2>\n<p>Git LFS foists Git’s problems with large files onto users.</p>\n<p>And the problems are significant:</p>\n<ul>\n<li><strong>🖕 High vendor lock-in</strong> – When GitHub wrote Git LFS,\nthe other large file systems—Git Fat, Git Annex, and Git Media—were\nagnostic about the server-side. But GitHub locked users to their\nproprietary server implementation and charged folks to use it.<a\nhref=\"https://tylercipriani.com/blog/#fn1\" class=\"footnote-ref\" id=\"fnref1\"\nrole=\"doc-noteref\"><sup>1</sup></a></li>\n<li><strong>💸 Costly</strong> – GitHub won because it let users host\nrepositories for free. But Git LFS started as a paid product. Nowadays,\nthere’s a free tier, but you’re dependent on the whims of GitHub to set\npricing. Today, a 50GB repo on GitHub will cost $40/year for storage. In\ncontrast, storing 50GB on Amazon’s S3 standard storage is $13/year.</li>\n<li><strong>😰 Hard to undo</strong> – Once you’ve moved to Git LFS,\nit’s impossible to undo the move without rewriting history.</li>\n<li><strong>🌀 Ongoing set-up costs</strong> – All your collaborators\nneed to install Git LFS. Without Git LFS installed, your collaborators\nwill get confusing, metadata-filled text files instead of the large\nfiles they expect.</li>\n</ul>\n</section>\n<section id=\"the-future-git-large-object-promisors\" class=\"level2\">\n<h2>The future: Git large object promisors</h2>\n<p>Large files create problems for Git forges, too.</p>\n<p>GitHub and GitLab put limits on file size<a href=\"https://tylercipriani.com/blog/#fn2\"\nclass=\"footnote-ref\" id=\"fnref2\" role=\"doc-noteref\"><sup>2</sup></a>\nbecause big files cost more money to host. Git LFS keeps server-side\ncosts low by offloading large files to CDNs.</p>\n<p>But the Git project has a new solution.</p>\n<p>Earlier this year, Git merged a new feature: <strong>large object\npromisers</strong>. Large object promisors aim to provide the same\nserver-side benefits as LFS, minus the hassle to users.</p>\n<blockquote>\n<p>This effort aims to especially improve things on the server side, and\nespecially for large blobs that are already compressed in a binary\nformat.</p>\n<p>This effort aims to provide an alternative to Git LFS</p>\n<p>– Large Object Promisors, <a\nhref=\"https://git-scm.com/docs/large-object-promisors\">git-scm.com</a></p>\n</blockquote>\n<p><strong>What is a large object promisor?</strong></p>\n<p>Large object promisors are special Git remotes that only house large\nfiles.</p>\n<p>In the bright, shiny future, large object promisors will work like\nthis:</p>\n<ol type=\"1\">\n<li>You push a large file to your Git host.</li>\n<li>In the background, your Git host offloads that large file to a large\nobject promisor.</li>\n<li>When you clone, the Git host tells your Git client about the\npromisor.</li>\n<li>Your client will clone from the Git host, and automagically nab\nlarge files from the promisor remote.</li>\n</ol>\n<p>But we’re still a ways off from that bright, shiny future.</p>\n<p>Git large object promisors are still a work in progress. Pieces of\nlarge object promisors merged to Git in <a\nhref=\"https://lore.kernel.org/git/xmqqfrjfilc8.fsf@gitster.g/\">March of\n2025</a>. But there’s <a\nhref=\"https://gitlab.com/groups/gitlab-org/-/epics/9094\">more to do</a>\nand <a href=\"https://gitlab.com/groups/gitlab-org/-/epics/15972\">open\nquestions</a> yet to answer.</p>\n<p>And so, for today, you’re stuck with Git LFS for giant files. But\nonce large object promisors see broad adoption, maybe GitHub will let\nyou push files bigger than 100MB.</p>\n</section>\n<section id=\"the-future-of-large-files-in-git-is-git.\" class=\"level2\">\n<h2>The future of large files in Git is Git.</h2>\n<p>The Git project is thinking hard about large files, so you don’t have\nto.</p>\n<p>Today, we’re stuck with Git LFS.</p>\n<p>But soon, the only obstacle for large files in Git will be your\nhalf-remembered, ominous hunch that it’s a bad idea to stow your MP3\nlibrary in Git.</p>\n<hr style=\"margin-top: 3em;\" />\n<div style=\"margin: 1em auto;\">\n<p>Edited by <a href=\"https://refactoringenglish.com/\">Refactoring\nEnglish</a></p>\n</div>\n</section>\n<section class=\"footnotes footnotes-end-of-document\"\nrole=\"doc-endnotes\">\n<hr />\n<ol>\n<li id=\"fn1\" role=\"doc-endnote\"><p>Later, other Git forges made their <a\nhref=\"https://about.gitlab.com/blog/towards-a-production-quality-open-source-git-lfs-server/\">own\nLFS servers</a>. Today, you can push to multiple Git forges or use an\nLFS transfer agent, but all this makes set up harder for contributors.\nYou’re pretty much locked-in unless you put in extra effort to get\nunlocked.<a href=\"https://tylercipriani.com/blog/#fnref1\" class=\"footnote-back\"\nrole=\"doc-backlink\">↩︎</a></p></li>\n<li id=\"fn2\" role=\"doc-endnote\"><p>File size limits: <a\nhref=\"https://docs.github.com/en/repositories/working-with-files/managing-large-files/about-large-files-on-github\">100MB\nfor GitHub</a>, <a\nhref=\"https://docs.gitlab.com/user/gitlab_com/#account-and-limit-settings\">100MB\nfor GitLab.com</a><a href=\"https://tylercipriani.com/blog/#fnref2\" class=\"footnote-back\"\nrole=\"doc-backlink\">↩︎</a></p></li>\n</ol>\n</section>\n"

},
{

    "id": "https://tylercipriani.com/blog/2025/05/21/git-commits/",

    "title": "Per-project git commit templates",
    "url": "https://tylercipriani.com/blog/2025/05/21/git-commits/",

    "author": {
        "name": "Tyler Cipriani"
    },


    "tags": [

     "computing"

    ],

    "date_published": "2025-05-21T19:22:45Z",
    "date_modified": "2025-06-02T23:05:20Z",


    "content_html": "<blockquote>\n<p>People should try to compare the quality of the kernel git logs with\nsome other projects, and cry themselves to sleep.</p>\n<p>– Linus Torvalds</p>\n</blockquote>\n<p>I’ll never remember your project’s commit guidelines.</p>\n<p>Every project insists on something different:</p>\n<ul>\n<li><a\nhref=\"https://www.conventionalcommits.org/en/v1.0.0/\">Conventional\ncommits</a></li>\n<li><a\nhref=\"https://zeromq.org/how-to-contribute/#write-good-commit-messages\">Problem/Solution\nformat</a></li>\n<li><a href=\"https://gitmoji.dev/\">Gitmoji</a></li>\n<li>The twisty maze of <a\nhref=\"https://docs.kernel.org/process/submitting-patches.html#when-to-use-acked-by-cc-and-co-developed-by\">trailers\nin the Linux Kernel</a></li>\n</ul>\n<p>But <a\nhref=\"https://git-scm.com/docs/git-config#Documentation/git-config.txt-codecommittemplatecode\">git\ncommit templates</a> help. Commit templates provide a scaffold for\ncommit messages, offering documentation where you need it: inside the\neditor where you’re writing your commit message.</p>\n<section id=\"what-is-a-git-commit-template\" class=\"level2\">\n<h2>What is a git commit template?</h2>\n<p>When you type <code>git commit</code>, git pops open your text\neditor<a href=\"https://tylercipriani.com/blog/#fn1\" class=\"footnote-ref\" id=\"fnref1\"\nrole=\"doc-noteref\"><sup>1</sup></a>. Git can pre-fill your editor with a\ncommit template—it’s like a form you fill out.</p>\n<p>Creating a commit template is simple.</p>\n<ul>\n<li>Create a plaintext file – mine lives at\n<code>~/.config/git/message.txt</code></li>\n<li>Tell git to use it:</li>\n</ul>\n<div class=\"sourceCode\" id=\"cb1\"><pre\nclass=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb1-1\"><a href=\"https://tylercipriani.com/blog/#cb1-1\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"fu\">git</span> config <span class=\"at\">--global</span> <span class=\"dt\">\\</span></span>\n<span id=\"cb1-2\"><a href=\"https://tylercipriani.com/blog/#cb1-2\" aria-hidden=\"true\" tabindex=\"-1\"></a>    commit.template <span class=\"st\">&#39;~/.config/git/message.txt&#39;</span></span></code></pre></div>\n<p><a\nhref=\"https://gist.github.com/thcipriani/5e79a11b69b66a493249bac1439bfe02\">My\ndefault template</a> packs everything I know about writing a commit.</p>\n</section>\n<section id=\"project-specific-templates-with-includeif\" class=\"level2\">\n<h2>Project-specific templates with <code>IncludeIf</code></h2>\n<p>The real magic of commit templates is you can have different\ntemplates for each project.</p>\n<p>Different projects can use different templates with git’s\n<code>includeIf</code> configuration setting.<a href=\"https://tylercipriani.com/blog/#fn2\"\nclass=\"footnote-ref\" id=\"fnref2\" role=\"doc-noteref\"><sup>2</sup></a></p>\n<p>Large projects, such as <a\nhref=\"https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/SubmittingPatches?id=4e8a2372f9255a1464ef488ed925455f53fbdaa1\">the\nLinux kernel</a>, <a\nhref=\"https://git-scm.com/docs/SubmittingPatches#describe-changes\">git</a>,\nand <a\nhref=\"https://www.mediawiki.org/wiki/Gerrit/Commit_message_guidelines\">MediaWiki</a>,\nhave their own commit guidelines.</p>\n<p>For Wikimedia work, I stow git repos in\n<code>~/Projects/Wikimedia</code> and at the bottom of my global git\nconfig (<code>~/.config/git/config</code>) I have:</p>\n<pre><code>[includeIf &quot;gitdir:~/Projects/Wikimedia/**&quot;]\n    path = ~/.config/git/config.wikimedia</code></pre>\n<p>In <code>config.wikimedia</code>, I point to my Wikimedia-specific\ncommit template. I also override other git config settings like my\n<code>user.email</code> or <code>core.hooksPath</code>.</p>\n</section>\n<section id=\"an-example-my-global-template\" class=\"level2\">\n<h2>An example: my global template</h2>\n<p>My default commit template contains three sections:</p>\n<ol type=\"1\">\n<li>Subject – 50 characters or less, capitalized, no end\npunctuation.</li>\n<li>Body – Wrap at 72 characters with a blank line separating it from\nthe subject.</li>\n<li>Trailers – Standard formats with a blank line separating them from\nthe body.</li>\n</ol>\n<p>In each section, I added pointers for both format<a href=\"https://tylercipriani.com/blog/#fn3\"\nclass=\"footnote-ref\" id=\"fnref3\" role=\"doc-noteref\"><sup>3</sup></a> and\ncontent.</p>\n<p>For the header, the guidance is quick:</p>\n<pre><code>\n# 50ch. wide ----------------------------- SUBJECT\n#                                                |\n#     &quot;If applied, this commit will...&quot;          |\n#                                                |\n#     Change / Add / Fix                         |\n#     Remove / Update / Document                 |\n#                                                |\n# ------- ↓ LEAVE BLANK LINE ↓ ---------- /SUBJECT\n</code></pre>\n<p>For the body, I remind myself to answer basic questions:</p>\n<pre><code># 72ch. wide ------------------------------------------------------ BODY\n#                                                                      |\n#     - Why should this change be made?                                |\n#       - What problem are you solving?                                |\n#       - Why this solution?                                           |\n#     - What&#39;s wrong with the current code?                            |\n#     - Are there other ways to do it?                                 |\n#     - How can the reviewer confirm it works?                         |\n#                                                                      |</code></pre>\n<p>And that’s it, except for git trailers.</p>\n</section>\n<section id=\"the-twisty-maze-of-git-trailers\" class=\"level2\">\n<h2>The twisty maze of git trailers</h2>\n<p>My template has a section for trailers used by the projects I work\non.</p>\n<pre><code>#     TRAILERS                                                         |\n#     --------                                                         |\n#     (optional) Uncomment as needed.                                  |\n#     Leave a blank line before the trailers.                          |\n#                                                                      |\n# Bug: #xxxx\n# Acked-by: Example User &lt;user@example.com&gt;\n# Cc: Example User &lt;user@example.com&gt;\n# Co-Authored-by: Example User &lt;user@example.com&gt;\n# Requested-by: Example User &lt;user@example.com&gt;\n# Reported-by: Example User &lt;user@example.com&gt;\n# Reviewed-by: Example User &lt;user@example.com&gt;\n# Suggested-by: Example User &lt;user@example.com&gt;\n# Tested-by: Example User &lt;user@example.com&gt;\n# Thanks: Example User &lt;user@example.com&gt;</code></pre>\n<p>These trailers serve as useful breadcrumbs of documentation. Git can\nparse them using standard commands.</p>\n<p>For example, if I wanted a tab-separated list of commits and their\nrelated tasks, I could find <code>Bug</code> trailers using\n<code>git log</code>:</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre\nclass=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb6-1\"><a href=\"https://tylercipriani.com/blog/#cb6-1\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">$</span> TAB=%x09</span>\n<span id=\"cb6-2\"><a href=\"https://tylercipriani.com/blog/#cb6-2\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">$</span> BUG_TRAILER=<span class=\"st\">&#39;%(trailers:key=Bug,valueonly=true,separator=%x2C )&#39;</span></span>\n<span id=\"cb6-3\"><a href=\"https://tylercipriani.com/blog/#cb6-3\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">$</span> SHORT_HASH=%h</span>\n<span id=\"cb6-4\"><a href=\"https://tylercipriani.com/blog/#cb6-4\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">$</span> SUBJ=%s</span>\n<span id=\"cb6-5\"><a href=\"https://tylercipriani.com/blog/#cb6-5\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">$</span> FORMAT=<span class=\"st\">&quot;</span><span class=\"va\">${SHORT_HASH}${TAB}${BUG_TRAILER}${TAB}${SUBJ}</span><span class=\"st\">&quot;</span></span>\n<span id=\"cb6-6\"><a href=\"https://tylercipriani.com/blog/#cb6-6\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">$</span> git log <span class=\"at\">--topo-order</span> <span class=\"at\">--no-merges</span> <span class=\"dt\">\\</span></span>\n<span id=\"cb6-7\"><a href=\"https://tylercipriani.com/blog/#cb6-7\" aria-hidden=\"true\" tabindex=\"-1\"></a>      <span class=\"at\">--format</span><span class=\"op\">=</span><span class=\"st\">&quot;</span><span class=\"va\">$FORMAT</span><span class=\"st\">&quot;</span></span>\n<span id=\"cb6-8\"><a href=\"https://tylercipriani.com/blog/#cb6-8\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">d2b09deb12f</span>     T359762 Rewrite Kurdish <span class=\"er\">(</span><span class=\"ex\">ku</span><span class=\"kw\">)</span> <span class=\"ex\">Latin</span> to Arabic converter</span>\n<span id=\"cb6-9\"><a href=\"https://tylercipriani.com/blog/#cb6-9\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">28123a6a262</span>     T332865 tests: Remove non-static fallback in HookRunnerTestBase</span>\n<span id=\"cb6-10\"><a href=\"https://tylercipriani.com/blog/#cb6-10\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">4e919a307a4</span>     T328919 tests: Remove unused argument from data provider in PageUpdaterTest</span>\n<span id=\"cb6-11\"><a href=\"https://tylercipriani.com/blog/#cb6-11\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">bedd0f685f9</span>             objectcache: Improve <span class=\"kw\">`</span><span class=\"fu\">RESTBagOStuff::handleError()</span><span class=\"kw\">`</span></span>\n<span id=\"cb6-12\"><a href=\"https://tylercipriani.com/blog/#cb6-12\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"ex\">2182a0c4490</span>     T393219 tests: Remove two data provider in RestStructureTest</span></code></pre></div>\n</section>\n<section id=\"stop-remembering-commit-message-guidelines\" class=\"level2\">\n<h2>Stop remembering commit message guidelines</h2>\n<p>Git commit templates free your brain from remembering what to write,\nallowing you to focus on the story you need to tell.</p>\n<p>Save your brain for what it’s good at.</p>\n</section>\n<section class=\"footnotes footnotes-end-of-document\"\nrole=\"doc-endnotes\">\n<hr />\n<ol>\n<li id=\"fn1\" role=\"doc-endnote\"><p>Starting with\n<code>core.editor</code> in your git config, <code>$VISUAL</code> or\n<code>$EDITOR</code> in your shell, finally falling back to\n<code>vi</code>.<a href=\"https://tylercipriani.com/blog/#fnref1\" class=\"footnote-back\"\nrole=\"doc-backlink\">↩︎</a></p></li>\n<li id=\"fn2\" role=\"doc-endnote\"><p>You could also set it inside a repo’s\n<code>.git/config</code>, <code>includeIf</code> is useful if you have\nmultiple repos with the same standards under one directory.<a\nhref=\"https://tylercipriani.com/blog/#fnref2\" class=\"footnote-back\" role=\"doc-backlink\">↩︎</a></p></li>\n<li id=\"fn3\" role=\"doc-endnote\"><p>All cribbed from <a\nhref=\"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html\">Tim\nPope</a><a href=\"https://tylercipriani.com/blog/#fnref3\" class=\"footnote-back\"\nrole=\"doc-backlink\">↩︎</a></p></li>\n</ol>\n</section>\n"

},
{

    "id": "https://tylercipriani.com/blog/2025/03/05/boox-go-10-3-review/",

    "title": "Boox Go 10.3, two months in",
    "url": "https://tylercipriani.com/blog/2025/03/05/boox-go-10-3-review/",

    "author": {
        "name": "Tyler Cipriani"
    },


    "tags": [

     "computing"

    ],

    "date_published": "2025-03-05T03:41:36Z",
    "date_modified": "2025-03-13T23:11:42Z",


    "content_html": "<blockquote>\n<p>[The] Linux kernel uses GPLv2, and if you distribute GPLv2 code, you\nhave to provide a copy of the source (and modifications) <em>once\nsomeone asks for it</em>. And now I’m asking nicely for you to do so\n🙂</p>\n<p>– Joga, <a\nhref=\"https://archive.is/HP2Qk\">bbs.onyx-international.com</a></p>\n</blockquote>\n<figure>\n<img\nsrc=\"https://photos.tylercipriani.com/thumbs/01/0c363d3da2755ee421a393d908b697/large.jpg\"\nalt=\"Boox in split screen, typewriter mode\" />\n<figcaption aria-hidden=\"true\">Boox in split screen, typewriter\nmode</figcaption>\n</figure>\n<p>In January, I bought a Boox Go 10.3—a 10.3-inch, 300-ppi, e-ink\nAndroid tablet.</p>\n<p>After two months, I use the Boox daily—it’s replaced my planner,\nnotebook, countless PDF print-offs, and the good parts of my phone.</p>\n<p>But Boox’s parent company, Onyx, is sketchy.</p>\n<p>I’m conflicted. The Boox Go is a beautiful, capable tablet that I use\nevery day, but I recommend avoiding as long as Onyx continues to\ndisregard the rights of its users.</p>\n<section id=\"how-im-using-my-boox\" class=\"level2\">\n<h2>How I’m using my Boox</h2>\n<figure>\n<img\nsrc=\"https://photos.tylercipriani.com/thumbs/f3/63eee65c63696d95c13156cd9f67ab/large.jpg\"\nalt=\"My e-ink floor desk\" />\n<figcaption aria-hidden=\"true\">My e-ink floor desk</figcaption>\n</figure>\n<p>Each morning, I plop down in front of my <a\nhref=\"https://www.amazon.com/MagicHold-Rotating-Height-Adjusting-13-15-6/dp/B01MG1EWPQ?th=1\">MagicHold</a>\nlaptop stand and journal on my Boox with Obsidian.</p>\n<p>I use Syncthing to back up my planner and sync my <a\nhref=\"https://www.zotero.org/\">Zotero</a> library between my Boox and\nlaptop.</p>\n<p>In the evening, I review my <a\nhref=\"https://mirzakhani.io/products/zen-planner-2025\">PDF planner</a>\nand plot for tomorrow.</p>\n<p>I use these apps:</p>\n<ul>\n<li><strong>Obsidian</strong> – a markdown editor that syncs between all\nmy devices with no fuss for $8/mo.</li>\n<li><strong>Syncthing</strong> – I love Syncthing—it’s an encrypted,\ncontinuous file sync-er without a centralized server.</li>\n<li><strong>Meditation apps</strong><a href=\"https://tylercipriani.com/blog/#fn1\" class=\"footnote-ref\"\nid=\"fnref1\" role=\"doc-noteref\"><sup>1</sup></a> – Guided meditation away\nfrom the blue light glow of my phone or computer is better.</li>\n</ul>\n<p>Before buying the Boox, I considered a reMarkable.</p>\n<p>The <a\nhref=\"https://remarkable.com/store/remarkable-paper/pro\">reMarkable\nPaper Pro</a> has a beautiful color screen with a frontlight, a nice\npen, and a “<a\nhref=\"https://remarkable.com/store/remarkable-2/type-folio\">type\nfolio</a>,” plus it’s <a\nhref=\"https://www.calmtech.institute/products\">certified by the Calm\nTech Institute</a>.</p>\n<p>But the reMarkable is a distraction-free e-ink tablet. Meanwhile, I\nneed <em>distraction-lite</em>.</p>\n</section>\n<section id=\"what-i-like\" class=\"level2\">\n<h2>What I like</h2>\n<ul>\n<li><strong>Calm(ish) technology</strong> – The Boox is an intentional\ndevice. Browsing the internet, reading emails, and watching videos is\nhard, but that’s good.</li>\n<li><strong>Apps</strong> – Google Play works out of the box. I can\ninstall F-Droid and change my launcher without difficulty.</li>\n<li><strong>Split screen</strong> – The built-in launcher has a split\nscreen feature. I use it to open a PDF side-by-side with a notes\ndoc.</li>\n<li><strong>Reading</strong> – The screen is a 300ppi Carta 1200, making\ntext crisp and clear.</li>\n</ul>\n</section>\n<section id=\"what-i-dislike\" class=\"level2\">\n<h2>What I dislike</h2>\n<figure>\n<img\nsrc=\"https://photos.tylercipriani.com/thumbs/10/733dfc26554fb3363483cf306f2d5a/large.jpg\"\nalt=\"I filmed myself typing at 240fps, each frame is 4.17ms. Boox’s typing latency is between 150ms and 275ms at the fastest refresh rate inside Obsidian.\" />\n<figcaption aria-hidden=\"true\">I filmed myself typing at 240fps, each\nframe is 4.17ms. Boox’s typing latency is between 150ms and 275ms at the\nfastest refresh rate inside Obsidian.</figcaption>\n</figure>\n<ul>\n<li><strong>Typing</strong> – Typing latency is noticeable.\n<ul>\n<li>At Boox’s highest refresh rate, after hitting a key, text takes\nbetween 150ms to 275ms to appear.</li>\n<li>I can still type, though it’s distracting at times.</li>\n</ul></li>\n</ul>\n<figure>\n<img\nsrc=\"https://photos.tylercipriani.com/thumbs/67/b6fd836c59e5ecd6aa429d5fda4ab6/large.jpg\"\nalt=\"The horror of the default pen\" />\n<figcaption aria-hidden=\"true\">The horror of the default\npen</figcaption>\n</figure>\n<ul>\n<li><strong>Accessories</strong>\n<ul>\n<li><strong>Pen</strong> – The default pen looks like a child’s\nwhiteboard marker and feels cheap. I replaced it with the <a\nhref=\"https://www.amazon.com/Amazon-Kindle-Scribe-Premium-Improved/dp/B0D2FBN1W8/\">Kindle\nScribe Premium pen</a>, and the writing experience is vastly\nimproved.</li>\n<li><strong>Cover</strong> – It’s impossible to find a nice cover. I’m\nusing a $15 cover that I’m encasing in stickers.</li>\n</ul></li>\n<li><strong>Tool switching</strong> – Swapping between apps is slow and\nclunky. I blame Android and the current limitations of e-ink more than\nBoox.</li>\n<li><strong>No frontlight</strong> – The Boox’s lack of frontlight\nprevents me from reading more with it. I knew this when I bought my\nBoox, but devices with frontlights seem to make other compromises.</li>\n</ul>\n<p><strong>Onyx</strong></p>\n<p>The Chinese company behind Boox, Onyx International, Inc., runs the\nservers where the Boox routes telemetry. I block this traffic with <a\nhref=\"https://pi-hole.net/\">Pi-Hole</a><a href=\"https://tylercipriani.com/blog/#fn2\"\nclass=\"footnote-ref\" id=\"fnref2\"\nrole=\"doc-noteref\"><sup>2</sup></a>.</p>\n<figure>\n<img\nsrc=\"https://photos.tylercipriani.com/2025-03-04_boox-telemetry.png\"\nalt=\"pihole-ing whatever telemetry Boox collects\" />\n<figcaption aria-hidden=\"true\">pihole-ing whatever telemetry Boox\ncollects</figcaption>\n</figure>\n<p>I inspected this traffic via <a href=\"https://mitmproxy.org/\">Mitm\nproxy</a>—most traffic was benign, though I never opted into sending any\ntelemetry (nor am I logged in to a Boox account). But it’s also an\nAndroid device, so it’s feeding telemetry into Google’s gaping maw,\ntoo.</p>\n<p>Worse, <a href=\"https://archive.is/HP2Qk\">Onyx is flouting the terms\nof the GNU Public License</a>, declining to release Linux kernel\nmodifications to users. This is anathema to me—<a\nhref=\"https://www.gnu.org/licenses/gpl-violation.en.html\">GPL\nviolations</a> are tantamount to theft.</p>\n<p>Onyx’s disregard for user rights makes me regret buying the Boox.</p>\n</section>\n<section id=\"verdict\" class=\"level2\">\n<h2>Verdict</h2>\n<p>I’ll continue to use the Boox and feel bad about it. I hope my\ndigging in this post will help the next person.</p>\n<p>Unfortunately, the e-ink tablet market is too niche to support the\nkind of solarpunk future I’d always imagined.</p>\n<p>But there’s an opportunity for an open, Linux-based tablet to\ndominate e-ink. Linux is playing catch-up on phones with PostmarketOS.\nMeanwhile, the best e-ink tablets have to offer are old, unupdateable\nversions of Android, like the OS on the Boox.</p>\n<p>In the future, I’d love to pay a license- and privacy-respecting\ncompany for beautiful, calm technology and recommend their product to\neveryone. But today is not the future.</p>\n</section>\n<section class=\"footnotes footnotes-end-of-document\"\nrole=\"doc-endnotes\">\n<hr />\n<ol>\n<li id=\"fn1\" role=\"doc-endnote\"><p>I go back and forth between “Waking\nUp” and “Calm”<a href=\"https://tylercipriani.com/blog/#fnref1\" class=\"footnote-back\"\nrole=\"doc-backlink\">↩︎</a></p></li>\n<li id=\"fn2\" role=\"doc-endnote\"><p>Using <a\nhref=\"https://github.com/JordanEJ/Onyx-Boox-Blocklist\">github.com/JordanEJ/Onyx-Boox-Blocklist</a><a\nhref=\"https://tylercipriani.com/blog/#fnref2\" class=\"footnote-back\" role=\"doc-backlink\">↩︎</a></p></li>\n</ol>\n</section>\n"

}

    ]
}
