main
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta name="viewport" content="width=device-width, initial-scale=1">
5 <meta charset="utf-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1">
7 <title>
8 TIL — Today I Learned
9 </title>
10 <meta name="author" content="Vincent Demeester">
11 <meta name="generator" content="Org Mode">
12 <script src="/flux.js" defer=""></script>
13 <link rel="stylesheet" href="/style.css" type="text/css">
14 </head>
15 <body>
16 <div class="site-controls">
17 <button class="theme-toggle" id="theme-toggle" title="Toggle dark/light mode">☀️</button>
18 </div>
19 <article id="content" class="content">
20 <header>
21 <h1 class="title">
22 TIL — Today I Learned
23 </h1>
24 </header>
25 <p>
26 Short things I learned, discovered, or found interesting.
27 </p>
28 <section id="outline-container-github-is-a-cve-numbering-authority" class="outline-2">
29 <h2 id="github-is-a-cve-numbering-authority">
30 <a href="#github-is-a-cve-numbering-authority" class="anchor">§</a>GitHub is a CVE Numbering Authority<span class="tags"><a href="/flux/tags/github.html" class="tag">github</a><a href="/flux/tags/security.html" class="tag">security</a><a href="/flux/tags/opensource.html" class="tag">opensource</a></span>
31 </h2>
32 <div id="text-github-is-a-cve-numbering-authority" class="outline-text-2">
33 <p>
34 <span class="timestamp-wrapper"><a class="timestamp-link" href="/flux/2026.html"><time class="timestamp" datetime="2026-03-12">2026-03-12</time></a></span>
35 </p>
36 <p>
37 GitHub can assign CVE IDs for any GitHub-hosted project. Most OSS projects don't need to
38become their own CNA.
39 </p>
40 <p>
41 The full flow stays within GitHub:
42 </p>
43 <ol class="org-ol">
44 <li>
45 Someone submits a Private Vulnerability Report (PVR)
46 </li>
47 <li>
48 Accepting it creates a draft security advisory <i>you own</i>
49 </li>
50 <li>
51 Create a private fork from the advisory for the fix
52 </li>
53 <li>
54 Merge + publish is atomic: GHSA entry, CVE to MITRE/NVD, Dependabot alerts — all at once
55 </li>
56 </ol>
57 <div class="callout callout-note" id="org7f16d57">
58 <div class="callout-title">
59 Note
60 </div>
61 <p>
62 Private forks from advisories do <i>not</i> run GitHub Actions. Test locally or use a separate CI environment.
63 </p>
64 </div>
65 <div class="callout callout-tip" id="org99c01cb">
66 <div class="callout-title">
67 Tip
68 </div>
69 <p>
70 Request the CVE early (it stays "reserved"), develop the fix privately, then go public all at once: merge → publish advisory → tag release → announce.
71 </p>
72 </div>
73 </div>
74 </section>
75 <section id="outline-container-fail2ban-push-notifications-self-dos" class="outline-2">
76 <h2 id="fail2ban-push-notifications-self-dos">
77 <a href="#fail2ban-push-notifications-self-dos" class="anchor">§</a>Fail2ban + push notifications = self-DoS<span class="tags"><a href="/flux/tags/homelab.html" class="tag">homelab</a><a href="/flux/tags/fail2ban.html" class="tag">fail2ban</a><a href="/flux/tags/nixos.html" class="tag">nixos</a></span>
78 </h2>
79 <div id="text-fail2ban-push-notifications-self-dos" class="outline-text-2">
80 <p>
81 <span class="timestamp-wrapper"><a class="timestamp-link" href="/flux/2026.html"><time class="timestamp" datetime="2026-03-02">2026-03-02</time></a></span>
82 </p>
83 <p>
84 Push notification services (ntfy, Gotify) behind a reverse proxy with fail2ban create a
85self-DoS loop:
86 </p>
87 <ol class="org-ol">
88 <li>
89 Persistent subscribers maintain long-lived connections
90 </li>
91 <li>
92 Any disruption (Caddy reload, auth blip) triggers reconnection bursts
93 </li>
94 <li>
95 Fail2ban sees the burst → bans your IP
96 </li>
97 <li>
98 Auto-unban → instant re-ban (subscriber retries immediately)
99 </li>
100 <li>
101 <code>bantime-increment</code> makes each cycle worse
102 </li>
103 </ol>
104 <p>
105 The fix: exclude the notification service from fail2ban entirely:
106 </p>
107 <div class="org-src-container">
108 <pre class="src src-ini"><code># In caddy-auth filter
109ignoreregex = ^.*"host":"ntfy\.sbr\.pm".*$
110</code></pre>
111 </div>
112 <div class="callout callout-warning" id="org7925b9e">
113 <div class="callout-title">
114 Warning
115 </div>
116 <p>
117 <code>ignoreIP</code> doesn't help if your IP changes (travel, mobile). The service itself must be excluded from the filter.
118 </p>
119 </div>
120 </div>
121 </section>
122 <section id="outline-container-usb-autosuspend-can-cascade-across-shared-xhci-controllers" class="outline-2">
123 <h2 id="usb-autosuspend-can-cascade-across-shared-xhci-controllers">
124 <a href="#usb-autosuspend-can-cascade-across-shared-xhci-controllers" class="anchor">§</a>USB autosuspend can cascade across shared xHCI controllers<span class="tags"><a href="/flux/tags/linux.html" class="tag">linux</a><a href="/flux/tags/hardware.html" class="tag">hardware</a><a href="/flux/tags/nixos.html" class="tag">nixos</a></span>
125 </h2>
126 <div id="text-usb-autosuspend-can-cascade-across-shared-xhci-controllers" class="outline-text-2">
127 <p>
128 <span class="timestamp-wrapper"><a class="timestamp-link" href="/flux/2026.html"><time class="timestamp" datetime="2026-02-27">2026-02-27</time></a></span>
129 </p>
130 <p>
131 Intermittent network drops on my laptop's USB ethernet adapter (Realtek r8152) turned out
132to be caused by a <b>webcam</b> (Logitech C920) on the same USB controller. USB autosuspend on
133the webcam triggered controller resets that cascaded to the ethernet adapter.
134 </p>
135 <p>
136 Devices on different logical USB buses can still share a physical xHCI controller.
137 </p>
138 <p>
139 Fix via udev rule in NixOS:
140 </p>
141 <div class="org-src-container">
142 <pre class="src src-nix"><code><span class="org-nix-attribute">services.udev.extraRules</span> = <span class="org-string">''
143 ACTION=="add", SUBSYSTEM=="usb", \
144 ATTR{idVendor}=="046d", ATTR{idProduct}=="082d", \
145 ATTR{power/control}="on"
146''</span>;
147</code></pre>
148 </div>
149 <div class="callout callout-tip" id="org7f6f53b">
150 <div class="callout-title">
151 Tip
152 </div>
153 <p>
154 When debugging USB issues, check <code>dmesg</code> for <code>xhci_hcd</code> reset messages. The culprit device may be on a completely different logical bus than the affected one.
155 </p>
156 </div>
157 </div>
158 </section>
159 <section id="outline-container-remote-nixos-install-don-t-bind-mount-over-nix-store" class="outline-2">
160 <h2 id="remote-nixos-install-don-t-bind-mount-over-nix-store">
161 <a href="#remote-nixos-install-don-t-bind-mount-over-nix-store" class="anchor">§</a>Remote NixOS install: don't bind-mount over <code>/nix/store</code><span class="tags"><a href="/flux/tags/nixos.html" class="tag">nixos</a><a href="/flux/tags/disko.html" class="tag">disko</a></span>
162 </h2>
163 <div id="text-remote-nixos-install-don-t-bind-mount-over-nix-store" class="outline-text-2">
164 <p>
165 <span class="timestamp-wrapper"><a class="timestamp-link" href="/flux/2026.html"><time class="timestamp" datetime="2026-02-12">2026-02-12</time></a></span>
166 </p>
167 <p>
168 When installing NixOS from a live USB, <code>/nix/store</code> is an overlayfs with a tmpfs upper
169layer (RAM-backed). If the closure is too large, you might be tempted to bind-mount the
170target disk's store over <code>/nix/store</code>.
171 </p>
172 <p>
173 <b>Don't.</b> It hides all system binaries and breaks the installer.
174 </p>
175 <p>
176 Instead, resize the tmpfs:
177 </p>
178 <div class="org-src-container">
179 <pre class="src src-shell"><code>mount -o remount,<span class="org-variable-name">size</span>=28G /nix/.rw-store
180</code></pre>
181 </div>
182 <p>
183 Or better: build the closure on a remote machine and <code>nix copy</code> it over.
184 </p>
185 <div class="callout callout-note" id="orgde0f8b8">
186 <div class="callout-title">
187 Note
188 </div>
189 <p>
190 The live USB's <code>/nix/store</code> has a read-only squashfs lower layer (<code>.ro-store</code>) and a tmpfs upper layer (<code>.rw-store</code>). Default tmpfs is ~50% of RAM.
191 </p>
192 </div>
193 </div>
194 </section>
195 <section id="outline-container-paperless-let-it-auto-detect-don-t-optimize" class="outline-2">
196 <h2 id="paperless-let-it-auto-detect-don-t-optimize">
197 <a href="#paperless-let-it-auto-detect-don-t-optimize" class="anchor">§</a>Paperless: let it auto-detect, don't "optimize"<span class="tags"><a href="/flux/tags/homelab.html" class="tag">homelab</a><a href="/flux/tags/paperless.html" class="tag">paperless</a></span>
198 </h2>
199 <div id="text-paperless-let-it-auto-detect-don-t-optimize" class="outline-text-2">
200 <p>
201 <span class="timestamp-wrapper"><a class="timestamp-link" href="/flux/2026.html"><time class="timestamp" datetime="2026-02-09">2026-02-09</time></a></span>
202 </p>
203 <p>
204 When migrating paperless-ngx to a more powerful machine (RK3588), I initially set
205restrictive "ARM64 optimizations" — fewer workers, single threads, OCR first page only.
206 </p>
207 <p>
208 This was wrong. The RK3588 has 8 cores and 32GB RAM. The defaults are <i>already optimized</i>:
209paperless auto-detects CPU count and sets workers/threads accordingly.
210 </p>
211 <div class="org-src-container">
212 <pre class="src src-nix"><code><span class="org-comment-delimiter"># </span><span class="org-comment">DON'T do this on capable hardware:
213</span><span class="org-nix-attribute">PAPERLESS_TASK_WORKERS</span> = <span class="org-string">"2"</span>; <span class="org-comment"># Limiting!
214</span><span class="org-nix-attribute">PAPERLESS_THREADS_PER_WORKER</span> = <span class="org-string">"1"</span>; <span class="org-comment"># Limiting!
215</span><span class="org-nix-attribute">PAPERLESS_OCR_PAGES</span> = <span class="org-string">"1"</span>; <span class="org-comment"># Incomplete!
216</span>
217<span class="org-comment-delimiter"># </span><span class="org-comment">DO this: just let paperless figure it out
218</span><span class="org-comment-delimiter"># </span><span class="org-comment">(remove all worker/thread overrides)</span>
219</code></pre>
220 </div>
221 </div>
222 </section>
223 <section id="outline-container-always-use-explicit-refspecs-for-git-push" class="outline-2">
224 <h2 id="always-use-explicit-refspecs-for-git-push">
225 <a href="#always-use-explicit-refspecs-for-git-push" class="anchor">§</a>Always use explicit refspecs for <code>git push</code><span class="tags"><a href="/flux/tags/git.html" class="tag">git</a><a href="/flux/tags/safety.html" class="tag">safety</a></span>
226 </h2>
227 <div id="text-always-use-explicit-refspecs-for-git-push" class="outline-text-2">
228 <p>
229 <span class="timestamp-wrapper"><a class="timestamp-link" href="/flux/2026.html"><time class="timestamp" datetime="2026-01-28">2026-01-28</time></a></span>
230 </p>
231 <p>
232 Bare <code>git push</code> without a refspec uses the branch's tracking configuration, which
233can silently push to the wrong branch — especially with worktrees or branches created
234from unexpected bases.
235 </p>
236 <p>
237 Always use:
238 </p>
239 <div class="org-src-container">
240 <pre class="src src-shell"><code>git push origin branch:branch
241</code></pre>
242 </div>
243 <p>
244 Never:
245 </p>
246 <div class="org-src-container">
247 <pre class="src src-shell"><code>git push <span class="org-comment-delimiter"># </span><span class="org-comment">← dangerous</span>
248</code></pre>
249 </div>
250 <div class="callout callout-warning" id="org79c4fb7">
251 <div class="callout-title">
252 Warning
253 </div>
254 <p>
255 With worktrees, tracking configs can diverge between the main repo and worktree checkouts. A bare <code>git push</code> in a worktree might push to a completely different remote branch than you expect.
256 </p>
257 </div>
258 </div>
259 </section>
260 </article>
261 <time hidden="" datetime="2026-03-12"></time>
262 </body>
263</html>