{"version":"https://jsonfeed.org/version/1.1","title":"Nikolay Bryskin","home_page_url":"https://zxczxc.dev/","feed_url":"https://zxczxc.dev/feed.json","description":"Notes on systems, Linux, and engineering","language":"en","items":[{"id":"https://zxczxc.dev/posts/mouse-over-wifi/","url":"https://zxczxc.dev/posts/mouse-over-wifi/","title":"Mouse over WiFi","language":"en","content_html":"<p>Today I found that I needed to connect a USB mouse to my laptop — and both\nUSB-C ports were occupied, one by the monitor, one by power. Turns out it's\nnot a huge deal on Linux if you have another PC nearby.</p>\n<p>The reasonable fix is a €3 USB-C adapter. But the adapter is three days of\nshipping away, and the other PC — a little Rockchip ARM board that lives on\nmy desk — was right there, with free USB-A ports and a network connection to\nmy laptop. On Linux, &quot;the mouse is plugged into the wrong computer&quot; is not\nreally a hardware problem. It's a routing problem.</p>\n<!--more-->\n<h2 id=\"pick-your-layer\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/mouse-over-wifi/#pick-your-layer\" class=\"header-anchor\">Pick your layer</a></h2>\n<p>There are two ways to ship a USB device across a network:</p>\n<p><strong>Forward the USB device itself</strong> — that's <code>usbip</code>, which is in the mainline\nkernel. The remote host sees a real USB device, URBs get shuttled over TCP,\nand everything that could talk to the physical device can talk to the remote\none. That's the right tool when something needs the <em>device</em> — firmware\nflashers, smartcard readers, license dongles.</p>\n<p><strong>Forward the input events</strong> — a mouse is not an interesting USB device. By\nthe time applications see it, it's just a stream of tiny <code>input_event</code>\nstructs coming out of <code>/dev/input/eventN</code>: relative X, relative Y, button,\nsync. The kernel will happily accept the same structs back through\n<code>/dev/uinput</code> and synthesize a new input device out of them. So instead of\ntunneling USB, you can read events on one machine and replay them on the\nother. Less protocol, less latency sensitivity, and it works regardless of\nwhat's on the other end — Wayland, X11, a bare console.</p>\n<p>For a mouse, events win. The tool for this is\n<a href=\"https://github.com/Blub/netevent\">netevent</a> — a ~130 KB binary with two\nrelevant subcommands: <code>cat</code> (open an event device, dump its events to\nstdout) and <code>create</code> (read events from stdin, create a uinput clone). It's\n<code>netevent-git</code> in the AUR.</p>\n<h2 id=\"the-five-minute-version\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/mouse-over-wifi/#the-five-minute-version\" class=\"header-anchor\">The five-minute version</a></h2>\n<p>Find the mouse on the machine it's plugged into. <code>/dev/input/by-id/</code> gives\nyou stable names, unlike <code>eventN</code> which reshuffles on replug:</p>\n<pre><code class=\"language-console\">$ ls -l /dev/input/by-id/\nlrwxrwxrwx 1 root root  9 platform-noserial-if01-event-mouse -&gt; ../event8\nlrwxrwxrwx 1 root root 13 usb-Logitech_USB_Receiver-hidraw -&gt; ../../hidraw0\n</code></pre>\n<p>Then, from the laptop, one pipe:</p>\n<pre><code class=\"language-console\">$ ssh zxc-pc 'sudo netevent cat /dev/input/by-id/platform-noserial-if01-event-mouse' \\\n    | netevent create\n</code></pre>\n<p>That's genuinely all of it. <code>netevent cat</code> grabs the device exclusively\n(<code>EVIOCGRAB</code> — the server machine stops seeing the mouse), streams events\nover the SSH connection, and <code>netevent create</code> conjures a virtual &quot;Logitech\nUSB Receiver Mouse&quot; on the laptop that the desktop picks up like any\nhotplugged device. The cursor moves. SSH gives you encryption and auth for\nfree.</p>\n<p>I used exactly this for about an hour. Then the sysadmin brain kicked in,\nbecause the pipe has problems:</p>\n<ul>\n<li>it needs <code>sudo</code> on the server (<code>/dev/input/*</code> is root:input),</li>\n<li>it dies silently on suspend, roaming, or a Wi-Fi blip, and takes the\nmouse with it,</li>\n<li>it drags my interactive SSH config — agent forwarding, a GPG socket\nRemoteForward — into what should be a dumb transport,</li>\n<li>and it plants a landmine that I would step on two hours later. We'll get\nthere.</li>\n</ul>\n<h2 id=\"the-grown-up-version%3A-socket-activation\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/mouse-over-wifi/#the-grown-up-version%3A-socket-activation\" class=\"header-anchor\">The grown-up version: socket activation</a></h2>\n<p>What I actually want is an appliance: the server <em>exports</em> the mouse,\npermanently and cheaply; the laptop <em>attaches</em> when it wants to; the grab —\nthe mouse going dead on the server — should last exactly as long as the\nattachment. That maps one-to-one onto systemd socket activation:</p>\n<p><img src=\"https://zxczxc.dev/uploads/mouse-over-wifi/architecture.svg\" alt=\"Events flow down through zxc-pc — receiver, /dev/input/event8, netevent cat — across a Tailscale-only TCP link on port 8763, then up through the laptop — socat, netevent create, /dev/uinput — into a virtual mouse\"></p>\n<p>Two unit files on the server. The socket:</p>\n<pre><code class=\"language-ini\"># /etc/systemd/system/zxc-mouse.socket\n[Unit]\nDescription=Export Logitech mouse over Tailscale (netevent)\n\n[Socket]\nListenStream=8763\nBindToDevice=tailscale0\nAccept=yes\nMaxConnections=1\n\n[Install]\nWantedBy=sockets.target\n</code></pre>\n<p>And the per-connection service it spawns:</p>\n<pre><code class=\"language-ini\"># /etc/systemd/system/zxc-mouse@.service\n[Unit]\nDescription=netevent mouse stream to %i\n\n[Service]\nExecStart=/usr/bin/netevent cat /dev/input/by-id/platform-noserial-if01-event-mouse\nStandardInput=socket\nStandardOutput=socket\nStandardError=journal\nDynamicUser=yes\nSupplementaryGroups=input\n</code></pre>\n<p>Almost every line here is doing security or lifecycle work:</p>\n<ul>\n<li><strong><code>Accept=yes</code></strong> is inetd mode: each TCP connection spawns a fresh\ninstance with the connection as stdin/stdout. <code>netevent cat</code> doesn't know\nthe network exists — it writes to &quot;stdout&quot; and systemd has already wired\nthat to the socket.</li>\n<li><strong><code>BindToDevice=tailscale0</code></strong> scopes the listener with <code>SO_BINDTODEVICE</code>:\nconnections arriving via any other interface never reach it. The mouse is\nreachable over the tailnet and nothing else — no LAN exposure, and\nTailscale brings the encryption and authentication that raw TCP doesn't.</li>\n<li><strong><code>MaxConnections=1</code></strong> — a second client gets refused instead of fighting\nover the grab.</li>\n<li><strong><code>DynamicUser=yes</code> + <code>SupplementaryGroups=input</code></strong> is my favorite part.\nThe prototype needed <code>sudo</code> for exactly one reason: reading\n<code>/dev/input/event8</code>, which is <code>root:input 0660</code>. But that's not a <em>root</em>\nproblem, it's a <em>group</em> problem — so let systemd mint a throwaway user\nthat exists only for the lifetime of the connection and holds exactly one\nprivilege: the <code>input</code> group. No sudo rules, no long-lived root process,\nno real user to keep in the group forever (permanent <code>input</code> membership\nis a standing keylogger license — every process you run can read every\nkeystroke).</li>\n</ul>\n<p>The lifecycle falls out of the socket activation for free:</p>\n<p><img src=\"https://zxczxc.dev/uploads/mouse-over-wifi/sequence.svg\" alt=\"Sequence diagram: laptop connects over the tailnet to port 8763, zxc-pc spawns an instance which grabs the mouse; events stream into the uinput clone; on disconnect the instance exits and the grab is released\"></p>\n<p>Nobody is grabbing anything while no one is attached. The mouse is a shared\nresource with connection-scoped ownership, which is exactly the semantics\nyou'd want and exactly what an always-running daemon wouldn't give you.</p>\n<p>On the laptop, the attaching end is one more unit:</p>\n<pre><code class=\"language-ini\"># /etc/systemd/system/zxc-mouse.service\n[Unit]\nDescription=Attach Logitech mouse exported by zxc-pc\n\n[Service]\nExecStart=/bin/sh -c 'socat -u TCP:zxc-pc:8763,keepalive,keepidle=5,keepintvl=5,keepcnt=2 STDOUT | netevent create --duplicates=resume'\nRestart=always\nRestartSec=2\nRestartSteps=8\nRestartMaxDelaySec=60\nDynamicUser=yes\nSupplementaryGroups=input\n</code></pre>\n<p>Same trick on this side — <code>netevent create</code> needs <code>/dev/uinput</code>, which is\nalso <code>root:input 0660</code>, so the same throwaway-user-plus-one-group pattern\ncovers it. <code>socat</code> instead of <code>nc</code> because of the keepalive knobs: a mouse\nstream is silent when the mouse is idle, so without keepalives a dead peer\nlooks identical to a boring evening. With <code>keepidle=5,keepintvl=5,keepcnt=2</code>\na vanished server is detected in ~15 seconds, the pipe exits, and\n<code>Restart=always</code> walks the backoff (2 s → 60 s) until the server is back.</p>\n<p>One deliberate omission: this unit has <strong>no <code>[Install]</code> section</strong>. It cannot\nbe enabled at boot, even by accident. <code>systemctl start zxc-mouse</code> is the new\n&quot;plug in the mouse&quot;; <code>systemctl stop zxc-mouse</code> is unplugging it — and\nhanding it back to the machine it's physically attached to.</p>\n<p>Did the interface scoping actually work? Two checks:</p>\n<pre><code class=\"language-console\">$ ssh zxc-pc ss -tln | grep 8763\nLISTEN 0  4096  *%tailscale0:8763  *:*        # the % is SO_BINDTODEVICE\n\n$ nc -z -w3 192.168.88.247 8763               # via LAN: refused\n$ systemctl start zxc-mouse &amp;&amp; systemctl is-active zxc-mouse\nactive                                         # via tailnet: streaming\n</code></pre>\n<h2 id=\"the-landmine\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/mouse-over-wifi/#the-landmine\" class=\"header-anchor\">The landmine</a></h2>\n<p>I promised a landmine. First start of the polished new service:</p>\n<pre><code>sh[1510469]: error: error while expecting hello packet: Success\n</code></pre>\n<p>The client connects, expects netevent's protocol hello, and gets EOF —\nreported with errno 0, the error message equivalent of a shrug. The server\njournal has the real story:</p>\n<pre><code>netevent[61925]: error: failed to grab input device: Device or resource busy\n</code></pre>\n<p>Something already held the exclusive grab. It was the prototype — the SSH\npipe from two hours earlier. I had killed my end of it, but the remote\n<code>netevent cat</code> had no idea: it only touches its dead socket when the mouse\n<em>moves</em>, and the mouse, grabbed and therefore inert, had not moved. A\nprocess whose only job is to report motion, kept alive by the absence of\nmotion, holding the grab forever. The new service died against it on every\nretry.</p>\n<p>One <code>pkill</code> on the server later, the service came up and stayed up.</p>\n<h2 id=\"was-it-worth-it%3F\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/mouse-over-wifi/#was-it-worth-it%3F\" class=\"header-anchor\">Was it worth it?</a></h2>\n<p>Latency over the LAN (Tailscale negotiates a direct path between machines\non the same network) is imperceptible — mouse events are a few dozen bytes\neach, and the event clock is carried in the protocol. The whole thing cost\nthree unit files and one 130 KB tool, runs with no root process and no sudo\nrule on either machine, exposes one port on one virtual interface, and\ngives the mouse back the moment I stop the service.</p>\n<p>The dongle is still not here. The mouse never found out it's being\nforwarded.</p>\n","tags":["linux","systemd","networking","tailscale"],"date_published":"2026-07-24T00:00:00.000Z","image":"https://zxczxc.dev/uploads/mouse-over-wifi/cover.jpg"},{"id":"https://zxczxc.dev/posts/why-ai-agents-are-bad-at-code-refactoring/","url":"https://zxczxc.dev/posts/why-ai-agents-are-bad-at-code-refactoring/","title":"AI agents are bad at code refactoring","language":"en","content_html":"<p>Give an AI agent an empty directory and a page of requirements, and it will impress you. Give the same agent an existing codebase and a one-line feature request, and there's a good chance it will leave the project slightly worse while fully satisfying the request.</p>\n<!--more-->\n<p>And &quot;existing&quot; no longer means old. In the agentic era a codebase can reach that state — drifted design, blurred patterns, obsolete docs — in a matter of weeks, sometimes days, often with an agent as the original author. The archaeology now has to happen on sites that are still under construction.</p>\n<p>I want to walk through why I think this happens, and why I suspect it's not a temporary weakness.</p>\n<h2 id=\"two-forward-passes\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/why-ai-agents-are-bad-at-code-refactoring/#two-forward-passes\" class=\"header-anchor\">Two forward passes</a></h2>\n<p>When an agent builds a project from scratch, the pipeline is:</p>\n<p><strong>requirements → design → code</strong></p>\n<p>The user states what is needed. The agent converts requirements into a software design, picking patterns that fit — it has seen thousands. Then it converts the design into code, step by step.</p>\n<p>Both arrows point the same way, and both are <em>forward passes</em> — and humanity has documented them exhaustively. Every architecture book, every pattern catalog, every tutorial, every open-source project is a record of someone moving in this direction. A model trained on public text has effectively watched this pipeline run millions of times. It's not surprising that agents are good at it. It would be surprising if they weren't.</p>\n<h2 id=\"refactoring-runs-the-pipeline-backwards\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/why-ai-agents-are-bad-at-code-refactoring/#refactoring-runs-the-pipeline-backwards\" class=\"header-anchor\">Refactoring runs the pipeline backwards</a></h2>\n<p><img src=\"https://zxczxc.dev/uploads/why-ai-agents-are-bad-at-code-refactoring/a6wju0m2pyp8fc2yixfx.jpg\" alt=\"Mental Gymnastics meme: &quot;Writing new code&quot; over gymnasts calmly standing on a mat; &quot;Refactoring an existing codebase&quot; over athletes doing extreme acrobatics off a burning van\"></p>\n<p>Now the opposite situation. The agent is dropped into an existing project. What it holds is the pipeline's <em>output</em> — the code. The inputs are exactly what's missing:</p>\n<ul>\n<li>The design has drifted; the code no longer matches any design that was ever drawn.</li>\n<li>The patterns are blurred by local, not-so-clean fixes and workarounds.</li>\n<li>The documentation and comments describe a system that no longer exists.</li>\n</ul>\n<p>The engineer's task — human or AI — becomes:</p>\n<ol>\n<li>Recover the <em>actual</em> current requirements (they've drifted from whatever the docs claim).</li>\n<li>Discern the real patterns inside the noisy code.</li>\n<li>For each anomaly, figure out <em>why</em> it exists: legacy? An unfinished plan? A past mistake? A requirement that moved?</li>\n<li>Correct the requirements picture based on what the code investigation revealed.</li>\n<li>Decide what design would be ideal for the corrected requirements.</li>\n<li>Plan a path from the current code state to the target state.</li>\n<li>Write the code.</li>\n</ol>\n<p>Look at the direction of these steps. Steps 1–4 run backwards: from code to design, from design to requirements. Steps 5 and 7 are the familiar forward passes — the easy, well-trained part. Step 6 is neither: it's plotting a route through a live system that has to keep working at every intermediate state — changing the tires while the car is moving.</p>\n<p>And the backward direction has a problem the forward one doesn't: it is <em>underdetermined</em>. Many different pairs of (requirements, design) could have produced the code in front of you. The forward pass loses information; running it backwards means guessing what was lost. The only thing that disambiguates the guesses is history. And while git preserves the <em>what</em> perfectly — it's all there in the repo — the <em>why</em> mostly isn't: it lived in review comments, chat threads, and the heads of people who have moved on.</p>\n<h2 id=\"step-zero%3A-the-smell\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/why-ai-agents-are-bad-at-code-refactoring/#step-zero%3A-the-smell\" class=\"header-anchor\">Step zero: the smell</a></h2>\n<p>Worse, you rarely know in advance that refactoring is needed. It is almost never a ticket. You sit down to add a feature or fix a bug, and every implementation you sketch <em>smells</em>: each one fights the current design, each needs two workarounds to fit. The decision to refactor is the recognition of that smell — noticing a mismatch between the design you have and the requirement that just arrived.</p>\n<p>This is a background process, not a step. A human engineer runs it constantly, motivated by their own future suffering: the person who will maintain this mess is them. An agent's episode ends when the diff lands. Nothing in its loop rewards stopping to question the design; if anything, the training signal runs the other way — &quot;sure, here's your feature&quot; reads as helpful, &quot;we should restructure first&quot; reads as evasive. The agent never has to live in the house it builds.</p>\n<h2 id=\"refactoring-is-a-compression-task\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/why-ai-agents-are-bad-at-code-refactoring/#refactoring-is-a-compression-task\" class=\"header-anchor\">Refactoring is a compression task</a></h2>\n<p>Strip it down and refactoring is, first of all, <em>compression</em>. Given tens of thousands of lines of code, extract the internal structure. And not only the current structure — also the historical one, because step 3 (<em>why is this here?</em>) is a question about time, not about code.</p>\n<p>Two compressions, then. The first — what the design currently is — is hard, but at least the input fits in a context window. The second is harder in principle: the rationale was never written down, or was written down and has rotted along with the docs. If the code was written by an agent, the problem inverts rather than disappears: the <em>why</em> is on record in exhausting detail — but uncompressed, too voluminous to load, and impossible to tell which of it is still true. The agent is asked to decompress a lossy archive with the dictionary thrown away.</p>\n<p>What a good human engineer brings here is a <em>prior</em> — and note what the prior is over. Not over patterns, but over <em>decay</em>. Having watched hundreds of codebases evolve, they can look at a warped shape and reconstruct the erosion: this was once a clean separation, then a deadline happened, then a hotfix happened. Their mental library includes the degraded versions of every pattern, because they have personally watched the degradation happen.</p>\n<p>Models are trained mostly on snapshots — final states, clean tutorials, curated open source. Their library holds the pristine versions. So faced with drifted code, a model does what it was trained to do: it matches the mess to the nearest clean shape and confidently describes the codebase as the textbook version of whatever it most resembles. But <em>which</em> clean shape, if any, this mess descended from is precisely the question that was supposed to be investigated, not assumed away. Pattern-matching the noise to a pristine archetype is guessing at the story instead of recovering it.</p>\n<h2 id=\"what-agents-do-instead\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/why-ai-agents-are-bad-at-code-refactoring/#what-agents-do-instead\" class=\"header-anchor\">What agents do instead</a></h2>\n<p><img src=\"https://zxczxc.dev/uploads/why-ai-agents-are-bad-at-code-refactoring/y2wrftm3gcxbqhqh6nxe.jpg\" alt=\"Drake meme: disapproving at &quot;Refactor the workaround&quot;, approving at &quot;Wrap it in an abstraction layer&quot;\"></p>\n<p>Deprived of the story, agents default to greenfield style: take the existing code as ground truth, assume everything in it is intentional, and build the new thing on top. A new abstraction here, a new entity there. Nothing is ever deleted; each session adds a stratum. The result is geological: layers on layers, each written as if everything below it were load-bearing.</p>\n<p>If you've watched an agent work on a mature project, you've seen this: it doesn't refactor the workaround — it <em>respects</em> it. Wraps it, generalizes it, builds on it. The workaround becomes architecture.</p>\n<p>One clarification, because it causes a lot of confusion: agents are quite good at the <em>mechanics</em> of refactoring. Tell one &quot;extract this logic into a module, keep behavior identical, update all call sites&quot; and it will do a decent job. This fools people. Fowler's catalog — extract method, move field, rename — is the last mile of refactoring. The hard part was never executing the moves; it's deciding that moves are needed, which ones, and toward what design. Judgment, not mechanics. Agents pass the mechanics test, and it tells us nothing about the judgment.</p>\n<h2 id=\"the-economics-flip\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/why-ai-agents-are-bad-at-code-refactoring/#the-economics-flip\" class=\"header-anchor\">The economics flip</a></h2>\n<p><img src=\"https://zxczxc.dev/uploads/why-ai-agents-are-bad-at-code-refactoring/xzr5i8inse89tciscjl1.jpg\" alt=\"Vince McMahon reaction meme, escalating excitement: ship a hotfix / fix it properly / refactor the codebase / rewrite from scratch\"></p>\n<p>So here is my suspicion: maybe with AI it is simply cheaper to throw the code away and regenerate it. Look at what a rewrite actually involves:</p>\n<ol>\n<li>Extract the requirements — explicit and implicit.</li>\n<li>Design against them.</li>\n<li>Write the code.</li>\n</ol>\n<p>That's the greenfield pipeline with one pre-step attached. No smell detection, no archaeology, no step-6 migration plan — most of the seven-step backward pipeline simply disappears. Mass production killed the repair shop for exactly this reason: when manufacturing a new unit costs less than diagnosing the old one, nobody fixes anything. Code generation is heading toward free. Code <em>diagnosis</em> is not.</p>\n","tags":["ai","refactoring","programming","softwareengineering"],"date_published":"2026-07-23T00:00:00.000Z","image":"https://zxczxc.dev/uploads/why-ai-agents-are-bad-at-code-refactoring/cover-lis3cjixcg02u0l36n8n.jpg"},{"id":"https://zxczxc.dev/posts/kilobyte-utxo-set/","url":"https://zxczxc.dev/posts/kilobyte-utxo-set/","title":"The Kilobyte UTXO Set: Utreexo, SwiftSync, and Bitcoin's state problem","language":"en","content_html":"<p>Every Bitcoin node drags a 12 GB database through its whole life just to answer one question: does this coin exist? This article — published as an interactive edition — shows how Utreexo compresses that answer into about thirty hashes. Add coins, watch equal-height trees carry-merge, and spend them with Merkle proofs, right in the page.</p>\n<p><img src=\"https://zxczxc.dev/uploads/kilobyte-utxo-set/playground.gif\" alt=\"Playground recording: coins added, trees merging, a Merkle proof spend\"></p>\n<p><a href=\"https://zxczxc.dev/posts/kilobyte-utxo-set/\">Open the interactive edition</a></p>\n","tags":["bitcoin","cryptography","datastructures"],"date_published":"2026-07-16T00:00:00.000Z","image":"https://zxczxc.dev/uploads/kilobyte-utxo-set/cover-bfjad0ppv1wiq78l2c2p.png"},{"id":"https://zxczxc.dev/posts/no-gnome-keyring-daemon/","url":"https://zxczxc.dev/posts/no-gnome-keyring-daemon/","title":"How to stop gnome-keyring-daemon prompts","language":"en","content_html":"<p>In Gnome there is gnome-keyring-daemon that should be unlocked to provide access to keyrings. By default it unlocks using login password,\nbut if you use fingerprint or face unlock then it will prompt you to enter password every time any process try to access keyring.\nFor my setup it's 6 times after login and 2 times when I start the browser.</p>\n<!--more-->\n<p>Package removal does not work because it's a dependency of many other packages.\nWhat actually works is to remove all ways of starting gnome-keyring-daemon like so</p>\n<pre><code>mkdir -p ~/.local/share/dbus-1/services\ncp /usr/share/dbus-1/services/{org.freedesktop.impl.portal.Secret.service,org.freedesktop.secrets,org.gnome.keyring.service}.service ~/.local/share/dbus-1/services\n# replace last line with &quot;Exec=/usr/bin/true&quot;\n\ncp /etc/xdg/autostart/gnome-keyring-{pkcs11,secrets,ssh}.desktop ~/.config/autostart\nfor f in ~/.config/autostart/gnome-keyring-*.desktop; do echo &quot;Hidden=true&quot; &gt;&gt; $f; done\n</code></pre>\n<p>BTW, may be it's simpler to <code>sudo chmod -x /usr/bin/gnome-keyring-daemon</code></p>\n","tags":["linux","gnome"],"date_published":"2022-05-24T00:00:00.000Z"},{"id":"https://zxczxc.dev/posts/github-pages/","url":"https://zxczxc.dev/posts/github-pages/","title":"How to make a personal blog on GitHub Pages (with Google Analytics)","language":"en","content_html":"<p>Create regular Github Pages repo, then edit <code>_config.yaml</code>:</p>\n<pre><code class=\"language-yaml\">google_analytics: G-XXX # https://support.google.com/analytics/answer/9306384\nremote_theme: jekyll/minima\nplugins:\n- jekyll-remote-theme\nminima:\n  skin: dark\n</code></pre>\n<!--more-->\n<p>Thanks to https://github.com/jekyll/minima/issues/561#issuecomment-748793956</p>\n","tags":["jekyll","github-pages"],"date_published":"2022-05-09T00:00:00.000Z"},{"id":"https://zxczxc.dev/posts/tucana/","url":"https://zxczxc.dev/posts/tucana/","title":"How to install Magisk on Xiaomi Mi Note 10 (tucana)","language":"en","content_html":"<ol>\n<li>unlock bootloader</li>\n<li>install <a href=\"https://github.com/topjohnwu/Magisk/releases/latest\">MagiskManager</a></li>\n<li>download <a href=\"https://eu.dl.twrp.me/tucana/\">TWRP</a></li>\n<li>enable USB debugging and connect adb</li>\n</ol>\n<!--more-->\n<ol>\n<li>reboot to fastboot <code>adb reboot bootloader</code></li>\n<li>boot to TWRP <code>fastboot boot twrp.img</code></li>\n<li>after booting twrp decrypt /sdcard using your pin/pattern</li>\n<li>connect to device using adb <code>adb shell</code></li>\n<li>copy boot partition to sdcard <code>dd if=/dev/block/bootdevice/by-name/boot of=/sdcard/boot.img</code></li>\n<li>reboot to system <code>adb reboot</code></li>\n<li>launch MagiskManager</li>\n<li>press &quot;Install&quot; in &quot;Magisk&quot; card</li>\n<li>uncheck &quot;Recovery Mode&quot;</li>\n<li>press next</li>\n<li>select boot.img file on the sdcard root</li>\n<li>after successeful patch pull patched boot image to pc <code>adb pull /sdcard/Download/magisk_patched-*.img ./</code></li>\n<li>reboot to bootloader again <code>adb reboot bootloader</code></li>\n<li>flash patched boot image <code>fastboot flash boot magisk_patched-*.img</code></li>\n<li>reboot to system <code>fastboot reboot</code></li>\n<li>profit!</li>\n</ol>\n","tags":["android","magisk"],"date_published":"2022-05-06T00:00:00.000Z"},{"id":"https://zxczxc.dev/posts/lnd-node/","url":"https://zxczxc.dev/posts/lnd-node/","title":"How to create an lnd node ready for accepting payments","language":"en","content_html":"<h2 id=\"install-lnd\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/lnd-node/#install-lnd\" class=\"header-anchor\">Install lnd</a></h2>\n<pre><code>yay -S lnd\n</code></pre>\n<!--more-->\n<h2 id=\"configure\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/lnd-node/#configure\" class=\"header-anchor\">Configure</a></h2>\n<pre><code>mkdir ~/.lnd\ncat &lt;&lt;EOF\nbitcoin.active=true\nbitcoin.mainnet=true\nbitcoin.node=neutrino\nfeeurl=https://nodes.lightning.computer/fees/v1/btc-fee-estimates.json\nno-rest-tls=true\nEOF &gt; ~/.lnd/lnd.conf\n</code></pre>\n<h2 id=\"create-wallet\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/lnd-node/#create-wallet\" class=\"header-anchor\">Create wallet</a></h2>\n<pre><code>lnd &amp;\nlncli create\n# pass password here and save seed phrase afterwards\nkill %1\n</code></pre>\n<h2 id=\"setup-autounlock-and-start-lnd\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/lnd-node/#setup-autounlock-and-start-lnd\" class=\"header-anchor\">Setup autounlock and start lnd</a></h2>\n<pre><code>echo &quot;&lt;password-for-the-wallet&gt;&quot; &gt; ~/.lnd/wallet.password\necho &quot;wallet-unlock-password-file=~/.lnd/wallet.password&quot; &gt;&gt; ~/.lnd/lnd.conf\nlnd &amp;\n</code></pre>\n<h2 id=\"obtain-inbound-liquidity\" tabindex=\"-1\"><a href=\"https://zxczxc.dev/posts/lnd-node/#obtain-inbound-liquidity\" class=\"header-anchor\">Obtain inbound liquidity</a></h2>\n<ul>\n<li>go to https://lnbig.com</li>\n<li>initiate the payment</li>\n<li>provide node id <code>lncli getinfo | jq -r .identity_pubkey</code></li>\n<li>use NodeID@Host:Port to connect to the node <code>lncli connect &lt;addr&gt;</code></li>\n<li>wait for on-chain confirmation</li>\n</ul>\n","tags":["bitcoin","lightning","linux"],"date_published":"2022-05-03T00:00:00.000Z"}]}