<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Terminal Thoughts</title><link>https://thoughts.greyh.at/</link><description>Recent content on Terminal Thoughts</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>@ 2023 quest</copyright><lastBuildDate>Thu, 26 Oct 2023 14:44:55 -1000</lastBuildDate><atom:link href="https://thoughts.greyh.at/index.xml" rel="self" type="application/rss+xml"/><item><title>Enhancing IPFS Performance in Kubernetes Environments</title><link>https://thoughts.greyh.at/posts/kubernetes-ipfs/</link><pubDate>Thu, 26 Oct 2023 14:44:55 -1000</pubDate><guid>https://thoughts.greyh.at/posts/kubernetes-ipfs/</guid><description>For over half a decade, I have been deploying IPFS on my Kubernetes cluster, gaining valuable insights into its operation within such an environment. Even though it&amp;rsquo;s just a modest singleton deployment, the experience has led to a deeper understanding of various nuances and potential pitfalls. This article aims to share the learned lessons and offer configuration guidelines to optimize IPFS performance in a Kubernetes setup.</description><content>&lt;p>For over half a decade, I have been deploying &lt;a href="https://ipfs.io">IPFS&lt;/a> on my Kubernetes cluster, gaining valuable insights into its operation within such an environment. Even though it&amp;rsquo;s just a modest singleton deployment, the experience has led to a deeper understanding of various nuances and potential pitfalls. This article aims to share the learned lessons and offer configuration guidelines to optimize IPFS performance in a Kubernetes setup.&lt;/p>
&lt;h2 id="kubernetes-configurations">Kubernetes Configurations&lt;/h2>
&lt;p>You can access my most recent &lt;a href="(https://github.com/zquestz/kube-ipfs)">IPFS Kubernetes configurations&lt;/a> on GitHub. These configurations are tailored for deployment on a &lt;a href="https://cloud.google.com/kubernetes-engine">Google Kubernetes Engine (GKE)&lt;/a> cluster, accompanied by &lt;a href="https://github.com/cert-manager/cert-manager">cert-manager&lt;/a>. However, the flexibility of Kubernetes means that these configurations also serve as a solid foundation for deployments on bare-metal or other cloud providers.&lt;/p>
&lt;p>Staying updated with the latest IPFS releases is crucial to leverage new features and security updates. A noteworthy point is that IPFS does not trigger migrations automatically by default. Therefore, it&amp;rsquo;s essential to include the &lt;code>--migrate&lt;/code> flag in your deployment configuration. This flag is especially crucial if you opt for the &lt;code>ipfs/kubo:release&lt;/code> image, as without it, a new release could halt your node from booting up.&lt;/p>
&lt;p>If you venture into setting up a public gateway, be aware that it can attract a significant amount of traffic. Publicizing a gateway should only be done if you are well-equipped to manage the potential surge in traffic.&lt;/p>
&lt;h2 id="delving-into-ipfs-configuration">Delving into IPFS Configuration&lt;/h2>
&lt;p>Getting IPFS up and running is just the initial step. To ensure it operates correctly, a handful of configuration adjustments are imperative. Otherwise you will only be able to make outgoing connections, and your peer count will be extremely limited.&lt;/p>
&lt;p>The first task is to navigate to &lt;code>/data/ipfs/config&lt;/code> within the pod and amend the &lt;code>Addresses&lt;/code> configuration to reflect the correct WAN IP and/or DNS. The default setup may not accurately detect these, which in turn would impede ingress connections unless rectified.&lt;/p>
&lt;p>In my setup, I have mirrored the configuration for &lt;code>Announce&lt;/code> and &lt;code>AppendAnnounce&lt;/code>, to make sure the correct IPs are announced to the network. Within the IPFS codebase, these are deduplicated, but I prefer to be redundant, as this has broken for me in the past.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;Addresses&amp;#34;&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;API&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;/ip4/0.0.0.0/tcp/5001&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Announce&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/ip4/35.209.82.27/tcp/4001&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/ip4/35.209.82.27/udp/4001/quic-v1&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/ip4/35.209.82.27/udp/4001/quic-v1/webtransport&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/dns/ipfs-swarm.greyh.at/tcp/4001&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/dns/ipfs-swarm.greyh.at/udp/4001/quic-v1&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/dns/ipfs-swarm.greyh.at/udp/4001/quic-v1/webtransport&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;AppendAnnounce&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/ip4/35.209.82.27/tcp/4001&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/ip4/35.209.82.27/udp/4001/quic-v1&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/ip4/35.209.82.27/udp/4001/quic-v1/webtransport&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/dns/ipfs-swarm.greyh.at/tcp/4001&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/dns/ipfs-swarm.greyh.at/udp/4001/quic-v1&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/dns/ipfs-swarm.greyh.at/udp/4001/quic-v1/webtransport&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Gateway&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;/ip4/0.0.0.0/tcp/8080&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;NoAnnounce&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">null&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Swarm&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/ip4/0.0.0.0/tcp/4001&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/ip4/0.0.0.0/udp/4001/quic-v1&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;/ip4/0.0.0.0/udp/4001/quic-v1/webtransport&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;span style="color:#960050;background-color:#1e0010">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Although these configurations seem comprehensive, there&amp;rsquo;s one more critical setting to adjust, the &lt;code>RelayClient&lt;/code>. This needs to be disabled, or else none of the preceding configurations, including &lt;code>AppendAnnounce&lt;/code>, will function!&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;RelayClient&amp;#34;&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">:&lt;/span> { &lt;span style="color:#f92672">&amp;#34;Enabled&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">false&lt;/span> }&lt;span style="color:#960050;background-color:#1e0010">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Additionally, I have customized the &lt;code>Access-Control-Allow-Origin&lt;/code> header on my Gateway to ensure the desired level of access control.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;Gateway&amp;#34;&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;APICommands&amp;#34;&lt;/span>: [],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;DeserializedResponses&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">null&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;HTTPHeaders&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Access-Control-Allow-Origin&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;*&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;NoDNSLink&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">false&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;NoFetch&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">false&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;PathPrefixes&amp;#34;&lt;/span>: [],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;PublicGateways&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">null&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;RootRedirect&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;span style="color:#960050;background-color:#1e0010">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Lastly, the &lt;code>StorageMax&lt;/code> parameter should be set in line with your mounted disk size to prevent any storage issues.&lt;/p>
&lt;p>Upon fine-tuning all the necessary configurations, a restart of IPFS is required to apply the changes:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>kubectl rollout restart deployment/ipfs
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>With these configurations in place, IPFS should now operate more efficiently within a Kubernetes environment, ensuring better performance and reliability.&lt;/p>
&lt;h2 id="optimizing-routing-performance">Optimizing Routing Performance&lt;/h2>
&lt;p>When hosting content on your IPFS setup, enabling the &lt;code>AcceleratedDHTClient&lt;/code> is a wise choice. Though it increases CPU usage, it significantly enhances the advertisement of your content across the network, ensuring better accessibility and performance.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;Routing&amp;#34;&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;AcceleratedDHTClient&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;span style="color:#960050;background-color:#1e0010">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="addressing-network-hiccups">Addressing Network Hiccups&lt;/h2>
&lt;p>Upon close inspection, you might notice an error indicating that the UDP receive buffer size is inadequate during IPFS startup. The error message would look something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>failed to sufficiently increase receive buffer size
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(was: 208 kiB, wanted: 2048 kiB, got: 416 kiB).
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Resolving this requires tweaking a few &lt;code>sysctl&lt;/code> settings on our node pool machines to ensure a smooth operation.&lt;/p>
&lt;p>A comprehensive guide to adjust system configurations can be found on the &lt;a href="https://cloud.google.com/kubernetes-engine/docs/how-to/node-system-config">Google Cloud documentation&lt;/a>. Following this guide, I adjusted the sysctl settings as shown below:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">kubeletConfig&lt;/span>: {}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">linuxConfig&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">sysctl&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">net.core.rmem_max&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;2500000&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">net.core.wmem_max&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;2500000&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To apply these configurations, utilize the &lt;code>gcloud&lt;/code> command-line interface as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>gcloud container node-pools update POOL_NAME &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span>--cluster CLUSTER_NAME --zone ZONE_NAME &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span>--project PROJECT_NAME --system-config-from-file sysctl.yaml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After executing the above command, allow a few minutes for your node pool machines to reboot with the updated settings. IPFS will be automatically migrated to one of the new machines, and upon startup, the previous error should no longer appear.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>We&amp;rsquo;ve journeyed through the technical terrain of tuning IPFS on Kubernetes, addressing configurations, fixing the UDP buffer error, and ramping up routing with &lt;code>AcceleratedDHTClient&lt;/code>. Now, with a finely-tuned setup, we&amp;rsquo;re all set to delve into the decentralized web, serving content with enhanced efficiency and reliability.&lt;/p></content></item><item><title>SSH Hardening with ssh-audit</title><link>https://thoughts.greyh.at/posts/ssh-audit/</link><pubDate>Sun, 15 Oct 2023 18:16:52 -1000</pubDate><guid>https://thoughts.greyh.at/posts/ssh-audit/</guid><description>Today I explored ssh-audit, a tool designed to audit SSH configurations. Although it’s an excellent tool, I found the hardening guides somewhat lacking. Hence, I decided to write a detailed walkthrough, ensuring the ssh/sshd configurations are easily readable.</description><content>&lt;p>Today I explored &lt;a href="https://github.com/jtesta/ssh-audit">ssh-audit&lt;/a>, a tool designed to audit SSH configurations. Although it&amp;rsquo;s an excellent tool, I found the &lt;a href="https://www.ssh-audit.com/hardening_guides.html">hardening guides&lt;/a> somewhat lacking. Hence, I decided to write a detailed walkthrough, ensuring the ssh/sshd configurations are easily readable.&lt;/p>
&lt;p>The ssh-audit tool is important for several reasons:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>SSH Protocol Analysis&lt;/strong>: The tool can identify the SSH protocol version and provide details about the supported key exchange, encryption, MAC, and compression algorithms. This is crucial for understanding the security posture of an SSH server.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Security Vulnerability Detection&lt;/strong>: ssh-audit can detect known vulnerabilities in the SSH server. This helps administrators identify and patch potential security risks.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Algorithm Recommendations&lt;/strong>: Based on the analysis, ssh-audit provides recommendations on which algorithms should be enabled or disabled to enhance security.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="getting-started">Getting Started&lt;/h2>
&lt;p>Setting up ssh-audit is straightforward. Simply follow the installation instructions on their &lt;a href="https://github.com/jtesta/ssh-audit">project page&lt;/a>, and you&amp;rsquo;ll be ready in no time.&lt;/p>
&lt;p>While it&amp;rsquo;s not mentioned on their GitHub, there&amp;rsquo;s a package available on the &lt;a href="https://aur.archlinux.org">Arch Linux User Repository&lt;/a>. You can install it using your preferred AUR helper:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>yay -S ssh-audit
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="running-ssh-audit">Running ssh-audit&lt;/h2>
&lt;p>After installation, ensure sshd is active. Then, execute ssh-audit with the command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>ssh-audit localhost
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This command produces a comprehensive security report. Pay special attention to the lines highlighted in red and yellow, as these indicate areas requiring attention.&lt;/p>
&lt;p>Here&amp;rsquo;s a snapshot of my algorithm recommendations. While yours might differ slightly, the approach to resolving issues remains the same.&lt;/p>
&lt;p>&lt;img alt="Initial Audit" src="https://thoughts.greyh.at/posts/ssh-audit/images/initial.webp" width=585 height=346 loading="lazy" />&lt;/p>
&lt;p>Observe the three primary types to eliminate: &lt;code>kex&lt;/code>, &lt;code>mac&lt;/code>, and &lt;code>key&lt;/code>. These correspond to &lt;code>KexAlgorithms&lt;/code>, &lt;code>MACs&lt;/code>, and &lt;code>HostKeyAlgorithms&lt;/code> in your sshd configuration.&lt;/p>
&lt;h2 id="updating-sshd">Updating sshd&lt;/h2>
&lt;p>Armed with a list of issues, it&amp;rsquo;s time to modify our sshd configuration. I created a file &lt;code>/etc/ssh/sshd_config.d/90-hardening.conf&lt;/code> with the content:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-conf" data-lang="conf">KexAlgorithms -diffie-hellman-group14-sha256,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521
Macs -hmac-sha1,hmac-sha1-etm@openssh.com,hmac-sha2-256,hmac-sha2-512,umac-128@openssh.com,umac-64-etm@openssh.com,umac-64@openssh.com
HostKeyAlgorithms -ecdsa-sha2-nistp256
&lt;/code>&lt;/pre>&lt;p>By prefixing values with &lt;code>-&lt;/code>, we ensure the removal of insecure configurations from the default set.&lt;/p>
&lt;p>After updating, restart sshd and run &lt;code>ssh-audit localhost&lt;/code>. You should no longer see any security warnings!&lt;/p>
&lt;h2 id="updating-ssh">Updating ssh&lt;/h2>
&lt;p>While your sshd is now more secure, it&amp;rsquo;s essential to ensure your ssh client also employs a secure configuration.&lt;/p>
&lt;p>In one terminal window, enter:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-zsh" data-lang="zsh">&lt;span style="display:flex;">&lt;span>ssh-audit -c
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In another window, connect to the ssh-audit process by accessing port 2222:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-zsh" data-lang="zsh">&lt;span style="display:flex;">&lt;span>ssh -p &lt;span style="color:#ae81ff">2222&lt;/span> localhost
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This will display a report on your ssh client. Here are the recommendations from my initial run:&lt;/p>
&lt;p>&lt;img alt="Initial Client Audit" src="https://thoughts.greyh.at/posts/ssh-audit/images/initial-client.webp" width=980 height=552 loading="lazy" />&lt;/p>
&lt;p>Again, we encounter the same &lt;code>kex&lt;/code>, &lt;code>mac&lt;/code>, and &lt;code>key&lt;/code> types. They map to &lt;code>KexAlgorithms&lt;/code>, &lt;code>MACs&lt;/code>, and &lt;code>HostKeyAlgorithms&lt;/code> in your ssh configuration.&lt;/p>
&lt;p>I created a file &lt;code>/etc/ssh/ssh_config.d/90-hardening.conf&lt;/code> with the following:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-conf" data-lang="conf">KexAlgorithms -diffie-hellman-group14-sha256,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521
Macs -hmac-sha1,hmac-sha1-etm@openssh.com,hmac-sha2-256,hmac-sha2-512,umac-128@openssh.com,umac-64-etm@openssh.com,umac-64@openssh.com
HostKeyAlgorithms -ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521,ecdsa-sha2-nistp521-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256@openssh.com
&lt;/code>&lt;/pre>&lt;p>When you execute &lt;code>ssh-audit -c&lt;/code> and conduct your tests, you should no longer encounter any security warnings!&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>SSH hardening is an ongoing process, and while tools like ssh-audit provide a solid foundation, it&amp;rsquo;s essential to stay updated with the latest security practices. Regularly auditing and updating your configurations will ensure a safer digital environment.&lt;/p></content></item><item><title>Introducing Shinobi Art Engine</title><link>https://thoughts.greyh.at/posts/shinobi/</link><pubDate>Fri, 29 Sep 2023 13:25:39 -1000</pubDate><guid>https://thoughts.greyh.at/posts/shinobi/</guid><description>Today I am thrilled to introduce the Shinobi Art Engine, the ultimate tool for creating NFT collections. This is an extension of the amazing work done by the HashLips team, known for their pioneering efforts in NFT art generation, and it&amp;rsquo;s the first tool to support both BCH and ETH NFT collections. The CashNinjas team has been working on this project for a while now and we are excited to share it with the world.</description><content>&lt;p>Today I am thrilled to introduce the &lt;a href="https://github.com/cashninjas/shinobi-art-engine">Shinobi Art Engine&lt;/a>, the ultimate tool for creating NFT collections. This is an extension of the amazing work done by the &lt;a href="https://hashlips.io/">HashLips&lt;/a> team, known for their pioneering efforts in NFT art generation, and it&amp;rsquo;s the first tool to support both BCH and ETH NFT collections. The CashNinjas team has been working on this project for a while now and we are excited to share it with the world.&lt;/p>
&lt;h2 id="about-shinobi-art-engine">About Shinobi Art Engine&lt;/h2>
&lt;p>Shinobi Art Engine serves as a bridge connecting creativity with blockchain technology. The engine facilitates the seamless creation of NFT collections, and by lowering the entry barrier for NFT creators, we aspire to foster a diverse and vibrant digital art ecosystem.&lt;/p>
&lt;p>We added quite a few new features to the original HashLips engine, including:&lt;/p>
&lt;ul>
&lt;li>Support for BCH &lt;a href="https://cashtokens.org">CashTokens&lt;/a> NFT projects with &lt;a href="https://cashtokens.org/docs/bcmr/chip/">Bitcoin Cash Metadata Registries&lt;/a> (BCMR).&lt;/li>
&lt;li>Icon generation for ETH and BCH projects.&lt;/li>
&lt;li>Updated to use ES module imports instead of &lt;code>require()&lt;/code> statements.&lt;/li>
&lt;li>New Shinobi example layers!&lt;/li>
&lt;li>OpenAI integration to generate names and descriptions!&lt;/li>
&lt;li>Tons of small code cleanups and optimizations!&lt;/li>
&lt;/ul>
&lt;h2 id="getting-started">Getting Started&lt;/h2>
&lt;p>Embarking on your NFT creation journey with Shinobi Art Engine is straightforward. Our &lt;a href="https://github.com/cashninjas/shinobi-art-engine">GitHub repository&lt;/a> provides an extensive &lt;code>README.md&lt;/code> to guide you, along with sample layers to play around with. Follow the step-by-step walkthrough to build the included Shinobi NFT collection, and unleash your creativity with the array of tools and features at your disposal.&lt;/p>
&lt;p>For those that just want a quick breakdown, here is how you would get started:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-zsh" data-lang="zsh">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Clone the repository&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git clone https://github.com/cashninjas/shinobi-art-engine.git
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Change directory to the repository&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd shinobi-art-engine
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Install dependencies&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>yarn install
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Build Shinobi!&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>npm run build
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can now look in the &lt;code>build&lt;/code> directory for your newly generated Shinobi collection!&lt;/p>
&lt;p>Now, if you look at the metadata, your Shinobi&amp;rsquo;s aren&amp;rsquo;t actually that interesting! They don&amp;rsquo;t have unique names, and they all contain the same description!&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Shinobi #1&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;description&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Elite digital ninja, guardian of BCH.&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;image&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;ipfs://NewUriToReplace/images/1.png&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;dna&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;9b1e5fd771a69adf90a363ea31c04b8785a1171c&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;edition&amp;#34;&lt;/span>: &lt;span style="color:#ae81ff">1&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;date&amp;#34;&lt;/span>: &lt;span style="color:#ae81ff">1696035479989&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;imageHash&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;558811fb10667a77e3a59051d704f208a0443091010876bc5daf7788fbeb716c&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;attributes&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;trait_type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Background&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;value&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Dark Forest&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;trait_type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Glow&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;value&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Red&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;trait_type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Weapons&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;value&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Scythe&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;trait_type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Body&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;value&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Shadow&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;trait_type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Eyes&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;value&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;White&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;icon&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;ipfs://NewUriToReplace/icons/1.png&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;iconHash&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;ba789fd47be7a9ce8ec139143ad61d26b3d1cce272d31ae9dfdcd44b7f3ca267&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now lets generate new names using OpenAI&amp;rsquo;s GPT-4 API!&lt;/p>
&lt;p>First, you will need to &lt;a href="https://platform.openai.com/apps">setup an OpenAI API key&lt;/a>. Then make sure the &lt;code>OPENAI_API_KEY&lt;/code> env var is set or added to your &lt;code>.env&lt;/code> file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-zsh" data-lang="zsh">&lt;span style="display:flex;">&lt;span>npm run openai
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The output should look like this:&lt;/p>
&lt;pre tabindex="0">&lt;code>&amp;gt; shinobi_art_engine@1.0.0 openai
&amp;gt; node utils/openai.js
Updating #1
Updating #2
Updating #3
Updating #4
Updating #5
Updating #6
Updating #7
Updating #8
Updating #9
Updating #10
Updating #11
Updated names using OpenAI
Updated descriptions using OpenAI
&lt;/code>&lt;/pre>&lt;p>Taking a look at the same JSON file again, shows much better data! We now have a cool unique name, and a description that is based off the NFT traits! Just compare it to the image!&lt;/p>
&lt;p>&lt;img alt="Harbinger of the Dark Forest" src="https://thoughts.greyh.at/posts/shinobi/images/harbinger.webp" width=512 height=512 loading="lazy" />&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Shinobi #1 - Harbinger of the Dark Forest&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;description&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Emerging from the mysterious shadows of the Dark Forest, this Shinobi is an elite guardian of the BCH blockchain. Emanating a powerful Red Glow, his presence commands respect and fear in equal measures. Armored in a Stealth Shadow Body, this ninja is almost invisible till he strikes with his terrifying Scythe. His White Eyes, unyielding and resolute, burn brightly in the dark, a testament to his unwavering commitment to maintain the balance and integrity of the decentralized world. Like a phantom of the night, he stands as a powerful avatar of Satoshi&amp;#39;s will.&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;image&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;ipfs://NewUriToReplace/images/1.png&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;dna&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;9b1e5fd771a69adf90a363ea31c04b8785a1171c&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;edition&amp;#34;&lt;/span>: &lt;span style="color:#ae81ff">1&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;date&amp;#34;&lt;/span>: &lt;span style="color:#ae81ff">1696035479989&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;imageHash&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;558811fb10667a77e3a59051d704f208a0443091010876bc5daf7788fbeb716c&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;attributes&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;trait_type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Background&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;value&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Dark Forest&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;trait_type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Glow&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;value&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Red&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;trait_type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Weapons&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;value&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Scythe&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;trait_type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Body&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;value&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Shadow&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;trait_type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Eyes&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;value&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;White&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;icon&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;ipfs://NewUriToReplace/icons/1.png&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;iconHash&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;ba789fd47be7a9ce8ec139143ad61d26b3d1cce272d31ae9dfdcd44b7f3ca267&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see, our Shinobi now has a backstory!&lt;/p>
&lt;blockquote>
&lt;p>Emerging from the mysterious shadows of the Dark Forest, this Shinobi is an elite guardian of the BCH blockchain. Emanating a powerful Red Glow, his presence commands respect and fear in equal measures. Armored in a Stealth Shadow Body, this ninja is almost invisible till he strikes with his terrifying Scythe. His White Eyes, unyielding and resolute, burn brightly in the dark, a testament to his unwavering commitment to maintain the balance and integrity of the decentralized world. Like a phantom of the night, he stands as a powerful avatar of Satoshi&amp;rsquo;s will.&lt;/p>
&lt;/blockquote>
&lt;p>The new descriptions will be significantly different for every NFT collection and is based off values in the configuration file and the attributes of the NFTs. These are not cookie cutter descriptions!&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Shinobi Art Engine is more than just a tool; it&amp;rsquo;s a canvas where your imagination takes the lead. We invite you to dive into the Shinobi Art Engine today, and start crafting your unique NFT collections. Be sure to also explore the &lt;a href="https://ninjas.cash">CashNinjas website&lt;/a> to stay updated on our latest projects and initiatives. Together, let&amp;rsquo;s redefine the boundaries of digital artistry in the NFT realm.&lt;/p></content></item><item><title>kitty, Starship and Terminal Customization</title><link>https://thoughts.greyh.at/posts/starship/</link><pubDate>Wed, 27 Sep 2023 16:31:39 -1000</pubDate><guid>https://thoughts.greyh.at/posts/starship/</guid><description>As someone who spends a significant chunk of time immersed in terminal windows, constantly tweaking and optimizing, I thought I had achieved an ideal setup with zsh and oh-my-zsh. However, in the spirit of continuous improvement, I recently embarked on a quest for re-evaluation and potential enhancement.</description><content>&lt;p>As someone who spends a significant chunk of time immersed in terminal windows, constantly tweaking and optimizing, I thought I had achieved an ideal setup with zsh and &lt;a href="https://ohmyz.sh/">oh-my-zsh&lt;/a>. However, in the spirit of continuous improvement, I recently embarked on a quest for re-evaluation and potential enhancement.&lt;/p>
&lt;h2 id="re-evaluating-terminal-emulators">Re-Evaluating Terminal Emulators&lt;/h2>
&lt;p>Though I had been a long-time user of the Gnome Terminal (with transparency patches), I decided it was time to reassess the myriad of terminal emulator options available for Linux — all of which I had previously encountered in some capacity. My exploration included &lt;a href="https://gnome-terminator.org/">Terminator&lt;/a>, &lt;a href="https://alacritty.org/">Alacritty&lt;/a>, &lt;a href="https://sw.kovidgoyal.net/kitty/">kitty&lt;/a>, rxvt, and xterm, among others.&lt;/p>
&lt;p>This wasn’t a mere surface-level trial; each emulator was meticulously configured to mirror my preferences, with a deep dive into their documentation to ensure a comprehensive understanding of their respective features. My findings? kitty unequivocally stood out as the superior option, meeting the essential criteria:&lt;/p>
&lt;ul>
&lt;li>Speedy and responsive performance&lt;/li>
&lt;li>Support for split windows, tabs, and transparency&lt;/li>
&lt;li>Smooth scrolling with functional page-up/page-down&lt;/li>
&lt;li>High-quality font support&lt;/li>
&lt;/ul>
&lt;p>kitty not only fulfilled, but exceeded expectations. Below is a copy of my &lt;code>~/.config/kitty/kitty.conf&lt;/code> for those interested:&lt;/p>
&lt;pre tabindex="0">&lt;code>background_opacity 0.9
font_size 10.0
font_family SauceCodePro Nerd Font Mono
bold_font auto
italic_font auto
bold_italic_font auto
tab_bar_style powerline
tab_powerline_style slanted
tab_bar_edge top
tab_bar_margin_height 0.0 0.0
map kitty_mod+enter new_window_with_cwd
map kitty_mod+n new_os_window_with_cwd
map kitty_mod+t new_tab_with_cwd
# BEGIN_KITTY_THEME
# Wez
include current-theme.conf
# END_KITTY_THEME
&lt;/code>&lt;/pre>&lt;p>A noteworthy feature of the config, are the &lt;code>map&lt;/code> commands, allowing newly opened terminals to inherit the current working directory, a practical deviation from the default home directory.&lt;/p>
&lt;p>Adjusting themes is a breeze with &lt;code>kitty +kitten themes&lt;/code>, providing an intuitive theme selector while automatically updating your &lt;code>kitty.conf&lt;/code>.&lt;/p>
&lt;p>&lt;img alt="kitten themes" src="https://thoughts.greyh.at/posts/starship/images/kitten-themes.webp" width=1225 height=721 loading="lazy" />&lt;/p>
&lt;p>Additionally, kitty supports image rendering in the terminal through &lt;code>kitty +kitten icat &amp;lt;file&amp;gt;&lt;/code> and offers integration with &lt;code>w3m&lt;/code> and other terminal applications, detailed on their &lt;a href="https://sw.kovidgoyal.net/kitty/integrations/">website&lt;/a>.&lt;/p>
&lt;h2 id="revamping-prompts-with-starship">Revamping Prompts with Starship&lt;/h2>
&lt;p>Now that I had a shiny new terminal emulator, my attention shifted to enhancing my zsh/bash prompts. Although &lt;a href="https://starship.rs/">Starship&lt;/a> was on my radar, I had not dedicated time to thoroughly explore its capabilities until now.&lt;/p>
&lt;p>While its out-of-the-box appearance felt cluttered, a few adjustments revealed Starship&amp;rsquo;s true potential, aligning seamlessly with my kitty theme. The final configuration, significantly influenced by the Nerd Font Symbols and Bracketed Segments Starship presets, resulted in a prompt that is as functional as it is aesthetically pleasing.&lt;/p>
&lt;p>&lt;img alt="starship prompt" src="https://thoughts.greyh.at/posts/starship/images/prompt.webp" width=658 height=78 loading="lazy" />&lt;/p>
&lt;p>For those keen to review my extensive Starship configuration, see the collapsed section below:&lt;/p>
&lt;div class="collapsable-code">
&lt;input id="1" type="checkbox" checked />
&lt;label for="1">
&lt;span class="collapsable-code__language">toml&lt;/span>
&lt;span class="collapsable-code__title">~/config/starship.toml&lt;/span>
&lt;span class="collapsable-code__toggle" data-label-expand="Show" data-label-collapse="Hide">&lt;/span>
&lt;/label>
&lt;pre class="language-toml" >&lt;code>
[aws]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($profile)(\($region\))(\[$duration\])]($style)\]&amp;#39;
[buf]
symbol = &amp;#34; &amp;#34;
[bun]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[c]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($version(-$name))]($style)\]&amp;#39;
[cmake]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[cmd_duration]
format = &amp;#39;\[[󰔛 $duration]($style)\]&amp;#39;
[cobol]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[conda]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol$environment]($style)\]&amp;#39;
[crystal]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[daml]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[dart]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[deno]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[directory]
read_only = &amp;#34; 󰌾&amp;#34;
format = &amp;#39;[$before_root_path]($before_repo_root_style)[$repo_root]($repo_root_style)[$path]($style)[$read_only]($read_only_style) &amp;#39;
[directory.substitutions]
&amp;#34;Android&amp;#34; = &amp;#34;&amp;#34;
&amp;#34;Desktop&amp;#34; = &amp;#34;&amp;#34;
&amp;#34;Documents&amp;#34; = &amp;#34;󰈙&amp;#34;
&amp;#34;Downloads&amp;#34; = &amp;#34;󰇚&amp;#34;
&amp;#34;Dropbox&amp;#34; = &amp;#34;&amp;#34;
&amp;#34;Games&amp;#34; = &amp;#34;&amp;#34;
&amp;#34;Music&amp;#34; = &amp;#34;&amp;#34;
&amp;#34;Pictures&amp;#34; = &amp;#34;&amp;#34;
&amp;#34;Videos&amp;#34; = &amp;#34;&amp;#34;
[docker_context]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol$context]($style)\]&amp;#39;
[dotnet]
format = &amp;#39;\[[$symbol($version)($tfm)]($style)\]&amp;#39;
[elixir]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($version \(OTP $otp_version\))]($style)\]&amp;#39;
[elm]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[erlang]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[fennel]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[fossil_branch]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol$branch]($style)\]&amp;#39;
[gcloud]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol$account(@$domain)(\($region\))]($style)\]&amp;#39;
disabled = true
[git_branch]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol$branch]($style)\]&amp;#39;
[git_status]
format = &amp;#39;([\[$all_status$ahead_behind\]]($style))&amp;#39;
[golang]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[gradle]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[guix_shell]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol]($style)\]&amp;#39;
[haskell]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[haxe]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[helm]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[hg_branch]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol$branch]($style)\]&amp;#39;
[hostname]
ssh_symbol = &amp;#34;󰣀 &amp;#34;
ssh_only = false
format = &amp;#39;[$ssh_symbol$hostname]($style):&amp;#39;
style = &amp;#39;bright-green&amp;#39;
[java]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[julia]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[kotlin]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[kubernetes]
format = &amp;#39;\[[$symbol$context( \($namespace\))]($style)\]&amp;#39;
[lua]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[memory_usage]
symbol = &amp;#34;󰍛 &amp;#34;
format = &amp;#39;\[$symbol[$ram( | $swap)]($style)\]&amp;#39;
[meson]
symbol = &amp;#34;󰔷 &amp;#34;
format = &amp;#39;\[[$symbol$project]($style)\]&amp;#39;
[nim]
symbol = &amp;#34;󰆥 &amp;#34;
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[nix_shell]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol$state( \($name\))]($style)\]&amp;#39;
[nodejs]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[ocaml]
format = &amp;#39;\[[$symbol($version)(\($switch_indicator$switch_name\))]($style)\]&amp;#39;
[opa]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[openstack]
format = &amp;#39;\[[$symbol$cloud(\($project\))]($style)\]&amp;#39;
[os]
format = &amp;#39;\[[$symbol]($style)\]&amp;#39;
[os.symbols]
Alpaquita = &amp;#34; &amp;#34;
Alpine = &amp;#34; &amp;#34;
Amazon = &amp;#34; &amp;#34;
Android = &amp;#34; &amp;#34;
Arch = &amp;#34; &amp;#34;
Artix = &amp;#34; &amp;#34;
CentOS = &amp;#34; &amp;#34;
Debian = &amp;#34; &amp;#34;
DragonFly = &amp;#34; &amp;#34;
Emscripten = &amp;#34; &amp;#34;
EndeavourOS = &amp;#34; &amp;#34;
Fedora = &amp;#34; &amp;#34;
FreeBSD = &amp;#34; &amp;#34;
Garuda = &amp;#34;󰛓 &amp;#34;
Gentoo = &amp;#34; &amp;#34;
HardenedBSD = &amp;#34;󰞌 &amp;#34;
Illumos = &amp;#34;󰈸 &amp;#34;
Linux = &amp;#34; &amp;#34;
Mabox = &amp;#34; &amp;#34;
Macos = &amp;#34; &amp;#34;
Manjaro = &amp;#34; &amp;#34;
Mariner = &amp;#34; &amp;#34;
MidnightBSD = &amp;#34; &amp;#34;
Mint = &amp;#34; &amp;#34;
NetBSD = &amp;#34; &amp;#34;
NixOS = &amp;#34; &amp;#34;
OpenBSD = &amp;#34;󰈺 &amp;#34;
openSUSE = &amp;#34; &amp;#34;
OracleLinux = &amp;#34;󰌷 &amp;#34;
Pop = &amp;#34; &amp;#34;
Raspbian = &amp;#34; &amp;#34;
Redhat = &amp;#34; &amp;#34;
RedHatEnterprise = &amp;#34; &amp;#34;
Redox = &amp;#34;󰀘 &amp;#34;
Solus = &amp;#34;󰠳 &amp;#34;
SUSE = &amp;#34; &amp;#34;
Ubuntu = &amp;#34; &amp;#34;
Unknown = &amp;#34; &amp;#34;
Windows = &amp;#34;󰍲 &amp;#34;
[package]
symbol = &amp;#34;󰏗 &amp;#34;
format = &amp;#39;\[[$symbol$version]($style)\]&amp;#39;
[perl]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[php]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[pijul_channel]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol$channel]($style)\]&amp;#39;
[pulumi]
format = &amp;#39;\[[$symbol$stack]($style)\]&amp;#39;
[purescript]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[python]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[${symbol}${pyenv_prefix}(${version})(\($virtualenv\))]($style)\]&amp;#39;
[rlang]
symbol = &amp;#34;󰟔 &amp;#34;
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[raku]
format = &amp;#39;\[[$symbol($version-$vm_version)]($style)\]&amp;#39;
[red]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[ruby]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[rust]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[scala]
symbol = &amp;#34; &amp;#34;
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[spack]
format = &amp;#39;\[[$symbol$environment]($style)\]&amp;#39;
[sudo]
format = &amp;#39;\[[as $symbol]($style)\]&amp;#39;
[swift]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[terraform]
format = &amp;#39;\[[$symbol$workspace]($style)\]&amp;#39;
[time]
format = &amp;#39;\[[$time]($style)\]&amp;#39;
[username]
format = &amp;#39;[$user@]($style)&amp;#39;
show_always = true
style_user = &amp;#39;bright-green&amp;#39;
style_root = &amp;#39;bright red&amp;#39;
[vagrant]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[vlang]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[zig]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
[solidity]
format = &amp;#39;\[[$symbol($version)]($style)\]&amp;#39;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>This re-evaluation was not just about discovering new tools but revisiting and reassessing familiar ones with a fresh perspective. It was a day well spent, culminating in a terminal experience that is more refined and personalized than ever. I encourage you to periodically revisit and re-evaluate your tools, you might be surprised by the newfound appreciation and potential improvements you’ll uncover!&lt;/p></content></item><item><title>Searching for the Perfect Keyboard</title><link>https://thoughts.greyh.at/posts/perfect-keyboard/</link><pubDate>Thu, 24 Aug 2023 00:00:00 +0000</pubDate><guid>https://thoughts.greyh.at/posts/perfect-keyboard/</guid><description>For years, I&amp;rsquo;ve been using a CODE Keyboard equipped with Cherry MX Clear switches. It has served me faithfully, but recently, I&amp;rsquo;ve felt the need to try something different. Given the plethora of options now available, I embarked on a quest to find the perfect keyboard.</description><content>&lt;p>For years, I&amp;rsquo;ve been using a &lt;a href="https://codekeyboards.com/">CODE Keyboard&lt;/a> equipped with &lt;a href="https://www.cherrymx.de/en/cherry-mx/mx-special/mx-clear.html">Cherry MX Clear&lt;/a> switches. It has served me faithfully, but recently, I&amp;rsquo;ve felt the need to try something different. Given the plethora of options now available, I embarked on a quest to find the perfect keyboard.&lt;/p>
&lt;p>Initially, I had to decide on the size. Having used a full-size keyboard, I was certain I wanted something more compact, but arrow keys were non-negotiable. Thus, I settled on a 65% keyboard design.&lt;/p>
&lt;p>I also considered the &lt;a href="https://ultimatehackingkeyboard.com/">Ultimate Hacking Keyboard&lt;/a>, even going so far as to order their &lt;a href="https://ultimatehackingkeyboard.com/product/switch-tester">Switch Tester&lt;/a>. However, I ultimately decided against purchasing one. For the curious, I found their white switches quite appealing.&lt;/p>
&lt;p>The mechanical keyboard market is vast. I spent days researching and finally decided to order a few for comparison.&lt;/p>
&lt;h2 id="the-ordering-begins">The Ordering Begins&lt;/h2>
&lt;p>Knowing my affinity for Cherry MX Clear switches, I wanted at least one of my new keyboards to feature them. I&amp;rsquo;m not one for flashy gadgets, and the sheer simplicity of the &lt;a href="https://vortexgear.store/products/cypher-single-spacebar-us1">Vortex Keyboard Cypher Single Spacebar US1&lt;/a> caught my eye. Plus, keyboards with Cherry MX Clear switches were becoming rare. So, that was my first order.&lt;/p>
&lt;p>But my shopping spree wasn&amp;rsquo;t over. I needed for something unique. The &lt;a href="https://www.gloriousgaming.com/products/gmmk2">GMMK 2&lt;/a> offered full customization, and I couldn&amp;rsquo;t resist designing my own. My order included:&lt;/p>
&lt;ul>
&lt;li>GMMK 2 Barebones Gaming Keyboard&lt;/li>
&lt;li>Glorious Panda Tactile Switches&lt;/li>
&lt;li>Coiled Keyboard Cable&lt;/li>
&lt;li>US (ANSI) Base Set / Black GMMK ABS Doubleshot Keycaps v2&lt;/li>
&lt;li>Black Ash GPBT - Premium Dye Sub Keycaps&lt;/li>
&lt;li>Padded Keyboard Wrist Rest (Stealth)&lt;/li>
&lt;li>Keyboard Carrying Case&lt;/li>
&lt;/ul>
&lt;p>Admittedly, I went a tad overboard, but the idea of assembling my own keyboard was too exciting to pass up.&lt;/p>
&lt;p>Lastly, I chose the &lt;a href="https://drop.com/buy/drop-alt-mechanical-keyboard">Drop ALT Mechanical Keyboard&lt;/a> due to its aesthetic appeal and stellar reviews. I opted for Cherry MX Brown switches to diversify my collection.&lt;/p>
&lt;h2 id="the-arrivals">The Arrivals&lt;/h2>
&lt;p>The Vortex Keyboard was the first to arrive.&lt;/p>
&lt;p>&lt;img alt="Vortex Keyboard Cypher Single Spacebar US1" src="https://thoughts.greyh.at/posts/perfect-keyboard/images/vortex.webp" width=1200 height=478 loading="lazy" />&lt;/p>
&lt;p>Despite its all-plastic casing, the build quality is commendable. The keycaps feel robust, and the typing experience mirrors that of my CODE. While I was pleased, I eagerly awaited the other keyboards, suspecting the best was still on the horizon. This was a very basic unit after all. No fancy backlighting found here!&lt;/p>
&lt;p>Soon after, the shipment from Glorious arrived with all the components for my GMMK 2. I meticulously assembled it, starting with the 65 &lt;a href="https://www.gloriousgaming.com/products/glorious-panda-mechanical-switches">Glorious Panda Tactile Switches&lt;/a>. Initially, I used the Black Ash Keycaps but later switched to the &lt;a href="https://www.gloriousgaming.com/products/gmmk-keycaps">US (ANSI) Base Set&lt;/a>.&lt;/p>
&lt;p>However, there was a snag. Glorious doesn&amp;rsquo;t provide keycaps with media keys for custom models - a significant oversight. I reached out to their support, and they confirmed that premium keycaps come exclusively with the prebuilt model.&lt;/p>
&lt;p>Despite this, the keyboard is exceptional. The switches offer a delightful typing experience, and I believe I&amp;rsquo;ve discovered my new favorite switches. The keycaps, though lacking some features, are of superior quality. Fortunately, I could reference the premium keycaps on their website to locate my media keys.&lt;/p>
&lt;p>The Drop ALT is still en route, currently in California. I&amp;rsquo;m eagerly anticipating its arrival, especially since I managed to order it before it sold out.&lt;/p>
&lt;p>The GMMK 2 has set a high bar, quickly becoming my top keyboard choice.&lt;/p>
&lt;h2 id="a-minor-hiccup">A Minor Hiccup&lt;/h2>
&lt;p>For terminal users, the tilde (~) is crucial. By default, the GMMK 2 assigns &lt;code>~&lt;/code> to shift-fn-escape which is extremely inconvenient to type. I resolved this by remapping &lt;code>~&lt;/code> to shift-escape by appending &lt;code>xmodmap -e 'keysym Escape = Escape asciitilde Escape'&lt;/code> to my &lt;code>.profile&lt;/code>. While it&amp;rsquo;s possible to flash custom firmware, it seemed excessive for a single key remap.&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>Currently, the GMMK 2 reigns supreme on my desk. However, the Drop ALT might challenge its position in a future episode. Stay tuned!&lt;/p></content></item><item><title>Exploring Infinite Zoom</title><link>https://thoughts.greyh.at/posts/infinite-zoom/</link><pubDate>Sat, 15 Jul 2023 00:00:00 +0000</pubDate><guid>https://thoughts.greyh.at/posts/infinite-zoom/</guid><description>Today, my friend Jeremiah brought up infinite zoom effects and started doing some research on how to create them. After a bit of time, he sent me over detailed documentation on creating infinite zoom effects, and asked me to translate the technical pieces. This is how the rabbit hole began. I just had to try it, so this blog will detail my own journey creating an infinite zoom using Midjourney!</description><content>&lt;p>Today, my friend Jeremiah brought up infinite zoom effects and started doing some research on how to create them.&lt;/p>
&lt;p>After a bit of time, he sent me over &lt;a href="https://beltoforion.de/en/infinite_zoom/index.php">detailed documentation on creating infinite zoom effects&lt;/a>, and asked me to translate the technical pieces. This is how the rabbit hole began.&lt;/p>
&lt;p>I just had to try it, so this blog will detail my own journey creating an infinite zoom using &lt;a href="https://www.midjourney.com/">Midjourney&lt;/a>!&lt;/p>
&lt;h2 id="prompts">Prompts&lt;/h2>
&lt;p>First, I needed to start creating prompts. In total I created 21 unique images. Here are the prompts I used:&lt;/p>
&lt;ul>
&lt;li>Two dragons at the top of mount olympus &amp;ndash;s 720 &amp;ndash;ar 16:9 &amp;ndash;v 5&lt;/li>
&lt;li>Two dragons at the top of mount olympus &amp;ndash;s 720 &amp;ndash;ar 16:9 &amp;ndash;v 5 &amp;ndash;zoom 2&lt;/li>
&lt;li>A look through an opening into a mystical land &amp;ndash;s 720 &amp;ndash;v 5 &amp;ndash;ar 16:9 &amp;ndash;zoom 2&lt;/li>
&lt;li>A mythical land seen through a rectangular mirror with an ornate frame &amp;ndash;s 720 &amp;ndash;v 5 &amp;ndash;ar 16:9 &amp;ndash;zoom 2&lt;/li>
&lt;li>An ornate room hidden inside of a pyramid &amp;ndash;s 720 &amp;ndash;v 5 &amp;ndash;ar 16:9 &amp;ndash;zoom 2 (x2)&lt;/li>
&lt;li>The Cosmos creating a vision into a pyramid &amp;ndash;s 720 &amp;ndash;v 5 &amp;ndash;ar 16:9 &amp;ndash;zoom 2 (x2)&lt;/li>
&lt;li>Eye shaped portal to another dimension &amp;ndash;s 720 &amp;ndash;v 5 &amp;ndash;ar 16:9 &amp;ndash;zoom 2&lt;/li>
&lt;li>Mayan Temple &amp;ndash;s 720 &amp;ndash;v 5 &amp;ndash;ar 16:9 &amp;ndash;zoom 2 (x3)&lt;/li>
&lt;li>Amazon rain forest &amp;ndash;s 720 &amp;ndash;v 5 &amp;ndash;ar 16:9 &amp;ndash;zoom 2 (x3)&lt;/li>
&lt;li>Disolves into darkness &amp;ndash;s 720 &amp;ndash;v 5 &amp;ndash;ar 16:9 &amp;ndash;zoom 2 (x2)&lt;/li>
&lt;li>Papyrus map &amp;ndash;s 720 &amp;ndash;v 5 &amp;ndash;ar 16:9 &amp;ndash;zoom 2 (x2)&lt;/li>
&lt;li>Desk with papyrus map on it &amp;ndash;s 720 &amp;ndash;v 5 &amp;ndash;ar 16:9 &amp;ndash;zoom 2&lt;/li>
&lt;li>Adventurers and archaeologists office space with bright lights and a desk &amp;ndash;s 720 &amp;ndash;v 5 &amp;ndash;ar 16:9 &amp;ndash;zoom 2&lt;/li>
&lt;/ul>
&lt;p>The images produced were not exactly what I wanted, but since this was a test run, I used what was generated.&lt;/p>
&lt;h2 id="video-generation">Video Generation&lt;/h2>
&lt;p>&lt;a href="https://github.com/beltoforion/AI-Infinite-Zoom-Generator">AI-Infinite-Zoom-Generator&lt;/a> was used to generate the video and stitch all the images together. The install was painless on my Arch Linux box. It just needed a couple dependencies (&lt;code>python-opencv&lt;/code> and &lt;code>python-rawpy&lt;/code>) and then it was ready to go.&lt;/p>
&lt;p>I put all the generated images in the &lt;code>story&lt;/code> directory, then ran:&lt;/p>
&lt;pre tabindex="0">&lt;code>python ./infinite_zoom.py -as -i ./story -o story.mp4
&lt;/code>&lt;/pre>&lt;p>This created &lt;code>story.mp4&lt;/code> which you can see below!&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe src="https://www.youtube.com/embed/8pAiq1F3jY0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video">&lt;/iframe>
&lt;/div>
&lt;h2 id="post-production">Post Production&lt;/h2>
&lt;p>Before finishing up, Thomas of &lt;a href="https://www.sacredmirrormedia.com/">Sacred Mirror Media&lt;/a>, suggested upscaling the content with &lt;a href="https://www.topazlabs.com/topaz-video-ai">Topaz Video AI&lt;/a>. This took about 20 minutes, and then produced the following 4K version.&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe src="https://www.youtube.com/embed/C4yLNXOBDw0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video">&lt;/iframe>
&lt;/div>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>Generating these videos is actually quite easy, and a lot of fun. Midjourney doesn&amp;rsquo;t always generate exactly what you want, but with enough patience you can get amazing results.&lt;/p>
&lt;p>Now go and create some epic infinite zoom videos!&lt;/p></content></item><item><title>Simple Privacy - A Layered Approach</title><link>https://thoughts.greyh.at/posts/privacy/</link><pubDate>Thu, 25 May 2023 00:00:00 +0000</pubDate><guid>https://thoughts.greyh.at/posts/privacy/</guid><description>The internet was never designed with privacy in mind. A majority of the protocols we use today were conceived without any security or privacy considerations. This guide is for those who value their privacy and can spare a few minutes to configure their system. It is not intended to be an exhaustive list of recommendations, but a source of simple privacy-enhancing tips for the average user.</description><content>&lt;p>The internet was never designed with privacy in mind. A majority of the protocols we use today were conceived without any security or privacy considerations. This guide is for those who value their privacy and can spare a few minutes to configure their system. It is not intended to be an exhaustive list of recommendations, but a source of simple privacy-enhancing tips for the average user.&lt;/p>
&lt;h2 id="dns">DNS&lt;/h2>
&lt;p>There&amp;rsquo;s a saying amongst network admins: &amp;ldquo;it&amp;rsquo;s always DNS&amp;rdquo;. Every time you visit a domain name (e.g., twitter.com), a DNS (Domain Name System) request is made to look up that domain&amp;rsquo;s IP address (e.g., 104.244.42.1). Therefore, if DNS isn&amp;rsquo;t functioning, it appears as though the internet is down. This also means that whoever you use as a DNS provider can see every single domain you visit. By default, this is generally your ISP, and since they control those DNS servers, they can manipulate the DNS results to filter where you can go online.&lt;/p>
&lt;p>The first step to better security and privacy is to switch your DNS provider. Choose a provider who supports the latest DNS security features such as DNS over TLS (&lt;a href="https://en.wikipedia.org/wiki/DNS_over_TLS">DoT&lt;/a>), DNS over HTTPS (&lt;a href="https://en.wikipedia.org/wiki/DNS_over_HTTPS">DoH&lt;/a>), &lt;a href="https://www.dnscrypt.org/">DNSCrypt&lt;/a> and &lt;a href="https://www.cloudflare.com/dns/dnssec/how-dnssec-works/">DNSSEC&lt;/a>.&lt;/p>
&lt;p>There are numerous secure DNS services available. This article is not about endorsing one over the other, but here are a few that are widely used and have reasonable privacy policies:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.quad9.net/">Quad9&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://1.1.1.1/">Cloudflare&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://nextdns.io/">NextDNS&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>These providers offer multiple DNS services. Most operating systems default to standard insecure DNS on port 53, while browsers have implemented Secure DNS via DNS over HTTPS (DoH). Make sure Secure DNS is enabled in all your browsers. Just go to &lt;code>Settings&lt;/code> and search for &lt;code>DNS&lt;/code>. It is pretty easy to find.&lt;/p>
&lt;p>Now, while browsing the web, DNS requests will be encrypted and routed to a DNS provider you trust. You might think that your ISP can&amp;rsquo;t see where you are going, but they still can, due to how &lt;a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS&lt;/a> handshakes work!&lt;/p>
&lt;h2 id="sni">SNI&lt;/h2>
&lt;p>Server Name Indication (&lt;a href="https://www.cloudflare.com/learning/ssl/what-is-sni/">SNI&lt;/a>) is used in a TLS handshake to determine the correct certificate to use for HTTPS. This allows a web server to host more than one domain and reliably send the correct certificate, regardless of which domain is being served.&lt;/p>
&lt;p>Unfortunately, this is done unencrypted, which means your ISP can still see what domains you are visiting! In fact, any device on the route between you and a domain could easily capture that information. Most routes to the websites we visit generally consist of 6 to 15 hops, each of which could have a device logging data about us!&lt;/p>
&lt;p>This has been a long-standing issue. We have had HTTPS for decades, and yet default browser installations still have this flaw! Fortunately, the tools to address this are now available.&lt;/p>
&lt;h2 id="ech">ECH&lt;/h2>
&lt;p>Encrypted Client Hello (&lt;a href="https://datatracker.ietf.org/doc/draft-ietf-tls-esni/">ECH&lt;/a>) fixes this privacy issue. The browser uses public key cryptography to encrypt the domain in the TLS handshake. CloudFlare has really championed the technology, and &lt;a href="https://blog.cloudflare.com/handshake-encryption-endgame-an-ech-update/">written about it extensively&lt;/a>. They also provide the best &lt;a href="https://www.cloudflare.com/ssl/encrypted-sni/">testing site&lt;/a> to ensure your browser is configured correctly.&lt;/p>
&lt;p>You don&amp;rsquo;t need to understand every detail of how it works to ensure your browser is using the latest technology. That part is much simpler.&lt;/p>
&lt;h3 id="chromium-setup">Chromium Setup&lt;/h3>
&lt;p>For Chromium-based browsers (Brave/Chrome/Edge) you need to ensure Secure DNS is setup, then navigate to &lt;code>chrome://flags/&lt;/code>.&lt;/p>
&lt;p>Search for &lt;code>DNS&lt;/code>. Ensure the following options are enabled:&lt;/p>
&lt;ul>
&lt;li>Encrypted ClientHello&lt;/li>
&lt;li>Use DNS https alpn&lt;/li>
&lt;/ul>
&lt;p>Once you restart your browser, ECH should be enabled!&lt;/p>
&lt;h3 id="firefox-setup">Firefox Setup&lt;/h3>
&lt;p>For Firefox go to &lt;code>about:config&lt;/code> and search for &lt;code>DNS&lt;/code>. Ensure the following are enabled:&lt;/p>
&lt;ul>
&lt;li>network.dns.echconfig.enabled&lt;/li>
&lt;li>network.dns.http3_echconfig.enabled&lt;/li>
&lt;/ul>
&lt;p>Then set &lt;code>network.trr.mode&lt;/code> to &lt;code>3&lt;/code>. After restarting Firefox, ECH should be setup!&lt;/p>
&lt;p>Unfortunately if you are using Firefox for mobile, only the nightly builds expose &lt;code>about:config&lt;/code>.&lt;/p>
&lt;h2 id="testing-ech">Testing ECH&lt;/h2>
&lt;p>There are a couple of places where you can test your new setup to ensure everything is working as expected:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://defo.ie/ech-check.php">https://defo.ie/ech-check.php&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.cloudflare.com/ssl/encrypted-sni/">https://www.cloudflare.com/ssl/encrypted-sni/&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="privacy-status">Privacy Status&lt;/h2>
&lt;p>Finally, your ISP can&amp;rsquo;t see which domains you&amp;rsquo;re accessing. They can only see the IP address of the server you connect to. When websites use a Content Delivery Network (CDN) that supports ECH, like CloudFlare, your ISP can&amp;rsquo;t determine your browsing destination. This applies to millions of websites!&lt;/p>
&lt;p>With no special software to install and just a few minutes of your time, you are browsing the web more securely than most. If you want to take it to the next level, consider a VPN.&lt;/p>
&lt;h2 id="vpn">VPN&lt;/h2>
&lt;p>A VPN, or Virtual Private Network, hides all data from your ISP by encrypting your network communications and sending it to a VPN provider instead. Any data your ISP used to gather is now hidden and can potentially be gathered by the VPN provider. Therefore, it is essential to use Secure DNS and set up ECH, or the same privacy issues will arise when you use a VPN!&lt;/p>
&lt;p>However, there are reasons to use a trustworthy VPN:&lt;/p>
&lt;ul>
&lt;li>You need to access a site that is inaccessible in your country.&lt;/li>
&lt;li>You don&amp;rsquo;t trust your ISP.&lt;/li>
&lt;/ul>
&lt;p>While I&amp;rsquo;ve generally been satisfied with my home internet providers, I find a VPN very useful for mobile devices. A VPN protects me from potentially malicious networks, be they my mobile provider or a random WiFi network I connect to. Many home routers also offer built-in VPN servers, allowing you to VPN into your own home network!&lt;/p>
&lt;p>If you&amp;rsquo;re exploring VPN services, I highly recommend watching this video from Naomi Brockwell.&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe src="https://www.youtube.com/embed/XLZmmxi_PYE" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video">&lt;/iframe>
&lt;/div>
&lt;p>I would also recommend &lt;a href="https://airvpn.org/">AirVPN&lt;/a> and their excellent VPN client, Eddie.&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>Security requires a multi-layered approach. The following technologies are designed to provide a more secure and private internet experience and are more effective when used together:&lt;/p>
&lt;ul>
&lt;li>Secure DNS (DoT/DoH/DNSSEC)&lt;/li>
&lt;li>Encrypted Client Hello (ECH)&lt;/li>
&lt;li>VPN&lt;/li>
&lt;/ul>
&lt;p>Remember, this article only scratches the surface of security and privacy. For those seeking even stronger privacy, here are a few links to get you started:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://lokinet.org/">Lokinet&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://yggdrasil-network.github.io/">Yggdrasil&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.torproject.org/index.html">Tor&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://geti2p.net/">I2P&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>A more private and secure internet benefits everyone. Let&amp;rsquo;s make it a reality!&lt;/p></content></item><item><title>The AI Journey So Far</title><link>https://thoughts.greyh.at/posts/ai/</link><pubDate>Fri, 03 Mar 2023 00:00:00 +0000</pubDate><guid>https://thoughts.greyh.at/posts/ai/</guid><description>Everywhere you look today, people are talking about artificial intelligence (AI). People are creating stunning art using Midjourney and DALL-E. AI based chat bots have become infinitely more powerful. Using ChatGPT can feel like magic… a conversational something. What that is, is a debate for another day.</description><content>&lt;p>Everywhere you look today, people are talking about artificial intelligence (AI). People are creating stunning art using &lt;a href="https://midjourney.com">Midjourney&lt;/a> and &lt;a href="https://labs.openai.com/">DALL-E&lt;/a>. AI based chat bots have become infinitely more powerful. Using &lt;a href="https://chat.openai.com">ChatGPT&lt;/a> can feel like magic&amp;hellip; a conversational &lt;em>something&lt;/em>. What that is, is a debate for another day.&lt;/p>
&lt;h2 id="intention">Intention&lt;/h2>
&lt;p>Many people haven&amp;rsquo;t explored these new tools yet, and I want to demystify the process. Walk through the process of signing up for the most common services. Plus provide a few tips I have found along the way.&lt;/p>
&lt;h2 id="generating-images-with-midjourney">Generating Images with Midjourney&lt;/h2>
&lt;p>There are many services out there that will generate images for you. However the most popular is &lt;a href="https://midjourney.com">Midjourney&lt;/a>. While their service is easy to use once you are setup, it does require a Discord account, as all image generation is done within their Discord server.&lt;/p>
&lt;p>Once you sign up to &lt;a href="https://discord.com/">Discord&lt;/a> and create an account, make sure you validate your email address. You won&amp;rsquo;t be able to login to Midjourney until that is complete.&lt;/p>
&lt;p>Now you can visit &lt;a href="https://midjourney.com">https://midjourney.com&lt;/a> and click &lt;code>Join The Beta&lt;/code>. This will login to their Discord server. You should see a white sailboat icon on the left side of the main Discord window.&lt;/p>
&lt;p>To start generating images, just type &lt;code>/imagine&lt;/code> in one of the newcomer rooms. Then you will provide it the prompt text, hit enter, then wait a minute or so for the request to be fulfilled. If you want to control the aspect ratio, you can append &lt;code>--ar 16:9&lt;/code> to your prompts to make them wide format.&lt;/p>
&lt;p>Once you have generated some images, you can view them on the Midjourney website by clicking &lt;code>Sign In&lt;/code>. This authorizes against your Discord account. Midjourney build a profile of all their users requests and upscales. You can find me &lt;a href="https://www.midjourney.com/app/users/f8ecd0b9-86f9-4621-bfb1-5326ecfa0182/">here&lt;/a>.&lt;/p>
&lt;h3 id="a-few-examples">A Few Examples&lt;/h3>
&lt;p>Below are a few images I generated, plus the prompts I used.&lt;/p>
&lt;p>&lt;img alt="Yogi" src="https://thoughts.greyh.at/posts/ai/images/yogi.webp" width=1024 height=1024 loading="lazy" />
&lt;em>Prompt: master yogi eminating power&lt;/em>&lt;/p>
&lt;p>&lt;img alt="Elf Queen" src="https://thoughts.greyh.at/posts/ai/images/queen.webp" width=1024 height=1024 loading="lazy" />
&lt;em>Prompt: elf queen with elaborate headpiece and throne&lt;/em>&lt;/p>
&lt;p>&lt;img alt="Cosmic" src="https://thoughts.greyh.at/posts/ai/images/cosmic.webp" width=1024 height=1024 loading="lazy" />
&lt;em>Prompt: entity capable of creating universes, skin has high levels of detail and textures, photo realistic, cosmic origin, guardian&lt;/em>&lt;/p>
&lt;p>&lt;img alt="Soul" src="https://thoughts.greyh.at/posts/ai/images/soul.webp" width=1024 height=1024 loading="lazy" />
&lt;em>Prompt: the soul represented as beams of light traveling through the cosmos HDR&lt;/em>&lt;/p>
&lt;p>Lastly, the banner of this post was generated by Midjourney.&lt;/p>
&lt;p>Don&amp;rsquo;t try to generate anything with text. It will not work, and will say the wrong things. We aren&amp;rsquo;t ready to get automated stationary and business cards just yet. While there are lots of prompts that provide incredible results, there are many cases where the generated images are unusable.&lt;/p>
&lt;h3 id="how-does-it-work">How Does it Work?&lt;/h3>
&lt;p>To keep this post short(er), let me refer you to this article:
&lt;a href="https://www.marktechpost.com/2022/11/14/how-do-dall%C2%B7e-2-stable-diffusion-and-midjourney-work/">How Do DALL·E 2, Stable Diffusion, and Midjourney Work?&lt;/a>.&lt;/p>
&lt;h2 id="who-is-openai">Who is OpenAI?&lt;/h2>
&lt;p>&lt;a href="http://openai.com">OpenAI&lt;/a> is one of the most innovative companies in the space. They have several popular &lt;a href="https://openai.com/product">products&lt;/a>, but I will only focus on two, ChatGPT and DALL-E.&lt;/p>
&lt;p>&lt;a href="https://labs.openai.com/">DALL-E&lt;/a> is OpenAI&amp;rsquo;s image generation product. It is very similar to Midjourney and I would highly suggest you try them both, as they have very different results. DALL-E does have one main advantage, you don&amp;rsquo;t need a Discord account, and you can easily use the web to generate images.&lt;/p>
&lt;h2 id="chatgpt">ChatGPT&lt;/h2>
&lt;p>&lt;a href="https://chat.openai.com">ChatGPT&lt;/a> is the first natural language bot I have used that can actually hold a conversation. You ask it things in a prompt, and it replies the best it can. It remembers things you have told it, and is extremely useful editing content.&lt;/p>
&lt;p>To start using ChatGPT first you need to sign up for an &lt;a href="https://openai.com/">OpenAI&lt;/a> account. Once you have created your account just visit &lt;a href="https://chat.openai.com">https://chat.openai.com&lt;/a>.&lt;/p>
&lt;p>This will bring you into the ChatGPT website and allow you to initiate chats. What you do in these chats is limited only by your imagination.&lt;/p>
&lt;p>Here are a few things I have used ChatGPT for:&lt;/p>
&lt;ul>
&lt;li>Editing emails/tweets&lt;/li>
&lt;li>Helping draft web content&lt;/li>
&lt;li>Asking random questions&lt;/li>
&lt;li>Writing short stories&lt;/li>
&lt;/ul>
&lt;p>ChatGPT is extremely versatile, after you play with it for a while, you are bound to find some utility.&lt;/p>
&lt;p>Note that ChatGPT can provide incorrect data, and should not be relied on to be 100% accurate. Always check the content it produces.&lt;/p>
&lt;h2 id="the-future">The Future&lt;/h2>
&lt;p>While it is remarkable how far these tools have come, they still require artists and designers to guide their creativity. It will be interesting to see how people build on top of this technology and how it weaves into societies artistic expression.&lt;/p></content></item><item><title>Remember to Breathe</title><link>https://thoughts.greyh.at/posts/breathe/</link><pubDate>Sat, 03 Sep 2022 00:00:00 +0000</pubDate><guid>https://thoughts.greyh.at/posts/breathe/</guid><description>Taking the time to breathe consciously, as simple as that is, provides significant improvements to body and spirit.</description><content>&lt;p>Taking the time to breathe consciously, as simple as that is, provides significant improvements to body and spirit.&lt;/p>
&lt;h2 id="my-wim-hof-journey">My Wim Hof Journey&lt;/h2>
&lt;p>For the past few years, I have been doing the &lt;a href="https://www.wimhofmethod.com/">Wim Hof Method&lt;/a> and thoroughly enjoying that practice. It helps me calm down, improves my sleep, and can provide a wonderful euphoric state. With just fifteen minutes, you can feel energized, and connect with your body in a new (ancient?) way.&lt;/p>
&lt;p>To start, I merely incorporated his breathing techniques, but after some time, cold therapy was rejuvenating and absolutely worthwhile. That being said, I mainly focus on my breath, and the cold isn&amp;rsquo;t for everyone.&lt;/p>
&lt;p>My friend Dina likes to remind me, the word for breath and spirit are the same in many languages. This has resonated in my head for a long time. Especially as I did longer journeys with her facilitation. A bit more on this later.&lt;/p>
&lt;h2 id="the-move-to-hawaii">The Move To Hawaii&lt;/h2>
&lt;p>Before I found the Big Island. I did Wim Hof regularly, but rarely found other people focusing on their breath. This changed when I got to Hawaii.&lt;/p>
&lt;p>As I met people, they actually talked about breathwork&amp;hellip; I started to look around.&lt;/p>
&lt;p>Igor Siberia, someone I had just met, was doing a three hour breathwork session called &amp;ldquo;Awaken Your Breath&amp;rdquo; at &lt;a href="https://dragonflyranch.com/">Dragonfly Ranch&lt;/a>. I was ready, and could not be more excited.&lt;/p>
&lt;p>&lt;img alt="Awaken Your Breath" src="https://thoughts.greyh.at/posts/breathe/images/igor.webp" width=500 height=505 loading="lazy" />&lt;/p>
&lt;p>The session did not disappoint. I was able to reach new states of consciousness and experience enhanced perception over my body&amp;rsquo;s internal systems.&lt;/p>
&lt;p>Although I really enjoyed the session, there were some side effects, including the worst case of &lt;a href="https://alchemyofbreath.com/understanding-tetany-and-discovering-the-treasure-within-it/">Tetany&lt;/a> I had ever experienced. I couldn&amp;rsquo;t move my hands for about 20 minutes!&lt;/p>
&lt;p>I developed a new appreciation for Tetany. It is just another way of the body showing you what needs attention. Seems my wrists need a lot of work as well. One step at a time.&lt;/p>
&lt;h2 id="friends-invitation">Friends Invitation&lt;/h2>
&lt;p>A few months passed after my session with Igor, and my friend Dina mentioned she was leading Wim Hof sessions once a week near my place!&lt;/p>
&lt;p>I decided to give it a shot. Generally, I don&amp;rsquo;t do classes, but this was being run by a friend, and I had to at least check it out.&lt;/p>
&lt;p>There was a small group of 10 or so. We spent some time stretching, saying hello, and focusing on our breath. Then we spent about 20 minutes doing 4 rounds of Wim Hof. I felt fantastic. My mind felt focused, clear, and ready to take on the day.&lt;/p>
&lt;p>The group keeps growing every week. I fear they will need to find a bigger space. 😉&lt;/p>
&lt;h2 id="more-breath">More Breath&lt;/h2>
&lt;p>This led to me signing up for a breathwork facilitator&amp;rsquo;s training. I might never lead a session, but I wanted to learn more. I spent over 30 hours studying different breathing techniques, and working with an incredibly talented set of people.&lt;/p>
&lt;p>While I had done many of these forms before, doing them with a larger group allowed me to extend my practice. Plus, it introduced me to my next exploration, &lt;a href="https://www.verywellmind.com/holotropic-breathwork-4175431">holotropic breathing&lt;/a>.&lt;/p>
&lt;p>There is magic in sound, and in breath. Holotropic breathing combines the two, and puts you into a heightened state. I don&amp;rsquo;t want to go into specifics, I just recommend everyone try it sometime.&lt;/p>
&lt;p>If you are looking for some musical inspiration, check out my &lt;a href="https://www.last.fm/user/zquestz">last.fm&lt;/a>. You will want to prepare a two hour playlist to fully experience the effects.&lt;/p>
&lt;h2 id="mahalo">Mahalo&lt;/h2>
&lt;p>Thanks for taking the time to read my thoughts. Take some time out of your busy schedule to breathe consciously.&lt;/p>
&lt;p>I will leave you with my favorite guided Wim Hof exercise.&lt;/p>
&lt;p>Breathe.&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe src="https://www.youtube.com/embed/tybOi4hjZFQ" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video">&lt;/iframe>
&lt;/div></content></item><item><title>A Quick Dive Into WebP</title><link>https://thoughts.greyh.at/posts/webp/</link><pubDate>Thu, 01 Sep 2022 00:00:00 +0000</pubDate><guid>https://thoughts.greyh.at/posts/webp/</guid><description>After ignoring WebP for years, I finally decided to check it out. For a long time I thought PNG, GIF, JPG, and MP4 would be enough. Not anymore.</description><content>&lt;p>After ignoring WebP for years, I finally decided to check it out. For a long time I thought PNG, GIF, JPG, and MP4 would be enough. Not anymore.&lt;/p>
&lt;h2 id="a-little-history">A Little History&lt;/h2>
&lt;p>WebP was developed by Google to create &lt;a href="https://developers.google.com/speed/webp/">an image format for the web&lt;/a>. A format that provides better compression, alpha channels (transparency), and even animation.&lt;/p>
&lt;p>When Google released WebP to the world on September 30th 2010, browser support was non-existent, and that made it near impossible to evaluate. Projects needed to stick with standard formats (PNG, GIF, JPG). They couldn&amp;rsquo;t risk serving next generation formats and have an asset not load correctly.&lt;/p>
&lt;p>In addition, there were thousands of image processing tools on the web, and none of them supported WebP.&lt;/p>
&lt;h2 id="today-is-different">Today is Different&lt;/h2>
&lt;p>Fast forward to 2022 and WebP is widely supported in &lt;a href="https://www.lambdatest.com/web-technologies/webp">all major browsers&lt;/a>, graphics software, and image conversion tools.&lt;/p>
&lt;p>For artists, designers and illustrators, they just export as WebP. It doesn&amp;rsquo;t matter if they use Photoshop or &lt;a href="https://www.gimp.org/">GIMP&lt;/a>.&lt;/p>
&lt;p>Users of older, non-compatible browsers, need to upgrade to a modern browser anyways. Security is a spectrum, and keeping your browser up to date is an important ingredient.&lt;/p>
&lt;p>This signals to me, WebP is now an acceptable default format. No more fallbacks, no more hacks. Just smaller, nicer images for everyone.&lt;/p>
&lt;h2 id="the-real-world">The Real World&lt;/h2>
&lt;p>I like to think I am pragmatic, and recently WebP has enabled me to massively shrink my asset sizes.&lt;/p>
&lt;p>Regularly, I deal with images hosted on &lt;a href="https://ipfs.io">IPFS&lt;/a>. Many of them are high resolution PNGs, typically thousands of them, and they are being served off of slow IPFS gateways. Luckily, they are also extremely cacheable, as those contents will never change.&lt;/p>
&lt;p>I use a caching resizer to generate optimized, cached assets, however it didn&amp;rsquo;t support WebP encoding!&lt;/p>
&lt;p>So I decided to &lt;a href="https://github.com/zquestz/imageproxy">add WebP support&lt;/a>, and do some testing!&lt;/p>
&lt;p>I even included &lt;a href="https://pngquant.org/">pngquant&lt;/a>, as it is my favorite PNG compression utility!&lt;/p>
&lt;p>&lt;a href="https://clementinesnightmare.mypinata.cloud/ipfs/QmSCpGdCSo5iDXDdGcJRWA9kcB22MdBo1xwnoMpx1nj937/1169.png">Original&lt;/a> - 2500px (6.7MB)&lt;/p>
&lt;p>WebP - 500px (80.5kB)
&lt;img alt="WebP" src="https://thoughts.greyh.at/posts/webp/images/1169.webp" width=500 height=500 loading="lazy" />&lt;/p>
&lt;p>PNG - 500px (295kB)
&lt;img alt="PNG" src="https://thoughts.greyh.at/posts/webp/images/1169.png" width=500 height=500 loading="lazy" />&lt;/p>
&lt;p>PNG-pngquant - 500px (99.3kB)
&lt;img alt="PNG" src="https://thoughts.greyh.at/posts/webp/images/1169-pngquant.png" width=500 height=500 loading="lazy" />&lt;/p>
&lt;p>Well, the results are impressive. The WebP output looks amazing and is &amp;lt; 30% of the PNG! Even when optimizing with &lt;code>pngquant&lt;/code> the WebP file was smaller, was of higher quality, and didn&amp;rsquo;t require post processing.&lt;/p>
&lt;h2 id="webp-for-developers">WebP for Developers&lt;/h2>
&lt;p>Most developers will merely use WebP assets, and won&amp;rsquo;t need the lower level libraries. However, if you want to encode/decode WebP files, then look no further.&lt;/p>
&lt;p>The main C libraries are open-source and available at: &lt;a href="https://developers.google.com/speed/webp/download">developers.google.com&lt;/a>&lt;/p>
&lt;p>One would think that WebP would be fully supported in Go, since it was also developed at Google. However, as of Go 1.19, WebP encoding is not supported in the standard library!&lt;/p>
&lt;p>If you are looking for WebP encoding in Go, and don&amp;rsquo;t mind including a C dependency, &lt;a href="https://github.com/kolesa-team/go-webp">github.com/kolesa-team/go-webp&lt;/a> works great.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Use WebP and serve your assets in &lt;a href="https://web.dev/uses-webp-images/">modern formats&lt;/a>. The web will thank you.&lt;/p></content></item><item><title>A Thought Experiment</title><link>https://thoughts.greyh.at/posts/experiment/</link><pubDate>Sun, 12 Jun 2022 00:00:00 +0000</pubDate><guid>https://thoughts.greyh.at/posts/experiment/</guid><description>For a while now, I have wanted a place to post my long form content. So after literally years of delay, I finally got around to it, and thoughts.greyh.at was born.</description><content>&lt;p>For a while now, I have wanted a place to post my long form content. So after literally years of delay, I finally got around to it, and &lt;a href="https://thoughts.greyh.at">thoughts.greyh.at&lt;/a> was born.&lt;/p>
&lt;p>To get started, I figured I would document the setup itself, from choosing a solution, through full deployment to my Kubernetes cluster.&lt;/p>
&lt;p>You can find the code for this experiment on &lt;a href="https://github.com/zquestz/thoughts">GitHub&lt;/a> and follow along if you are so inclined.&lt;/p>
&lt;h2 id="what-framework">What Framework?&lt;/h2>
&lt;p>Since my goal is to just share my ideas with the world, I wanted to focus on writing, and keep things version controlled with git. I also didn&amp;rsquo;t want to manage backend dependencies and ideally wanted to deploy static content.&lt;/p>
&lt;p>So, that meant no WordPress, no Drupal, it was time to keep things as simple as possible. So I looked into static site generators. These would give me a lot of useful tooling, and make packaging the site for deployment a breeze.&lt;/p>
&lt;p>Since I know both Ruby and Go, I naturally checked out the latest releases of &lt;a href="https://jekyllrb.com/">Jekyll&lt;/a> and &lt;a href="https://gohugo.io/">Hugo&lt;/a>, as they are the most popular static site generators on the market.&lt;/p>
&lt;p>While both are quality projects, I chose Hugo. It lets me create all my posts using markdown, is incredibly fast, and offers a bunch of themes to get started. Plus, it had a &lt;a href="https://github.com/panr/hugo-theme-terminal">theme&lt;/a> I really liked, so I figured why not.&lt;/p>
&lt;h2 id="configuration">Configuration&lt;/h2>
&lt;p>Setup was super simple. First, I created the new site.&lt;/p>
&lt;pre tabindex="0">&lt;code>hugo new site thoughts
cd thoughts
git init
&lt;/code>&lt;/pre>&lt;p>Then, I added my theme.&lt;/p>
&lt;pre tabindex="0">&lt;code>git submodule add -f https://github.com/panr/hugo-theme-terminal.git themes/terminal
&lt;/code>&lt;/pre>&lt;p>Next, I updated &lt;code>config.toml&lt;/code>!
&lt;div class="collapsable-code">
&lt;input id="1" type="checkbox" checked />
&lt;label for="1">
&lt;span class="collapsable-code__language">toml&lt;/span>
&lt;span class="collapsable-code__title">config.toml&lt;/span>
&lt;span class="collapsable-code__toggle" data-label-expand="Show" data-label-collapse="Hide">&lt;/span>
&lt;/label>
&lt;pre class="language-toml" >&lt;code>
baseurl = &amp;#34;/&amp;#34;
languageCode = &amp;#34;en-us&amp;#34;
theme = &amp;#34;terminal&amp;#34;
paginate = 5
[params]
# dir name of your main content (default is `content/posts`).
# the list of set content will show up on your index page (baseurl).
contentTypeName = &amp;#34;posts&amp;#34;
# [&amp;#34;orange&amp;#34;, &amp;#34;blue&amp;#34;, &amp;#34;red&amp;#34;, &amp;#34;green&amp;#34;, &amp;#34;pink&amp;#34;]
themeColor = &amp;#34;green&amp;#34;
# if you set this to 0, only submenu trigger will be visible
showMenuItems = 2
# show selector to switch language
showLanguageSelector = false
# set theme to full screen width
fullWidthTheme = false
# center theme with default width
centerTheme = true
# if your resource directory contains an image called `cover.(jpg|png|webp)`,
# then the file will be used as a cover automatically.
# With this option you don&amp;#39;t have to put the `cover` param in a front-matter.
autoCover = true
# set post to show the last updated
# If you use git, you can set `enableGitInfo` to `true` and then post will automatically get the last updated
showLastUpdated = true
# set a custom favicon (default is a `themeColor` square)
# favicon = &amp;#34;favicon.ico&amp;#34;
# Provide a string as a prefix for the last update date. By default, it looks like this: 2020-xx-xx [Updated: 2020-xx-xx] :: Author
# updatedDatePrefix = &amp;#34;Updated&amp;#34;
# set all headings to their default size (depending on browser settings)
# oneHeadingSize = true # default
# whether to show a page&amp;#39;s estimated reading time
readingTime = true # default was false
# whether to show a table of contents
# can be overridden in a page&amp;#39;s front-matter
# Toc = false # default
# set title for the table of contents
# can be overridden in a page&amp;#39;s front-matter
# TocTitle = &amp;#34;Table of Contents&amp;#34; # default
[params.twitter]
# set Twitter handles for Twitter cards
# see https://developer.twitter.com/en/docs/tweets/optimize-with-cards/guides/getting-started#card-and-content-attribution
# do not include @
creator = &amp;#34;zquestz&amp;#34;
site = &amp;#34;zquestz&amp;#34;
[languages]
[languages.en]
languageName = &amp;#34;English&amp;#34;
title = &amp;#34;Terminal Thoughts&amp;#34;
subtitle = &amp;#34;A small area for quest&amp;#39;s thoughts.&amp;#34;
owner = &amp;#34;&amp;#34;
keywords = &amp;#34;&amp;#34;
copyright = &amp;#34;&amp;#34;
menuMore = &amp;#34;Show more&amp;#34;
readMore = &amp;#34;Read more&amp;#34;
readOtherPosts = &amp;#34;Read other posts&amp;#34;
newerPosts = &amp;#34;Newer posts&amp;#34;
olderPosts = &amp;#34;Older posts&amp;#34;
missingContentMessage = &amp;#34;Page not found...&amp;#34;
missingBackButtonLabel = &amp;#34;Back to home page&amp;#34;
[languages.en.params.logo]
logoText = &amp;#34;Terminal Thoughts&amp;#34;
logoHomeLink = &amp;#34;/&amp;#34;
[languages.en.menu]
[[languages.en.menu.main]]
identifier = &amp;#34;thoughts&amp;#34;
name = &amp;#34;Thoughts&amp;#34;
url = &amp;#34;/&amp;#34;
[[languages.en.menu.main]]
identifier = &amp;#34;intent&amp;#34;
name = &amp;#34;Intent&amp;#34;
url = &amp;#34;/intent&amp;#34;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;/p>
&lt;p>The last piece was adding &lt;code>intent.md&lt;/code> and &lt;code>experiment.md&lt;/code> (this post).&lt;/p>
&lt;h2 id="dns">DNS&lt;/h2>
&lt;p>I already have a load balancer setup in GCE to handle a number of other websites, so all I had to do was add an A record to handle thoughts.greyh.at. I generally use a TTL of 60, just in case I have to move a domain.&lt;/p>
&lt;pre tabindex="0">&lt;code>thoughts.greyh.at. 60 IN A 35.208.63.54
&lt;/code>&lt;/pre>&lt;h2 id="deployment">Deployment&lt;/h2>
&lt;p>To get the site deployed, I would need to create a Dockerfile and Kubernetes configurations to deploy to my GCE cluster.&lt;/p>
&lt;p>To generate static files, it was as simple as running &lt;code>hugo&lt;/code> and the static files are created in the &lt;code>public&lt;/code> directory. This made the Dockerfile super simple.&lt;/p>
&lt;pre tabindex="0">&lt;code>FROM alpine:3.7
RUN apk add --update --no-cache curl bash
FROM nginx
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY public/ /usr/share/nginx/html/
&lt;/code>&lt;/pre>&lt;p>Then I generated the image, and pushed it to Docker Hub.&lt;/p>
&lt;pre tabindex="0">&lt;code>docker build -t thoughts .
docker tag thoughts:latest zquestz/thoughts:06-12-2022
docker tag thoughts:latest zquestz/thoughts:latest
docker push zquestz/thoughts:06-12-2022
docker push zquestz/thoughts:latest
&lt;/code>&lt;/pre>&lt;p>Now that the image was pushed, I could finally write the &lt;a href="https://github.com/zquestz/thoughts/tree/master/kube">Kubernetes configs&lt;/a> and deploy the app to my cluster.&lt;/p>
&lt;pre tabindex="0">&lt;code>kubectl apply -f kube
&lt;/code>&lt;/pre>&lt;h2 id="profit">Profit!&lt;/h2>
&lt;p>Once I kicked off the deploy, &lt;a href="https://cert-manager.io/">cert-manager&lt;/a> automatically setup the new https certificate through &lt;a href="https://letsencrypt.org/">Let&amp;rsquo;s Encrypt&lt;/a> and brought the site online!&lt;/p>
&lt;p>Overall I am quite happy with the result, and hope this will be the start of a new writing journey!&lt;/p></content></item><item><title>Intent</title><link>https://thoughts.greyh.at/intent/</link><pubDate>Sun, 12 Jun 2022 00:00:00 +0000</pubDate><guid>https://thoughts.greyh.at/intent/</guid><description>You have found my tiny corner of the internet!
This is where I share my thoughts about:
Crypto Security Infrastructure Linux Food Health Spirit You can find my short form content on Twitter or Mastodon, and my code on GitHub.</description><content>&lt;p>You have found my tiny corner of the internet!&lt;/p>
&lt;p>This is where I share my thoughts about:&lt;/p>
&lt;ul>
&lt;li>Crypto&lt;/li>
&lt;li>Security&lt;/li>
&lt;li>Infrastructure&lt;/li>
&lt;li>Linux&lt;/li>
&lt;li>Food&lt;/li>
&lt;li>Health&lt;/li>
&lt;li>Spirit&lt;/li>
&lt;/ul>
&lt;p>You can find my short form content on &lt;a href="https://twitter.com/zquestz">Twitter&lt;/a> or &lt;a href="https://mastodon.social/web/@zquestz">Mastodon&lt;/a>, and my code on &lt;a href="https://github.com/zquestz">GitHub&lt;/a>.&lt;/p></content></item></channel></rss>