<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>JJGadgets</title><link>https://6f8f7e98.jjgadgets-tech.pages.dev/</link><description>Recent content on JJGadgets</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>JJGadgets</copyright><lastBuildDate>Sun, 11 May 2025 13:23:18 +0800</lastBuildDate><atom:link href="https://6f8f7e98.jjgadgets-tech.pages.dev/index.xml" rel="self" type="application/rss+xml"/><item><title>Tidbits: Ceph RGW - List All S3 Buckets Disk Usage</title><link>https://6f8f7e98.jjgadgets-tech.pages.dev/2025/ceph-rgw-list-all-s3-buckets-disk-usage/</link><pubDate>Sun, 11 May 2025 13:23:18 +0800</pubDate><guid>https://6f8f7e98.jjgadgets-tech.pages.dev/2025/ceph-rgw-list-all-s3-buckets-disk-usage/</guid><description>&lt;p>Quick one liner command to list all Ceph RGW buckets usage, using &lt;code>jq&lt;/code> to extract only the bucket name and size KV pairs and converting from bytes to gigabytes (GB):&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">radosgw-admin bucket stats &lt;span class="p">|&lt;/span> jq &lt;span class="s1">&amp;#39;def calc(val): if val!=null then val|tonumber/1000000000|tostring + &amp;#34;GB&amp;#34; else . end; .[] | {bucket} + (.usage.&amp;#34;rgw.main&amp;#34; | {size: (calc(.size)), size_actual: (calc(.size_actual)), size_utilized: (calc(.size_utilized))})&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>For me, I use Rook-Ceph on Kubernetes for my homelab, and like to view the output in Neovim, so the full command line for me looks like this:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">kubectl &lt;span class="nb">exec&lt;/span> -it -n rook-ceph deploy/rook-ceph-tools -- radosgw-admin bucket stats &lt;span class="p">|&lt;/span> jq &lt;span class="s1">&amp;#39;def calc(val): if val!=null then val|tonumber/1000000000|tostring + &amp;#34;GB&amp;#34; else . end; .[] | {bucket} + (.usage.&amp;#34;rgw.main&amp;#34; | {size: (calc(.size)), size_actual: (calc(.size_actual)), size_utilized: (calc(.size_utilized))})&amp;#39;&lt;/span> &lt;span class="p">|&lt;/span> nvim
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Or only the &lt;code>jq&lt;/code> query if you prefer:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">def&lt;/span> &lt;span class="err">calc(val):&lt;/span> &lt;span class="err">if&lt;/span> &lt;span class="err">val!=&lt;/span>&lt;span class="kc">null&lt;/span> &lt;span class="err">then&lt;/span> &lt;span class="err">val|tonumber/&lt;/span>&lt;span class="mi">1000000000&lt;/span>&lt;span class="err">|tostring&lt;/span> &lt;span class="err">+&lt;/span> &lt;span class="s2">&amp;#34;GB&amp;#34;&lt;/span> &lt;span class="err">else&lt;/span> &lt;span class="err">.&lt;/span> &lt;span class="err">end;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">.&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="err">|&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">bucket&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="err">+&lt;/span> &lt;span class="err">(.usage.&lt;/span>&lt;span class="s2">&amp;#34;rgw.main&amp;#34;&lt;/span> &lt;span class="err">|&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">size:&lt;/span> &lt;span class="err">(calc(.size)),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">size_actual:&lt;/span> &lt;span class="err">(calc(.size_actual)),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">size_utilized:&lt;/span> &lt;span class="err">(calc(.size_utilized))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;span class="err">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Here&amp;rsquo;s an example output:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucket&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;pg-authentik&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.688277968GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_actual&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.725648896GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_utilized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.688277968GB&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucket&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;pg-gts-robo&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.003353182GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_actual&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.003444736GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_utilized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.003353182GB&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucket&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;joplin&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_actual&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_utilized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucket&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;pg-atuin&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.468781088GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_actual&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.48617472GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_utilized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.468781088GB&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucket&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;gotosocial-media&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;7.746584026GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_actual&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;7.825215488GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_utilized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;7.746584026GB&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucket&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;gts-robo-media&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_actual&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_utilized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucket&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;zipline-data&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.00079149GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_actual&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.000794624GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_utilized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.00079149GB&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucket&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;pg-home&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.763082224GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_actual&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.787741696GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_utilized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.763082224GB&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucket&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;reactive-resume-media&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_actual&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_utilized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucket&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;pg-gotosocial-valetudo&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_actual&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_utilized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucket&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;volsync&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;373.431380366GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_actual&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;373.502193664GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_utilized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;373.431380366GB&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucket&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;pg-default&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.899299768GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_actual&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.965950464GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_utilized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.899299768GB&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucket&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;k8s-schemas-rgw&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.0391532GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_actual&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.040087552GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_utilized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.0391532GB&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucket&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;pg-gotosocial&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;6.635240072GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_actual&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;6.69478912GB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size_utilized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;6.635240072GB&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>PSA: Avoid WD NVMe SSDs due to bad ASPM</title><link>https://6f8f7e98.jjgadgets-tech.pages.dev/2024/avoid-wd-nvme-aspm/</link><pubDate>Tue, 15 Oct 2024 22:33:02 +0800</pubDate><guid>https://6f8f7e98.jjgadgets-tech.pages.dev/2024/avoid-wd-nvme-aspm/</guid><description>&lt;h1 id="psa-avoid-western-digital-wd-nvme-ssds-if-you-want-stable-working-pcie-aspm-and-no-io-errors">PSA: &lt;strong>Avoid Western Digital (WD) NVMe SSDs&lt;/strong> if you want stable working PCIe ASPM. And no I/O errors.&lt;/h1>
&lt;blockquote>
&lt;p>Note: This is merely my experience with WD NVMe SSDs, and is not a fully objective or scientific post. You may (and most likely do) know better than me on this topic. Whatever the case is, please keep it civil and respectful if you would like to let me (and/or others) know more on this topic, for educational purposes. I would love to learn more about this. But I&amp;rsquo;m done with buying any more WD NVMe SSDs, and that&amp;rsquo;s final.&lt;/p>
&lt;/blockquote>
&lt;h2 id="first-strike-wd-blue-sn550-1tb-for-2-years-on-blackhawk-my-laptop">First strike: WD Blue SN550 1TB for 2 years, on Blackhawk, my laptop.&lt;/h2>
&lt;p>Earlier this year (2024), I had a 1TB SN550 in my laptop whose controller became very unstable at 98TB writes (it&amp;rsquo;s supposed to take at least 4x that amount of writes, if not way more) and only by disabling PCIe ASPM both in UEFI and in Linux boot arguments could I have I/O operations working for more than 100MB without crashing. This SSD had been used for 2 years, maybe a little longer. Just a little.&lt;/p>
&lt;p>I came up with the idea of disabling PCIe ASPM after looking at dmesg and noticing that I could sometimes get as far as login and launching SwayWM and i3status-rs with my config intact before I/O error would occur, indicating that the flash was probably fine and it was probably a controller error (after all, it was showing &lt;code>controller reset&lt;/code> in dmesg), and in my head thinking that one of the factors that would cause the controller to change its state and thus go from working fine in boot process to crashing after boot finished, is either power or thermal related.&lt;/p>
&lt;p>And that&amp;rsquo;s how my solution came down to disabling PCIe ASPM (and all PCIe and drive power management options I could find), and pointing a Noctua fan directly at the NVMe drive.&lt;/p>
&lt;p>To recover my data, I moved the NVMe to a different machine (we&amp;rsquo;ll call it recovery rig), did the above mitigations on the recovery rig, and had to use &lt;code>ddrescue&lt;/code> with its map file to extract my data out in cycles (boot into live ISO with &lt;code>ddrescue&lt;/code> script in USB, start ddrescue reading from SSD, controller crash, shut down and drain power fully (every last light on the motherboard must be off), boot into live ISO, repeat).&lt;/p>
&lt;p>The laptop is a 2020 Lenovo ThinkPad T14 Gen 1 AMD. The recovery rig is a custom gaming rig, with an ASUS MAXIMUS VIII HERO motherboard.&lt;/p>
&lt;p>Also, because I bought 4x 1TB SN550 drives from Amazon US during an Amazon SG sale in 2021, when I went to enquire about warranty coverage for this failure in Feb 2024, I was told that it was ineligible for any warranty as the drive came from US and I was based in SG.&lt;/p>
&lt;h2 id="second-strike-wd-black-sn770-2tb-for-1-week-on-nighthawk-my-gaming-desktop">Second strike: WD Black SN770 2TB for &amp;lt;1 week, on Nighthawk, my gaming desktop.&lt;/h2>
&lt;p>Last month, I bought a 2TB SN770 for use in my gaming rig, thinking &amp;ldquo;well, it&amp;rsquo;s only a gaming rig, with mainly games on it (due to SMB Folder Redirection storing my user folders on my NAS), surely, going with WD again due to it being the cheapest 2TB on Amazon SG at the time, that&amp;rsquo;s no big deal right? I don&amp;rsquo;t mind replacing 3 years down the road once it wears!&amp;rdquo;. Oh, how naive last month me was.&lt;/p>
&lt;p>I &lt;code>dd&lt;/code> transferred my data from a different 1TB SN550 (the one from the gaming rig) to the 2TB SN770. Less than a week into using the SN770, I run a &lt;code>chkdsk&lt;/code> on Windows (as GParted partition move needed it), and I start getting BSoDs with error code &lt;code>WHEA_HARDWARE_ERROR&lt;/code> on 9/10 boot attempts. The actual blue screen would only last for less than a second before the machine force reboots itself, and I needed to record my monitor to catch the error code. Even after booting, it was 50/50 whether it will crash in a few hours or not.&lt;/p>
&lt;p>I was unsure if the &lt;code>chkdsk&lt;/code> had screwed my data in any way, and wanted to continue gaming, and honestly wanted to take the easy way out using my 30 day Amazon return, so I swapped back to the previous 1TB SN550 for my gaming rig, put the SN770 in the recovery rig, this time to wipe its data for the Amazon return as the old gaming rig SN550 was still working fine (&lt;em>knocks on wood&lt;/em>), and lo and behold: only the UEFI could detect it.&lt;/p>
&lt;p>Using an EndeavourOS live ISO, its boot process included scanning all disks for LVM volumes to activate, and consistently on the first boot since the machine was previously fully power drained (power switch off &lt;em>and&lt;/em> all lights off), the LVM activation would cause the controller to I/O error but /dev/nvme0 was still detected and populated in the filesystem, and then subsequent reboots without full power drain would cause the SSD to not be detected entirely after UEFI boot menu.&lt;/p>
&lt;p>I was insistent in wiping my data before the return, so I thought about anything to make the controller stable, and I recalled my previous SN550 experience where I had stumbled on the idea of disabling PCIe ASPM and doing so had stabilized the controller. Thus, despite the lack of &lt;code>controller reset&lt;/code> messages in dmesg (instead going straight to &lt;code>Input/output error&lt;/code>) and the fact that this was a brand new SSD, I disabled PCIe ASPM in the UEFI and Linux boot args &lt;em>again&lt;/em>, and what do you know: I can now &lt;code>fdisk -l /dev/nvme0n1&lt;/code> and &lt;code>dd if=/dev/urandom of=/dev/nvme0n1 bs=1M oflag=direct status=progress&lt;/code>.&lt;/p>
&lt;p>I can finally return the drive in peace. Amazon is waiting.&lt;/p>
&lt;p>The gaming rig is a custom gaming rig, with an MSI B550-A Pro that supports PCIe 4.0 NVMe SSDs. The recovery rig supports PCIe 3.0 so the NVMe drive was running at 3.0x4.&lt;/p>
&lt;blockquote>
&lt;p>&lt;em>Side note&lt;/em>: In my head, I had a theory: in the recovery rig the SN770 would run cooler as the speed is limited, as I had thought the issue was PCIe 4.0 causing thermal throttling on the SSD. The heat source that would cause the throttle in my theory would be from the RTX 2060 and the CPU heatsink being basically &amp;ldquo;above&amp;rdquo; the NVMe slot on the B550-A Pro, but despite the much better NVMe slot positioning on the MAXIMUS rig and the lack of GPU, alas it was not the case.&lt;/p>
&lt;/blockquote>
&lt;h2 id="no-third-strike">No third strike&lt;/h2>
&lt;p>No more Amazon sales shall tempt me to getting another WD NVMe SSD. I want my PCIe ASPM so I can avoid paying for unnecessary electricity, and WD to me now has a bad track record for PCIe ASPM support. Especially when a failure occurs not even a week into using the drive. I&amp;rsquo;m done.&lt;/p>
&lt;p>For my laptop, I got a 2TB SK Hynix P31 Gold from Amazon SG with no offers (as I had to replace the drive ASAP to continue my personal endeavours), and for the gaming rig, the recent (Oct 2024) Prime Day sales had the 2TB Samsung 990 EVO for even cheaper than what I got the WD SN770 for, so I got 2 of those, 1 as a cold spare.&lt;/p>
&lt;p>I still have 2x WD SN550 drives in use today on my R730xd server, and once those give out (hopefully not soon), it won&amp;rsquo;t be replaced with another WD NVMe SSD, though I&amp;rsquo;m not sure what it&amp;rsquo;ll be yet or even what size as I plan to restructure that part of my homelab (or more realistically, home-prod).&lt;/p>
&lt;blockquote>
&lt;p>When I wrote this, I had just finished the &lt;code>dd if=/dev/urandom of=/dev/nvme0n1&lt;/code> on the SN770, it&amp;rsquo;s now a few days later and my money has been refunded after returning the SN770 to Amazon, so I&amp;rsquo;m now publishing this post.&lt;/p>
&lt;/blockquote>
&lt;h2 id="comments-from-fediverse">Comments (from Fediverse)&lt;/h2>
&lt;iframe src="https://social.jjgadgets.tech/@jj/statuses/01JAMKE2KVXX96A9ZFHXG2238P" min-width="75vw" width="100%" height="750vh" max-height="999999px">&lt;/iframe></description></item><item><title>Ferris Sweep</title><link>https://6f8f7e98.jjgadgets-tech.pages.dev/2024/ferris-sweep/</link><pubDate>Sun, 17 Mar 2024 23:35:48 +0800</pubDate><guid>https://6f8f7e98.jjgadgets-tech.pages.dev/2024/ferris-sweep/</guid><description>&lt;p>I recently got a Ferris Sweep to get into the r/ErgoMechKeyboards cult, reduce shoulder strain from using a joint keyboard, and reduce finger movement and stretching when typing. I love this thing. I&amp;rsquo;m typing this very post with the Sweep on my iPad wirelessly!&lt;/p>
&lt;p>&lt;img src="https://6f8f7e98.jjgadgets-tech.pages.dev/2024/ferris-sweep/sweep.png"
width="4032"
height="3024"
srcset="https://6f8f7e98.jjgadgets-tech.pages.dev/2024/ferris-sweep/sweep_hu739469aade10817ca3254098f3042b31_7727971_480x0_resize_box_3.png 480w, https://6f8f7e98.jjgadgets-tech.pages.dev/2024/ferris-sweep/sweep_hu739469aade10817ca3254098f3042b31_7727971_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Ferris Sweep on desk"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;img src="https://6f8f7e98.jjgadgets-tech.pages.dev/2024/ferris-sweep/sweep-jank.jpg"
width="4032"
height="2268"
srcset="https://6f8f7e98.jjgadgets-tech.pages.dev/2024/ferris-sweep/sweep-jank_huaacde9ab12a850449272adc6950d4cbe_3160925_480x0_resize_q75_box.jpg 480w, https://6f8f7e98.jjgadgets-tech.pages.dev/2024/ferris-sweep/sweep-jank_huaacde9ab12a850449272adc6950d4cbe_3160925_1024x0_resize_q75_box.jpg 1024w"
loading="lazy"
alt="Ferris Sweep with janky cardboard bottom case"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>⚠️ WARNING ⚠️&lt;/strong>: I am &lt;strong>not&lt;/strong> responsible for any readers falling into rabbit holes of building keyboards or modding keymaps without restraint, instead of actually doing typing or work. Ask me how I know. &lt;strong>You have been warned.&lt;/strong>&lt;/p>
&lt;/blockquote>
&lt;h2 id="specs">Specs&lt;/h2>
&lt;p>This is a Ferris Sweep with MX profile keys, using Akko Crystal Silver switches and the stock Anne Pro 2 keycaps I had unused. It is wireless thanks to a nice!nano clone (SuperMini nRF52840) microcontroller, and runs the ZMK firmware.&lt;/p>
&lt;h2 id="first-impressions">First Impressions&lt;/h2>
&lt;p>Once I had the keyboard, I started making my own keymap based on some personal preferences and habits I had already known about my keyboard usage. I started with Colemak, and fit all of the numbers and symbols and navigation keys that aren&amp;rsquo;t already covered by the default layer into the 2nd layer I named &amp;ldquo;num&amp;rdquo;. I then fit modifiers and &amp;ldquo;text editing&amp;rdquo; keys on the thumb keys and hold-taps.&lt;/p>
&lt;p>While my initial keymap was mostly satisfactory, I did immediately start &lt;strong>noticing issues with my typing habits&lt;/strong> that I never noticed on a normal ANSI stagger keyboard. Some of these could be fixed with optimising my keymap to my comforts, but &lt;strong>most of these weren&amp;rsquo;t a keymap issue&lt;/strong>.&lt;/p>
&lt;h2 id="initial-struggles">Initial Struggles&lt;/h2>
&lt;p>I had realized that while I was comfortable with typing in the Colemak layout, I was familiar with it on my Anne Pro 2 which had an ANSI stagger. This caused me to &lt;strong>develop very bad finger habits&lt;/strong>, including:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Idle finger positions not on home row.&lt;/strong> I would idle my left fingers on WASD (or WARS on Colemak) because I was used to that from gaming movements and it was more comfortable than cramping my fingers in a straight row for ASDF (or ARST on Colemak). On the Sweep, it was just natural to idle on home row because of the horizontal &amp;ldquo;ergo&amp;rdquo; stagger.&lt;/li>
&lt;li>&lt;strong>Hitting spacebar with the right index finger.&lt;/strong> I thought I was used to hitting spacebar with the left thumb, but it turned out that was only during gaming where the left hand was doing movement and selecting items/skills and the right hand was aiming with the mouse. When I was doing actual typing fast,, I had subconsciously gotten used to using my right index to smash on the spacebar.&lt;/li>
&lt;li>&lt;strong>Bottom row finger positions offset by 1 column.&lt;/strong> The ANSI stagger makes it such that I would hit every key on the bottom row with the wrong finger, but the offset was fixed: I would hit the key to the left of what that finger was supposed to hit. In other words, my fingers were used to the bottom row being moved 1 column to the right. I would hit B with the right index, C with the left index, X with the left middle finger, and Z with the left forth finger. Left pinky solely does Left Shift.&lt;/li>
&lt;/ol>
&lt;p>The first issue was naturally resolved after typing for the first day. The whole reason I was interested in a split ergo was to have the rows staggered, so I just needed to follow through on that.&lt;/p>
&lt;p>The other 2 issues however, were much harder to get used to, and even now on the 2nd week, I&amp;rsquo;m still tripping out over these. It&amp;rsquo;s hard to break these natural habits that form because of a bad fundamental layout such as the normal US ANSI layout with the ANSI stagger, especially when I was familiar enough with ANSI to hit 100WPM comfortably (I don&amp;rsquo;t see a point in training myself to be faster than this because I already type faster than my brain can think of what to type out).&lt;/p>
&lt;p>For the spacebar, I had tried to add the spacebar to both sides of the thumb cluster, as I originally thought it was a matter of which side&amp;rsquo;s thumb I was using. Until, I added a right thumb spacebar, but it collected dust anyway, and when I went back to typing on an ANSI keyboard like my Anne Pro 2, I noticed the real issue: I was using right index specifically for hitting spacebar when I was typing fast.&lt;/p>
&lt;p>I also opted to not take typing lessons or typing tests or things of the sort, and I jumped right into doing actual typing with the new Sweep layout. The reason was simple: I was never going to use over half the words from typing tests and stuff, not when most of my typing involves CLI words like &lt;code>git&lt;/code>, &lt;code>kubectl&lt;/code>, &lt;code>ssh&lt;/code>, &lt;code>sops&lt;/code>, &lt;code>nvim&lt;/code> etc, swear words when I&amp;rsquo;m chatting with friends, tech words and names like &amp;ldquo;Kubernetes&amp;rdquo;, &amp;ldquo;Ceph&amp;rdquo;, &amp;ldquo;SSD&amp;rdquo;, &amp;ldquo;NVMe&amp;rdquo;, &amp;ldquo;Cilium&amp;rdquo;, &amp;ldquo;VLAN&amp;rdquo;, &amp;ldquo;WireGuard&amp;rdquo; etc, internet lingo like &amp;ldquo;LOL&amp;rdquo;, &amp;ldquo;LMAO&amp;rdquo;, &amp;ldquo;idk&amp;rdquo;, and passwords. Oh, and the ZMK keymap itself.&lt;/p>
&lt;p>I never got very far with keybr to figure out any of these issues, but within the 2nd and 3rd day of daily driving this keyboard, I had already noticed and outlined the core issue with the bottom row finger positions.&lt;/p>
&lt;p>Unfortunately, these issues can&amp;rsquo;t be solved by simply editing the layout. &lt;a class="link" href="https://github.com/JJGadgets/zmk-config/commit/2dff50a8e1ebd1bbbcd272433855cfe12665b32b" target="_blank" rel="noopener"
>I tried&lt;/a>, but it breaks more than it fixes. So, I had no choice but to resolve myself to re-building the proper habits.&lt;/p>
&lt;p>If you find that you&amp;rsquo;re also struggling with bad typing habits that only show on split ergos, &lt;strong>you&amp;rsquo;re not alone&lt;/strong>. It&amp;rsquo;s a part of the process. &lt;strong>Keep at it&lt;/strong> and you&amp;rsquo;ll find yourself more comfortable with proper typing habits the more you use the split ergo.&lt;/p>
&lt;h2 id="less-is-actually-more">Less Is Actually More&lt;/h2>
&lt;p>I thought lesser keys was more hassle. I was comfortable with layers, but that was for &amp;ldquo;non-core&amp;rdquo; or lesser used keys like media controls, and the Home/End/PageUp/PageDown keys, but putting everything except alphabets behind layers was a whole new level.&lt;/p>
&lt;p>However, &lt;strong>lesser keys meant lesser finger movements&lt;/strong>, which meant it was actually &lt;em>more&lt;/em> comfortable to hit the non-alphabet keys without moving my fingers nearly as far. Combined with Colemak, I could feel the results of the reduced finger movement very quickly, and this was combined with the reduced shoulder strain now that I could spread the keyboard out further and avoid cramping up my shoulders to get in a (now not as much) comfortable typing position. More on the shoulders coming up!&lt;/p>
&lt;h2 id="the-bliss-of-a-wireless-split-ergo">The Bliss of a Wireless Split Ergo&lt;/h2>
&lt;p>Despite the struggles, I was already &lt;strong>very happy&lt;/strong> with using the Sweep in less than 2 days. The reason was simple: &lt;strong>I loved the freedom of having a wireless split ergo keyboard.&lt;/strong>&lt;/p>
&lt;p>I could place the keyboard wherever I wanted, however I wanted, without needing to cramp my fingers and shoulders up to accomodate the limited positioning of an un-split keyboard, and I could do this with just the device I was going to type on, and the Sweep halves. Nothing more. I could already feel the difference on my shoulders by day 2.&lt;/p>
&lt;p>It &lt;em>has&lt;/em> to be wireless, split, and the ergo key layout, to achieve this level of freedom while retaining hand comfort. Without any one of those, I would not enjoy the keyboard at all.&lt;/p>
&lt;p>See, I already have a wired Sweep with Kailh Choc Sunset switches. But, needing 2 cables made it very annoying to use with my laptop at my home desk with the cables running everywhere and restricting my positioning and angling of the halves, let alone use it on a much more portable iPad which I bring around more often than the laptop if I&amp;rsquo;m out but not at school or work.&lt;/p>
&lt;p>However, I had various difficulties converting it to wireless (I have a pair of actual nice!nanos ready and unused), and I wasn&amp;rsquo;t even the one doing the soldering (I have none of the tools or experience). So, I ended up letting that keeb sit around unused for over a year.&lt;/p>
&lt;p>(Also, I found QMK a little annoying to work with, and now having used ZMK, it reaffirmed my preference for ZMK over QMK.)&lt;/p>
&lt;p>Recently, I finally found a local seller selling preassembled wireless Sweeps using nice!nano clones from SuperMini. I was hesitant at first as I really liked the low profile of the Choc, but after some thinking, and given how just less than a month before that I was revisiting the split ergo scene again, I decided to buy the keyboard anyway despite the MX profile, as I was really feeling the shoulder strain of a normal ANSI more than the last time I used my Choc Sweep.&lt;/p>
&lt;p>Within a week, I had used my wireless MX Sweep a lot more than I had used the wired Choc Sweep in the whole period of owning it and having it assembled by a friend.&lt;/p>
&lt;p>I don&amp;rsquo;t mind the Choc Sweep collecting dust for a while, because aside from switches, I had picked the absolute cheapest parts for that build, so as long as I can get my switches unsoldered, the rest of the build (including the wired Pro Micros) was less than $30. My MX Sweep looks much cleaner in terms of colors than my Choc Sweep anyway. The nice!nanos I bought and had planned to install in the Choc Sweep, as well as the Choc Sunset switches, will go to a future build, so it&amp;rsquo;s all good.&lt;/p>
&lt;p>The main thing was I wanted a wireless split ergo &lt;strong>now&lt;/strong>, and I wanted to stop giving myself excuses and procrastinating over it.&lt;/p>
&lt;h2 id="my-layout">My Layout&lt;/h2>
&lt;p>My current layout, which can be found on &lt;a class="link" href="https://github.com/JJGadgets/zmk-config/tree/166d36d1dfb63afb90b64199d4a2f11a2948428d" target="_blank" rel="noopener"
>Git (current commit as of time of writing)&lt;/a>, currently features the following, tailored to my personal preferences and natural flow of thinking and typing:&lt;/p>
&lt;h3 id="colemak-layout">Colemak layout&lt;/h3>
&lt;ul>
&lt;li>Comfortable, easy to switch from and with QWERTY in case I use a device I don&amp;rsquo;t own that I can&amp;rsquo;t use my Sweep on, not mod-DH because I couldn&amp;rsquo;t get used to it&lt;/li>
&lt;/ul>
&lt;h3 id="main-3-layers-include-default-lnum-and-lnav">Main 3 layers include default, LNUM and LNAV&lt;/h3>
&lt;ul>
&lt;li>Rest are less important to core typing experience&lt;/li>
&lt;li>LNUM handles left hand numbers, symbols across both, and right side navigation&lt;/li>
&lt;li>LNAV does left hand navigation, right hand Bluetooth, and media controls across both&lt;/li>
&lt;li>Gaming layer has top 2 rows on left half moved to the right by 1 column and remapped to use middle 3 rows for WASD (or WARS on Colemak), right half stays the same&lt;/li>
&lt;/ul>
&lt;h3 id="bottom-row-mods">Bottom row mods&lt;/h3>
&lt;ul>
&lt;li>Home row mods got in my way more often, and this makes the most used home row delay free&lt;/li>
&lt;/ul>
&lt;h4 id="update-bilateral-combinations-cross-hand">UPDATE: bilateral combinations (cross-hand)&lt;/h4>
&lt;blockquote>
&lt;p>&lt;strong>2024-03-26&lt;/strong>: I have now added cross-hand settings to my bottom row mods, and I found that I&amp;rsquo;m liking them more than I thought I would.&lt;/p>
&lt;p>I initially thought that the ZMK &lt;code>hold-trigger-key-positions&lt;/code> settings meant that the mods actvation would only work if the next key is on the other half, but upon re-reading, that is only true for the interrupt time window for &lt;code>balanced&lt;/code> or &lt;code>hold-preferred&lt;/code> hold-tap flavors, between initial press down and &lt;code>tapping-term-ms&lt;/code>. It doesn&amp;rsquo;t affect holding past &lt;code>tapping-term-ms&lt;/code>, meaning &lt;strong>you can still use same-hand mods once you hold the mod key past &lt;code>tapping-term-ms&lt;/code>&lt;/strong>, no workflow changes there.&lt;/p>
&lt;p>Additionally, I&amp;rsquo;ve only ever used mods on the left half of the keyboard on normal ANSI keyboards such as on laptops, as I find the right hand mods uncomfortable to hold and use. However, upon adding the &lt;code>hold-trigger-key-positions&lt;/code>, I found that I actually quite liked using cross-hand bottom row mods and having them activate blazing fast, and that it was significantly more comfortable using right hand mods when used in bottom row mode on split ergo keyboards, rather than where right hand mods would be located on a normal ANSI keyboard.&lt;/p>
&lt;p>So I now use urob&amp;rsquo;s home-row mods behavior snippet from his &lt;a class="link" href="https://github.com/urob/zmk-config/tree/f27b0031cb6868ff6c0389b39a032fed4b483d25?tab=readme-ov-file#timeless-homerow-mods" target="_blank" rel="noopener"
>zmk-config&amp;rsquo;s README.md&lt;/a> wholesale rather than tweaking it like I had before, and I love it. It&amp;rsquo;s the same behavior I wanted for same-hand mods, for those lazy moments, but much faster cross-hand without introducing finger roll issues, and much more comfortable than normal ANSI keyboards&amp;rsquo; right hand mods. I would definitely recommend giving the entire behavior snippet a try before tweaking it (like I always do).&lt;/p>
&lt;/blockquote>
&lt;h3 id="shared-space-shift-thumb-button">Shared space shift thumb button&lt;/h3>
&lt;ul>
&lt;li>I want space and shift on left half because of familiraity and to free up right hand for mouse use, but it was too annoying to have them separate as I would keep mixing them up and tripping over my left thumb. I am aware this slows me down, but it&amp;rsquo;s more natural and comfortable.&lt;/li>
&lt;/ul>
&lt;h3 id="my-layer-switching-system">My layer switching system&lt;/h3>
&lt;ul>
&lt;li>I liked Ben Vallack&amp;rsquo;s idea of having reliable and repeatable mechanisms for layer switching without accidentally pressing unintended keys, but the one thing I didn&amp;rsquo;t like was the lack of layer hold. I find it easier to hold for my LNUM layer and have the release immediately bring me back to the default layer, but sometimes I also like switching entirely to the LNUM layer if I know I need my arrow keys for the next while or I&amp;rsquo;m typing an IPv4 address.&lt;/li>
&lt;li>After trying a layer hold-tap where pressing will switch to LNUM permanently but holding will only switch to LNUM for the hold duration, and finding that a little rough with misfires, I now use a layer lock, so I can hold to LNUM with the right inner thumb key and lock with the left inner thumb key.&lt;/li>
&lt;/ul>
&lt;h3 id="layer-lock-or-to0-on-other-layers-is-esc-on-tap--control-on-hold-on-default">Layer Lock or TO(0) on other layers is Esc on Tap + Control on hold on Default&lt;/h3>
&lt;ul>
&lt;li>I wanted Control on a thumb key to make Control+[Z|X|C|V] easier, and ESC on a thumb key because of Vim modes, but also wanted my &amp;ldquo;to layer 0&amp;rdquo; or &amp;ldquo;unlock non-default-layer layer lock&amp;rdquo; key to be harmless if tapped on the default layer 0. What better than to combine all of those as 1 well known key!&lt;/li>
&lt;/ul>
&lt;h3 id="mod-morphs-everywhere">Mod-morphs everywhere&lt;/h3>
&lt;ul>
&lt;li>I use mod-morphs for many things, such as Alt+Tab on default layer (for non-Linux-WM machines like Windows gaming rig), and custom shifted keys like a custom colon key where colon is the unmodded tap and semicolon is the shift tap (easier for both Vim and YAML). It was also nicer to not have everything be time-based such as tap-hold or tap-dance.&lt;/li>
&lt;/ul>
&lt;h3 id="combos">Combos&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>2024-03-26&lt;/strong>: I use combos for some things I would like faster access to, and smashing 2 keys, when placed optimally, is faster than holding a key for a layer and then hitting the target key. I frequently use it for hyphen (&lt;code>-&lt;/code>) and Enter key.
&lt;ul>
&lt;li>For the Enter key, I have the middle 3 home row keys as the combo activation, which makhes it much safer than putting the Enter key on the thumb keys while I&amp;rsquo;m still getting used to the thumb pressing more than 1 finger (think preventing accidental form submissions or sending messages), while making it pretty comfortable to activate.&lt;/li>
&lt;li>I just smack my 3 middle fingers down harder than my typing force and it reliably activates, unlike my initial combo of the rightmost 2 homerow keys where my pinky and ring finger would frequently press with different forces and timings making it hard to reliably activate.).&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="zmk-vs-qmk">ZMK vs QMK&lt;/h2>
&lt;p>I personally found ZMK much more flexible and cleaner to use than QMK.&lt;/p>
&lt;p>Checking my layout into Git was easier with ZMK thanks to them supporting user config repos compared to QMK needing to fork the whole QMK repo to add your own keymap.&lt;/p>
&lt;p>ZMK also allows for much more flexible time-based behavior tuning, such as having the hold-tap tuning be behavior based rather than for the whole keyboard, and generally being more straightforward to tune and understand.&lt;/p>
&lt;p>I could get my desired keymap dialed in on ZMK much quicker than in QMK, I never did iron out the time-based tuning on QMK after all, and I have a lot more time-based and mod-based keys on ZMK than QMK, in the way I wanted them configured, because of how easy it is to set them up on ZMK over QMK.&lt;/p>
&lt;p>It&amp;rsquo;s also pretty neat that I can use ZMK keyboards in wired USB mode while still using only 1 cable, because the slave half will still connect to the master half using Bluetooth. So even if I have to use a cable, it&amp;rsquo;s still less infuriating than having the TRRS jack coming out the side of the keyboard and blocking certain keyboard positions I prefer to use.&lt;/p>
&lt;p>(Oh, and ZMK&amp;rsquo;s USB mode can run at 1000Hz and with eager debouncing (0ms press debounce, 5ms release debounce), making it &lt;em>technically&lt;/em> possible to game with the wireless Sweep&amp;rsquo;s left half connected over USB. The only issue left for me is ironing out my personal kinds on the GAME layer. It&amp;rsquo;s 17 keys on one half after all.)&lt;/p>
&lt;p>For a split ergo setup specifically, I would personally use ZMK over QMK as much as I can.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>If you are also looking to increase typing comfort, while also building much better and more proper typing habits, a wireless split ergo such as the Sweep is definitely worth the investment.&lt;/p>
&lt;p>A diodeless single PCB keyboard like the Sweep also makes it more reassuring to bring around everywhere without needing to baby it as much, and takes lesser effort to rebuild.&lt;/p>
&lt;p>A 34 key keyboard also forces the user to build better habits over a keyboard with more keys while being cheaper to get into, even if it might occasionally be a hassle to not hve enough keys (e.g. gaming).&lt;/p>
&lt;p>&lt;strong>I love my Sweep.&lt;/strong> It&amp;rsquo;s been an amazing journey to get into using it full time, and I&amp;rsquo;m looking forward to fully mastering it and continue its usage.&lt;/p></description></item><item><title>CVE-2023-43809: My experience with my first CVE</title><link>https://6f8f7e98.jjgadgets-tech.pages.dev/2023/cve-2023-43809/</link><pubDate>Wed, 15 Nov 2023 10:00:00 +0800</pubDate><guid>https://6f8f7e98.jjgadgets-tech.pages.dev/2023/cve-2023-43809/</guid><description>&lt;h2 id="disclaimer">Disclaimer&lt;/h2>
&lt;p>Everything in this post is my perspective, opinions and experience, and does not represent anyone else but myself. Also, I will be using Markdown URL texts for non-vulnerability-related links, but please, always check what link you&amp;rsquo;re about to click on or visit, as part of general internet safety practices.&lt;/p>
&lt;h2 id="about-soft-serve-my-opinions">About Soft Serve (my opinions)&lt;/h2>
&lt;p>Simple but very effective self-hosted Git server (and now local browser!), with an amazing TUI and CLI over SSH.&lt;/p>
&lt;p>I selfhost Soft Serve on my home Kubernetes cluster, for private projects.&lt;/p>
&lt;h2 id="vulnerability-info">Vulnerability Info&lt;/h2>
&lt;p>Soft Serve Public Key Authentication Bypass Vulnerability when Keyboard-Interactive SSH Authentication is Enabled&lt;/p>
&lt;p>&lt;strong>My tl;dr:&lt;/strong> if public key needs client-side verification, and &lt;code>allow-keyless&lt;/code> is enabled which turns on &lt;code>keyboard-interactive&lt;/code>, public key can match an account but bypass (fail) client-side validation and successfully login on Soft Serve servers.&lt;/p>
&lt;p>Find out more on the GHSA page &lt;a class="link" href="https://github.com/advisories/GHSA-mc97-99j4-vm2v" target="_blank" rel="noopener"
>https://github.com/advisories/GHSA-mc97-99j4-vm2v&lt;/a> or the MITRE page &lt;a class="link" href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-43809" target="_blank" rel="noopener"
>https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-43809&lt;/a> or my GitHub issue on it &lt;a class="link" href="https://github.com/charmbracelet/soft-serve/issues/389" target="_blank" rel="noopener"
>https://github.com/charmbracelet/soft-serve/issues/389&lt;/a>.&lt;/p>
&lt;h2 id="experience-with-developers-good">Experience with Developers (Good!)&lt;/h2>
&lt;p>The Charm team have been professional about handling this with minimal friction during the disclosure process, with regular progress updates and swift responses as they confirmed the vulnerability and worked to find the root cause and fix it.&lt;/p>
&lt;h2 id="my-pov">My POV&lt;/h2>
&lt;p>So here comes what I consider the most unexpected way to find a vulnerability ever. I won&amp;rsquo;t be surprised if you think it&amp;rsquo;s a fake story, but I know for sure it was real. &lt;del>Else I wouldn&amp;rsquo;t have got my lazy ass to write a post about it, would I?&lt;/del>&lt;/p>
&lt;p>I am aboard a public bus, and my destination is only a few stops away, within 10 minutes.&lt;/p>
&lt;p>I tapped my Android phone, which has a virtual wallet card registered to the NFC, to board the bus. Found a seat, opened Termux.&lt;/p>
&lt;p>&amp;ldquo;Hmm, what should I do?&amp;rdquo; Undecided, I fiddle with k9s to look at the list of pods, wondering which app to mess with. &amp;ldquo;Ah, I got it, I need to reconfigure my Soft Serve after screwing around with making sure &lt;a class="link" href="https://github.com/charmbracelet/soft-serve/issues/363" target="_blank" rel="noopener"
>the PostgreSQL support was fully working&lt;/a> on the &lt;a class="link" href="https://github.com/charmbracelet/soft-serve/releases/tag/v0.6.0" target="_blank" rel="noopener"
>latest release&lt;/a>.&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;Damn, HelmRelease didn&amp;rsquo;t update, lemme fix that&amp;hellip; okay, good to go.&amp;rdquo; &lt;code>ssh softserve&lt;/code> &amp;ldquo;Now, need my YubiKey to authenticate Soft Serve&amp;rsquo;s SSH with my YubiKey&amp;rsquo;s PGP SSH key&amp;hellip;&amp;rdquo;&lt;/p>
&lt;p>&lt;strong>&amp;ldquo;Shit, it&amp;rsquo;s my stop, doors are about to close!&amp;rdquo;&lt;/strong> I literally jump up from my seat with one foot launching me forward, one hand grabbing onto my YubiKey, and one hand holding my Android phone with OpenKeychain prompt open.&lt;/p>
&lt;p>And, the moment: &lt;em>taps phone&amp;rsquo;s NFC card to alight bus, foreground app switches from Termux with OpenKeychain prompt to virtual wallet app, alights and switches back to Termux&lt;/em> &amp;ldquo;Wait&amp;hellip; &lt;strong>why am I logged in?&lt;/strong> I don&amp;rsquo;t remember even plugging the YubiKey in&amp;hellip;&amp;rdquo;&lt;/p>
&lt;p>I kill the SSH session, &lt;code>ssh softserve&lt;/code> again, and when OpenKeychain prompt came up this time, I intentionally clicked &amp;ldquo;Cancel&amp;rdquo;. To my shock, I was logged in again, YubiKey having never been plugged in. &lt;strong>This was no accident nor was I seeing things.&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Side note&lt;/strong>: Funny enough, I had &lt;a class="link" href="https://github.com/charmbracelet/soft-serve/issues/380" target="_blank" rel="noopener"
>filed an issue&lt;/a> on the bus ride back home for not being able to change &lt;code>allow-keyless&lt;/code> and &lt;code>anon-access&lt;/code> settings when PostgreSQL was used, which led to 0.6.1 being released, and opted to properly test and report this vulnerability when I got home. I didn&amp;rsquo;t know at the time that it would be the very setting that would mitigate this vulnerability if Soft Serve users couldn&amp;rsquo;t yet update to a patched version of 0.6.2 and above. So if you look at it a certain way, I was basically the reason for all the patch versions of the 0.6.x version family being released. All done from an Android phone. Oops!&lt;/p>
&lt;/blockquote>
&lt;h2 id="timeline-gmt8">Timeline (GMT+8)&lt;/h2>
&lt;p>&lt;strong>12 September 2023&lt;/strong>: I found the potential vulnerability.&lt;/p>
&lt;p>&lt;strong>15 September 2023&lt;/strong>: I reported the vulnerability in a thread on the Charm Discord server with a description and listing the environments that I used to reproduce the vulnerability. Devs acknowledged and stated they would look into it.&lt;/p>
&lt;p>&lt;strong>16 September 2023&lt;/strong>: I screen recorded a PoC video using my Android, and reported my further discovery that turning &lt;code>allow-keyless&lt;/code> off seemed to mitigate the vulnerability, amongst other details.&lt;/p>
&lt;p>&lt;strong>17-22 September 2023&lt;/strong>: Further communications between me and dev to identify the root cause.&lt;/p>
&lt;p>&lt;strong>27 September 2023&lt;/strong>: PR with patched code opened and merged.&lt;/p>
&lt;p>&lt;strong>28 September 2023&lt;/strong>: I tested that the nightly build with the patch PR merged does fix the issue, and opened the GitHub issue for public transparency.&lt;/p>
&lt;p>&lt;strong>3 October 2023&lt;/strong>: v0.6.2 patch version released with verified fix, GitHub Security Advisory (GHSA) filed and I accepted credit. GitHub issue closed as completed.&lt;/p>
&lt;p>&lt;strong>5 October 2023&lt;/strong>: CVE-2023-43809 was published.&lt;/p></description></item><item><title>Flux Repo Structure</title><link>https://6f8f7e98.jjgadgets-tech.pages.dev/2024/flux-repo-structure/</link><pubDate>Wed, 03 May 2023 00:00:00 +0000</pubDate><guid>https://6f8f7e98.jjgadgets-tech.pages.dev/2024/flux-repo-structure/</guid><description>&lt;h1 id="introduction">Introduction&lt;/h1>
&lt;p>In this post, I go over the repository structure that I use for my Kubernetes homelab, powered with GitOps by FluxCD.&lt;/p>
&lt;p>Remember that &lt;em>there is no &amp;ldquo;right way&amp;rdquo; or &amp;ldquo;one size fits all&amp;rdquo; to repo structuring&lt;/em>, define and be clear on your goals before you structure your repo.&lt;/p>
&lt;p>&lt;strong>NOTE:&lt;/strong> From here on out, all &lt;em>files&lt;/em> will be in bold and is prefixed by &lt;strong>./&lt;/strong> while all &lt;em>folders&lt;/em> will be in bold and suffixed by &lt;strong>/&lt;/strong> e.g. &lt;strong>folder/&lt;/strong> and &lt;strong>./file.yaml&lt;/strong>&lt;/p>
&lt;h1 id="goals">Goals&lt;/h1>
&lt;ul>
&lt;li>
&lt;p>Define desired configuration of apps and services deployed in Kubernetes cluster.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Version control all configuration changes with Git.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Automate all the things! (as much as possible)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Establish high security baseline of both Kubernetes cluster&amp;rsquo;s components, and GitOps components.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Allow for basic multi-cluster environments (production and non-production) while using monorepo&lt;/p>
&lt;ul>
&lt;li>Cluster-specific configurations using variable substitution in deploy manifests, and cluster-specific secrets.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Allow for opting-in which apps/services/components to deploy per cluster, to optimize resource allocation (CPU, memory, storage, network etc).&lt;/p>
&lt;ul>
&lt;li>I &lt;em>don&amp;rsquo;t really need&lt;/em> a Minecraft Java server on both prod and staging if I don&amp;rsquo;t change any of its configuration, do I?&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Minimize human errors or forgetfulness by keeping duplicated code to an absolute minimum.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h1 id="repo-structure-kube">Repo Structure (&lt;strong>kube/&lt;/strong>)&lt;/h1>
&lt;ul>
&lt;li>
&lt;h2 id="bootstrap">&lt;strong>bootstrap/&lt;/strong>&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>flux/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>./kustomization.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Installs Flux from the kustomization.yaml located in ./manifests/install of fluxcd/flux2 remote GitHub repo.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;h2 id="clusters">&lt;strong>clusters/&lt;/strong>&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>cluster-name/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Cluster specific folder&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Any name is fine, e.g. &lt;strong>prod/&lt;/strong> and &lt;strong>dev/&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>distro/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Cluster distribution&amp;rsquo;s configuration&lt;/p>
&lt;/li>
&lt;li>
&lt;p>e.g. Talos via talhelper (&lt;strong>talos/&lt;/strong>)&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>config/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>All Flux manifests for cluster-specific configuration state goes here&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./flux-install.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Flux SourceRepository pointed to Flux&amp;rsquo;s manifests OCI repo, scoped to version branch&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Fluxtomization to deploy and control Flux&amp;rsquo;s components (takes over control of Flux components from Flux bootstrap)&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./flux-repo.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Flux SourceRepository pointed to user&amp;rsquo;s repo (e.g. JJGadgets/Biohazard, onedr0p/home-ops, 0dragosh/homelab etc), use SSH key to clone (and optionally push if configured)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&amp;ldquo;Master&amp;rdquo; Fluxtomization to deploy and control cluster-specific configuration (path points to cluster folder) and all other deployments (via cluster folder&amp;rsquo;s ./kustomization.yaml)&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Includes configuration and patches for variable substitution ( &lt;code>${VARIABLE}&lt;/code> ) and SOPS secret decryption of all other deployments&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Includes patches for DRYing configs&lt;/p>
&lt;ul>
&lt;li>For the truly lazy, patching a Fluxtomization with patches for other resources controlled by said Fluxtomization (e.g. HelmRelease) is possible&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./kustomization.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Native Kubernetes Kustomization to control what resources are deployed, either by Fluxtomization or &lt;code>kubectl apply -k&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Used here for opt-in deployment of all cluster-specific configuration manifests in cluster folder.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Used here for opt-in deployment of which apps folders to deploy to cluster (from &lt;strong>kube/deploy/&lt;/strong>).&lt;/p>
&lt;ul>
&lt;li>
&lt;p>References the apps folders to deploy like so: &lt;code>../../../deploy/apps/jellyfin&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Each app folder will then have its own &lt;strong>kustomization.yaml&lt;/strong>, which opts-in deployment of the namespaces needed as well as the app&amp;rsquo;s Fluxtomization (&lt;strong>ks.yaml&lt;/strong>).&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Indirectly the &amp;ldquo;master&amp;rdquo; Fluxtomization &lt;em>also controls the namespaces deployed&lt;/em> (since there&amp;rsquo;s no Fluxtomizations in between the chain of &lt;strong>kustomization.yaml&lt;/strong>s).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>This allows the app&amp;rsquo;s Fluxtomization to add &amp;ldquo;master&amp;rdquo; Fluxtomization as dependency (via dependsOn).&lt;/p>
&lt;ul>
&lt;li>Ensures that namespaces are always deployed before the resources are deployed within the namespaces.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;em>NOTE:&lt;/em> (about namespacing)&lt;/p>
&lt;ul>
&lt;li>
&lt;p>I choose to group apps in their own namespace, unless an app requires either multiple containers (e.g. server and web client, or microservices architecture), or 2 different apps must be in the same namespace to share Kubernetes resources or other reasons.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>My structure is a janky ghetto way to implement &amp;ldquo;multi-cluster&amp;rdquo; with a very specific purpose of having production and non-production (dev/test/staging/UAT, whichever is appropriate) clusters, where the differences in the clusters mostly come down to cluster-specific variables/secrets and control of which apps are deployed, allowing the non-prod cluster(s) to be scaled smaller than the prod cluster (e.g. I &lt;em>don&amp;rsquo;t really need&lt;/em> Jellyfin on both prod and staging if I don&amp;rsquo;t change any of its configuration)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Other folks like onedr0p, bjw-s, 0dragosh etc will have the &amp;ldquo;master&amp;rdquo; Fluxtomization deploy a separate &amp;ldquo;apps&amp;rdquo; Fluxtomization that points to &lt;strong>kubernetes/apps&lt;/strong> which has &lt;strong>kubernetes/apps/namespace/kustomization.yaml&lt;/strong> control both namespace and app Fluxtomizations (&lt;strong>kubernetes/apps/namespace/app/ks.yaml&lt;/strong>) to deploy, &lt;em>which isn&amp;rsquo;t a wrong way to structure for their needs&lt;/em>, however this means that there is no easy way to control which cluster should deploy which apps which I desire&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./secrets.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Cluster-specific native Kubernetes secrets, encrypted-at-rest by SOPS (Flux supports decrypting SOPS-encrypted files).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Can be used via variable substitution at deploy-time.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>external-secrets is preferred over storing secrets in here to easily sync and/or consume namespace-specific secrets.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>One hacky way to have cluster-specific yet namespace-specific native Kubernetes secrets (you can&amp;rsquo;t reference a secret stored in &lt;code>flux-system&lt;/code> namespace from e.g. &lt;code>minecraft&lt;/code> namespace) is to variable substitute the secret data into a &lt;code>kind: Secret&lt;/code> manifest in &lt;strong>kube/deploy/&lt;/strong>.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./vars.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Cluster-specific variables.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Can be used via variable substitution at deploy-time.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;h2 id="deploy">&lt;strong>deploy/&lt;/strong>&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>Apps, services and components to be deployed to clusters.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Contains baseline common manifests that are written to work across all clusters.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Cluster-specific configuration is achieved via variable substitution and optionally patches.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>core/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Backend(-related) components that are &amp;ldquo;core&amp;rdquo; to Kubernetes clusters for them to operate, such as CNI, storage solution, Ingress Controller, and more.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>apps/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>User-facing apps and services.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>app-name/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Replace &lt;strong>app-name/&lt;/strong> with app name, such as &lt;strong>minecraft/&lt;/strong>, &lt;strong>authentik/&lt;/strong>, or &lt;strong>immich/&lt;/strong>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./ns.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Namespace definition for app, or group of apps.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./ks.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>All app-specific Fluxtomizations that deploy and control the app&amp;rsquo;s resources.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Points to either &lt;strong>app/&lt;/strong> and/or &lt;strong>component-name/&lt;/strong> folders which are explained below.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Use dependsOn to ensure that dependencies (such as storage solution and Ingress Controller) are deployed and configured before app&amp;rsquo;s resources are deployed and configured.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./repo.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Only needed if deployment method relies on Flux SourceRepository, such as HelmRepository or OCIRepository.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./kustomization.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Native Kubernetes Kustomization to control what resources are deployed, either by Fluxtomization or &lt;code>kubectl apply -k&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Used here for opting-in all YAML files but not folders, since folders are controlled by app-specific Fluxtomization which itself is controlled by &amp;ldquo;master&amp;rdquo; Fluxtomization via this and cluster&amp;rsquo;s &lt;strong>kustomization.yaml&lt;/strong>.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>app/&lt;/strong> OR &lt;strong>component-name/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>All manifests used to deploy Kubernetes resources needed for app are placed here.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Usually if both &lt;strong>app/&lt;/strong> and another folder such as &lt;strong>config/&lt;/strong> or &lt;strong>certs/&lt;/strong> are present, &lt;strong>app/&lt;/strong> is used to deploy components which are a dependency for the other folders&amp;rsquo; resources to be deployed.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./netpol.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>CiliumNetworkPolicy or Kubernetes native Network Policy&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&amp;ldquo;&lt;em>Principle of Least Privilege&lt;/em>&amp;rdquo; and &amp;ldquo;&lt;em>Default Deny&lt;/em>&amp;rdquo; practices are followed, only allow necessary connections&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Commonly allowed include Ingress Controller, intra-namespace traffic, communication with other apps&amp;rsquo; Kubernetes services &lt;em>as needed&lt;/em>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Not all Kubernetes components need to be network accessible by every application, such as APIServer, storage solution pods/services, or Flux. Assign only if such traffic is necessary for app to function.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./hr.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>HelmRelease deployment method.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul></description></item><item><title>ZFS list rough cheatsheet</title><link>https://6f8f7e98.jjgadgets-tech.pages.dev/2024/zfs-list-rough-cheatsheet/</link><pubDate>Sun, 05 Dec 2021 00:00:00 +0000</pubDate><guid>https://6f8f7e98.jjgadgets-tech.pages.dev/2024/zfs-list-rough-cheatsheet/</guid><description>&lt;h3 id="list--sort-ascending-only-total-used-space-by-all-snapshots-of-individual-datasets">List &amp;amp; sort ascending only total used space by all snapshots of individual datasets:&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">zfs list -r -o name,usedsnap -s usedsnap
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="list--sort-ascending-actual-disk-usage--disk-usage-from-specific-dataset-without-children-datasets-storage">List &amp;amp; sort ascending actual disk usage = disk usage from specific dataset without children dataset&amp;rsquo;s storage&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">zfs list -r -o name,usedds -s usedds
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="list-all-space-related-columns-available-in-zfs-list">List all space related columns available in &lt;code>zfs list&lt;/code>:&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">zfs list -ro space
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>-s [column name]&lt;/code> sorts &lt;code>zfs list&lt;/code> output in ascending order by selected column&amp;rsquo;s data, &lt;code>-S [column name]&lt;/code> sorts the same descending.&lt;/p></description></item><item><title>Archives</title><link>https://6f8f7e98.jjgadgets-tech.pages.dev/archives/</link><pubDate>Tue, 28 May 2019 00:00:00 +0000</pubDate><guid>https://6f8f7e98.jjgadgets-tech.pages.dev/archives/</guid><description/></item><item><title/><link>https://6f8f7e98.jjgadgets-tech.pages.dev/go/external-dns-fortigate-webhook/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://6f8f7e98.jjgadgets-tech.pages.dev/go/external-dns-fortigate-webhook/</guid><description/></item><item><title>About Me</title><link>https://6f8f7e98.jjgadgets-tech.pages.dev/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://6f8f7e98.jjgadgets-tech.pages.dev/about/</guid><description>&lt;h2 id="contact">Contact&lt;/h2>
&lt;p>Feel free to contact me via the profile icons on the sidebar/menu, or via the links on the bottom of the page.&lt;/p>
&lt;h2 id="about-me">About Me&lt;/h2>
&lt;p>Hey, I&amp;rsquo;m JJ. I have a deep interest in &lt;strong>Information Technology&lt;/strong>. In particular, I am passionate about the following domains, many of which are applied in my homelab Git repo (&lt;a class="link" href="https://github.com/JJGadgets/Biohazard%29" target="_blank" rel="noopener"
>https://github.com/JJGadgets/Biohazard)&lt;/a>, including but not limited to:&lt;/p>
&lt;h3 id="information-security-infosec">Information Security (&lt;strong>infosec&lt;/strong>)&lt;/h3>
&lt;ul>
&lt;li>Principle of Least Privilege&lt;/li>
&lt;li>Penetration Testing&lt;/li>
&lt;li>Finding and reporting vulnerabilities (e.g. &lt;a class="link" href="../2023/cve-2023-43809" >CVE-2023-43809/GHSA-mc97-99j4-vm2v&lt;/a>)&lt;/li>
&lt;li>System Security, such as OS hardening&lt;/li>
&lt;li>Multi-Factor Authentication with secure methods, such as WebAuthn and hardware security keys&lt;/li>
&lt;li>Encryption in transit &amp;amp; Encryption at rest&lt;/li>
&lt;li>PKI, key management&lt;/li>
&lt;li>Network security, such as firewall rules on gateways and hosts&lt;/li>
&lt;li>Integration with modern developer workflows such as DevOps &amp;amp; Kubernetes&lt;/li>
&lt;/ul>
&lt;h3 id="networking">Networking&lt;/h3>
&lt;ul>
&lt;li>Subnetting and VLANs&lt;/li>
&lt;li>Routing protocols (BGP (iBGP, DN42, eBGP with public ASN), OSPF, OpenFabric, etc)&lt;/li>
&lt;li>VPNs (WireGuard, Tailscale, OpenVPN) and tunnels (VXLAN, Geneve)&lt;/li>
&lt;/ul>
&lt;h3 id="linux">Linux&lt;/h3>
&lt;ul>
&lt;li>Desktop Linux ecosystem (e.g. Wayland, SwayWM)&lt;/li>
&lt;li>Linux command line &amp;amp; configuration&lt;/li>
&lt;li>Immutable systems like NixOS and Talos Linux&lt;/li>
&lt;/ul>
&lt;h3 id="servers">Servers&lt;/h3>
&lt;ul>
&lt;li>Dell PowerEdge&lt;/li>
&lt;li>Supermicro&lt;/li>
&lt;li>Installing and removing hardware and parts such as CPUs, RAM, drives, GPUs, NICs, etc&lt;/li>
&lt;/ul>
&lt;h3 id="infrastructure-side-of-devops">Infrastructure side of DevOps&lt;/h3>
&lt;ul>
&lt;li>Kubernetes&lt;/li>
&lt;li>Cloud computing&lt;/li>
&lt;li>CI/CD&lt;/li>
&lt;li>Technologies related to the above.&lt;/li>
&lt;/ul>
&lt;p>I enjoy exploring these domains by getting my hands dirty with these technologies in my Homelab, which features networking gear and server hardware, as well as a wide range of deployed software such as Talos Linux and OPNsense.&lt;/p></description></item><item><title>Contact Me</title><link>https://6f8f7e98.jjgadgets-tech.pages.dev/contact/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://6f8f7e98.jjgadgets-tech.pages.dev/contact/</guid><description/></item><item><title>Search</title><link>https://6f8f7e98.jjgadgets-tech.pages.dev/search/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://6f8f7e98.jjgadgets-tech.pages.dev/search/</guid><description/></item></channel></rss>