<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Marin Atanasov Nikolov</title>
    <description>A place about Open Source Software, Operating Systems and some random thoughts</description>
    <link>https://dnaeon.github.io</link>
    <atom:link href="https://dnaeon.github.io/feed.xml" rel="self" type="application/rss+xml" />
    
      <item>
        <title>Exporting org-roam notes to Hugo and Quartz</title>
        <description>&lt;p&gt;I use &lt;a href=&quot;https://orgmode.org&quot;&gt;Org Mode&lt;/a&gt; for note taking and tracking purposes.&lt;/p&gt;

&lt;p&gt;Recently I have also made the switch to &lt;a href=&quot;https://www.orgroam.com&quot;&gt;Org Roam&lt;/a&gt;,
that is great extension to Org Mode, which leverages the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Zettelkasten&quot;&gt;Zettlekasten&lt;/a&gt; method for organizing
and linking your notes.&lt;/p&gt;

&lt;p&gt;I have also found this approach quite useful and intuitive once you start using
it. Being able to establish relationships between your notes and then inspect
them by looking at the backlinks is quite useful.&lt;/p&gt;

&lt;p&gt;There are plenty of resources about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; available, so if you are just
starting out I’d suggest checking out the &lt;a href=&quot;https://www.orgroam.com/manual.html&quot;&gt;Org Roam Manual&lt;/a&gt;,
and the &lt;a href=&quot;https://systemcrafters.net/build-a-second-brain-in-emacs/getting-started-with-org-roam/&quot;&gt;Build a Second Brain in Emacs with Org Roam&lt;/a&gt; guide for a good overview.&lt;/p&gt;

&lt;p&gt;In this post we will see how we can export our existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; notes to
Markdown, which can then be served by a static-site generator like
&lt;a href=&quot;https://gohugo.io&quot;&gt;Hugo&lt;/a&gt; or &lt;a href=&quot;https://quartz.jzhao.xyz&quot;&gt;Quartz&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My Emacs configuration for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-mode&lt;/code&gt; looks like this.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;use-package&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ox-pandoc&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;use-package&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ob-go&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;use-package&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ox-hugo&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dnaeon/set-creation-date-heading-property&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Sets the CREATED property on each org-mode heading&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-excursion&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-back-to-heading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-set-property&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;CREATED&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;format-time-string&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%Y-%m-%d %T&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;use-package&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:defer&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:init&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; (setq org-src-preserve-indentation t)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;ob-shell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;ob-lisp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;ob-python&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;ob-go&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-babel-do-load-languages&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;&apos;org-babel-load-languages&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shell&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;lisp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;python&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:config&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org-insert-heading-hook&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dnaeon/set-creation-date-heading-property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It is a pretty simple and standard setup. I like to have a timestamp associated
with each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-mode&lt;/code&gt; heading, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dnaeon/set-creation-date-heading-property&lt;/code&gt; takes
care of that.&lt;/p&gt;

&lt;p&gt;When creating a new heading using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-insert-heading-respect-content&lt;/code&gt; or
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C-&amp;lt;enter&amp;gt;&lt;/code&gt; we automatically have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATED&lt;/code&gt; property like in the example
below.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;TODO&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Just&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;another&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;entry&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;:PROPERTIES:&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;:CREATED:&lt;/span&gt;  &lt;span class=&quot;nv&quot;&gt;2026-02-23&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;13:14:20&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;:END:&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;Something&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;useful.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; configuration looks like this.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dnaeon/org-set-created-property&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Sets the CREATED property when a new org-roam node is created&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-set-property&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;CREATED&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;format-time-string&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%Y-%m-%d %T&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dnaeon/tag-new-org-roam-node-as-draft&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Tags org-roam nodes as draft&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-roam-tag-add&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;draft&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;use-package&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-roam&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:defer&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:bind&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C-c n f&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-roam-node-find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C-c n i&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-roam-node-insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C-c n t&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-roam-buffer-toggle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C-c n g&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-roam-graph&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C-c n c&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-roam-capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;c1&quot;&gt;;; Dailies&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C-c n d&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-roam-dailies-capture-today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:config&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; Display mtime for nodes&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cl-defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-roam-node-mtime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-roam-node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;format-time-string&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%Y-%m-%d %T&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-roam-node-file-mtime&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;;; A method to display the :CREATED: property from each node when browsing the&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; org-roam nodes. In order to display the :CREATED: property add the&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; following to the ORG-ROAM-NODE-DISPLAY-TEMPLATE var.&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; (propertize &quot;${created-property}&quot; &apos;face &apos;org-date)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cl-defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-roam-node-created-property&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-roam-node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cdr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;assoc-string&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;CREATED&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-roam-node-properties&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;;; Add CREATED property to each org-roam node&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org-roam-capture-new-node-hook&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dnaeon/org-set-created-property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;;; Add a `draft&apos; tag to each newly created node, until we remove it.&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org-roam-capture-new-node-hook&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dnaeon/tag-new-org-roam-node-as-draft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;;; Richer context information when browsing nodes&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-roam-node-display-template&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt;
                                        &lt;span class=&quot;s&quot;&gt;&quot;${title:80} &quot;&lt;/span&gt;
                                        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;propertize&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;${mtime}&quot;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;face&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org-date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                        &lt;span class=&quot;s&quot;&gt;&quot;  &quot;&lt;/span&gt;
                                        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;propertize&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;${tags:25}&quot;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;face&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org-tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;;; Configure path to database and notes&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt;
   &lt;span class=&quot;nv&quot;&gt;org-roam-directory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-truename&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~/Projects/docs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;nv&quot;&gt;org-roam-db-location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-truename&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~/.emacs.d/org-roam.db&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;setq-default&lt;/span&gt;
   &lt;span class=&quot;nv&quot;&gt;org-roam-capture-templates&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;default&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;plain&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%?&quot;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:target&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file+head&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;notes/%&amp;lt;%Y&amp;gt;/%&amp;lt;%m&amp;gt;/${slug}.org&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;#+title: ${title}\n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:unnarrowed&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;e&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;encrypted&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;plain&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%?&quot;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:target&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file+head&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;notes/%&amp;lt;%Y&amp;gt;/%&amp;lt;%m&amp;gt;/${slug}.org.gpg&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;#+title: ${title}\n#+filetags: :gpg:encrypted:\n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:unnarrowed&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;;; Configure what sections to display in the org-roam buffer.&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; https://www.orgroam.com/manual.html#Configuring-what-is-displayed-in-the-buffer-1&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-roam-mode-sections&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-roam-backlinks-section&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:unique&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;nv&quot;&gt;org-roam-reflinks-section&lt;/span&gt;
          &lt;span class=&quot;c1&quot;&gt;;; #&apos;org-roam-unlinked-references-section&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;;; Enable automatic database sync or invoke manually via `M-x org-roam-db-sync&apos;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-roam-db-autosync-mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam-capture-templates&lt;/code&gt; contain two templates – one for regular notes,
and another one for encrypted GPG notes.&lt;/p&gt;

&lt;p&gt;All notes reside in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/path/to/docs/notes/YYYY/MM/&amp;lt;note&amp;gt;.org&lt;/code&gt; path. I prefer
organizing my notes in a timestamped directory structure, but that is a personal
preference. The nice thing about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; (or capture templates in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org&lt;/code&gt; in
general) is that you can tweak it to your personal needs.&lt;/p&gt;

&lt;p&gt;Upon creating a new note I use a hook to add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATED&lt;/code&gt; property, similar to
the other hook for regular &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org&lt;/code&gt; files and headings. Another hook takes care of
adding a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draft&lt;/code&gt; tag to newly created notes, which is manually removed once I’m
done with the note. This helps me keep track of things I need to go back and
work on.&lt;/p&gt;

&lt;p&gt;I also use a couple of functions for
&lt;a href=&quot;https://www.orgroam.com/manual.html#Customizing-Node-Completions-1&quot;&gt;Customizing Node Completions&lt;/a&gt;
via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam-node-mtime&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam-node-created-property&lt;/code&gt; functions.&lt;/p&gt;

&lt;p&gt;This is what it looks when browsing the list of notes via
&lt;a href=&quot;https://github.com/minad/consult&quot;&gt;consult&lt;/a&gt; and
&lt;a href=&quot;https://github.com/minad/marginalia&quot;&gt;marginalia&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/org-roam-node-select.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/org-roam-node-select.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A nice addition to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; is the graphical frontend
&lt;a href=&quot;https://github.com/org-roam/org-roam-ui&quot;&gt;org-roam-ui&lt;/a&gt;, with which you can
browse and read your notes.&lt;/p&gt;

&lt;p&gt;At the time of writing this post this is what the graph of my notes looks like.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/org-roam-ui.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/org-roam-ui.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other Emacs packages that I’ve found useful when working with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; (or
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-mode&lt;/code&gt; in general) include
&lt;a href=&quot;https://github.com/nobiot/org-transclusion&quot;&gt;org-transclusion&lt;/a&gt;,
&lt;a href=&quot;https://github.com/alphapapa/org-ql&quot;&gt;org-ql&lt;/a&gt;,
&lt;a href=&quot;https://github.com/jgru/consult-org-roam&quot;&gt;consult-org-roam&lt;/a&gt; and
&lt;a href=&quot;https://github.com/ahmed-shariff/org-roam-ql&quot;&gt;org-roam-ql&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Whenever I need to export my notes and share them with others I might create a
single note composed of multiple, standalone notes using
&lt;a href=&quot;https://github.com/nobiot/org-transclusion&quot;&gt;org-transclusion&lt;/a&gt;, and then export
it using &lt;a href=&quot;https://github.com/kawabata/ox-pandoc&quot;&gt;ox-pandoc&lt;/a&gt; or another exporter,
depending on what the audience of the document would be.&lt;/p&gt;

&lt;p&gt;We can also export our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; notes in Markdown format, which can then be
served by a static-site generator like Hugo. The
&lt;a href=&quot;https://ox-hugo.scripter.co&quot;&gt;ox-hugo&lt;/a&gt; exporter does exactly this, and I also
use it in order to self-host my notes on an internal system.&lt;/p&gt;

&lt;p&gt;Other options include &lt;a href=&quot;https://orgmode.org/manual/Publishing.html&quot;&gt;Org Mode Publishing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My workflow for publishing notes usually involves two repos – one repo contains
my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; notes, which is specifically being used for storing and tracking
my notes only. And then I also use a second repo, which contains static-site
generator specific configurations and files.&lt;/p&gt;

&lt;p&gt;I prefer keeping them separate, because I don’t want to pollute my main
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; notes repo with anything else, which would simply &lt;em&gt;consume&lt;/em&gt; these
notes. This also allows me to experiment with different static-site generators
without causing too much noise in the main notes repo.&lt;/p&gt;

&lt;p&gt;So, how do I get the notes from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; notes repo to the static-site
generator repo?&lt;/p&gt;

&lt;p&gt;Well, I use an &lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_node/elisp/index.html&quot;&gt;Emacs Lisp&lt;/a&gt; script,
which iterates through each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; node and exports it via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ox-hugo&lt;/code&gt;. However, there were a few
issues I have faced.&lt;/p&gt;

&lt;p&gt;The first one is related to the date-metadata, which is associated with each
entry. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ox-hugo&lt;/code&gt; expects that your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org&lt;/code&gt; contain various &lt;a href=&quot;https://ox-hugo.scripter.co/doc/dates/#dates-file-based-exports&quot;&gt;File-level properties&lt;/a&gt;,
in order to produce a &lt;a href=&quot;https://gohugo.io/content-management/front-matter/&quot;&gt;valid front matter for Hugo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since all of my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; notes already contain the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATED&lt;/code&gt; property, then
the ideal solution would be for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ox-hugo&lt;/code&gt; to use that when generating the Hugo
front matter.&lt;/p&gt;

&lt;p&gt;Unfortunately &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ox-hugo&lt;/code&gt; &lt;a href=&quot;https://github.com/kaushalmodi/ox-hugo/issues/772&quot;&gt;does not support mapping properties to front-matter variables&lt;/a&gt;. But with
&lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_node/elisp/Advising-Functions.html&quot;&gt;Advising Emacs Lisp Functions&lt;/a&gt;
we can implement a function that affects the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-mode&lt;/code&gt; export environment, which &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ox-hugo&lt;/code&gt; uses in
order to generate the Markdown document.&lt;/p&gt;

&lt;p&gt;The following &lt;em&gt;advice function&lt;/em&gt; pushes the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:date&lt;/code&gt; property to the org export
environment. This property represents the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;date&lt;/code&gt; property from the Hugo
front-matter. If needed, we can also push the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:hugo-publishdate&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:hugo-lastmod&lt;/code&gt; properties, which correspond to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;publishDate&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lastmod&lt;/code&gt;
properties respectively.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; All my org-roam notes contain the `CREATED&apos; property, which specifies the&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; creation date of the note. This wrapper adds the `:date&apos; property to the&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; export environment, which `ox-hugo&apos; will use when exporting to Markdown.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-export-get-environment/add-date&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;orig-fun&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;amp;rest&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;orig-fun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;created&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-entry-get&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;CREATED&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;plist-put&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:date&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;created&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;advice-add&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org-export-get-environment&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:around&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-export-get-environment/add-date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another issue I have faced when using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ox-hugo&lt;/code&gt; is related to how links between
notes are generated. Since my notes reside in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notes/YYYY/MM/&lt;/code&gt; directory
structure, when exporting them &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ox-hugo&lt;/code&gt; would generate invalid links, in the
form of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;../MM/some-note.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is documented in this issue about &lt;a href=&quot;https://github.com/kaushalmodi/ox-hugo/issues/668&quot;&gt;broken links when exporting&lt;/a&gt;. Since the final Markdown documents, which will be served by Hugo (or Hugo-compatible service), we can implement the
following hook, which strips away the directory part of the generated link.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; My org-roam capture templates store the notes in `notes/YYYY/MM&apos; directory&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; structure.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; When exporting to Markdown via `ox-hugo&apos; the resulting links are invalid,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; because they would point to `../MM/filename.md&apos;.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; Since all files are being exported to the `$HUGO_BASE_DIR/content/notes&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; directory we are simply stripping any leading directories from the filenames&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; here, in order to have valid links.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-export-resolve-id-link/strip-directory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;last&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-name-split&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;advice-add&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org-export-resolve-id-link&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:filter-return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-export-resolve-id-link/strip-directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, we can assemble all the pieces together and automate the process of
exporting all of our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; notes using the script below.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;!/usr/bin/env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;emacs&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;--script&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; A utility script to export all my org-roam notes via `ox-hugo&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; The script expects the `HUGO_BASE_DIR&apos;, `ORG_ROAM_DIR&apos; and `ORG_ROAM_DB&apos; env&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; vars to be set.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; `HUGO_BASE_DIR&apos; specifies the directory where org-roam nodes will be&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; exported.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; Upon successful completion the script produces the following directories.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; $HUGO_BASE_DIR/content/notes - contains the generated Markdown files&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; $HUGO_BASE_DIR/static/ox-hugo - contains static files, e.g. images&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; In order to serve the notes generated by the script simply sync the Markdown&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; files and static files from the directories above to your Hugo installation.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dolist&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env-var&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HUGO_BASE_DIR&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ORG_ROAM_DIR&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ORG_ROAM_DB&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;getenv&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;env-var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%s env var is not set&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;env-var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;package&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;package-initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;cl-lib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;ox-hugo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;use-package&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-roam&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:config&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; Configure path to the org-roam database and notes&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt;
   &lt;span class=&quot;nv&quot;&gt;org-roam-directory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-truename&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;getenv&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ORG_ROAM_DIR&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
   &lt;span class=&quot;nv&quot;&gt;org-roam-db-location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-truename&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;getenv&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ORG_ROAM_DB&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; My org-roam capture templates store the notes in `notes/YYYY/MM&apos; directory&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; structure.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; When exporting to Markdown via `ox-hugo&apos; the resulting links are invalid,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; because they would point to `../MM/filename.md&apos;.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; Since all files are being exported to the `$HUGO_BASE_DIR/content/notes&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; directory we are simply stripping any leading directories from the filenames&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; here, in order to have valid links.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-export-resolve-id-link/strip-directory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;last&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-name-split&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;advice-add&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org-export-resolve-id-link&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:filter-return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-export-resolve-id-link/strip-directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; All my org-roam notes contain the `CREATED&apos; property, which specifies the&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; creation date of the note.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; This wrapper adds the `:date&apos; property to the export environment, which&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; `ox-hugo&apos; will use when exporting to Markdown.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; Additional keys that may be added here include `:hugo-publishdate&apos;,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; `:hugo-lastmod&apos;, etc.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-export-get-environment/add-date&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;orig-fun&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;amp;rest&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;orig-fun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;created&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-entry-get&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;CREATED&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;plist-put&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:date&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;created&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;advice-add&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org-export-get-environment&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:around&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-export-get-environment/add-date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; Iterate through the org-roam nodes and export each of them&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;hugo-base-dir&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-truename&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;getenv&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;HUGO_BASE_DIR&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;hugo-static-dir&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-name-concat&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;hugo-base-dir&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;static&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-roam-nodes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-roam-node-list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;unique-roam-nodes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cl-remove-duplicates&lt;/span&gt;
                           &lt;span class=&quot;nv&quot;&gt;org-roam-nodes&lt;/span&gt;
                           &lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-roam-node-id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;;; ox-hugo expects that the $HUGO_BASE_DIR/static directory exists in advance.&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-accessible-directory-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;hugo-static-dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;make-directory&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;hugo-static-dir&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;;; Export each org-roam node&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dolist&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;unique-roam-nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;node-title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-roam-node-title&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;node-file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-roam-node-file&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-current-buffer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;find-file-noselect&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node-file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;setq-local&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-hugo-base-dir&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;hugo-base-dir&lt;/span&gt;
                    &lt;span class=&quot;nv&quot;&gt;org-hugo-front-matter-format&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;yaml&quot;&lt;/span&gt;
                    &lt;span class=&quot;nv&quot;&gt;org-hugo-section&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;notes&quot;&lt;/span&gt;
                    &lt;span class=&quot;nv&quot;&gt;org-agenda-files&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
                    &lt;span class=&quot;nv&quot;&gt;org-export-with-broken-links&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-hugo-export-wim-to-md&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Exported %d org-roam node(s)&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;unique-roam-nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also find the &lt;a href=&quot;https://gist.github.com/dnaeon/87427d319ae0b0a14bf7bf2bc0c49a77&quot;&gt;full script here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Download the script and make it executable.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget https://gist.githubusercontent.com/dnaeon/87427d319ae0b0a14bf7bf2bc0c49a77/raw/5034f599a2beb234c6bd28e67dba4f47b17f126d/export-roam-notes-to-hugo.el
&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x export-roam-notes-to-hugo.el
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The script expects the following env vars to be provided.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HUGO_BASE_DIR&lt;/code&gt; - base directory of your Hugo site&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ORG_ROAM_DIR&lt;/code&gt; - directory, which contains your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; notes&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ORG_ROAM_DB&lt;/code&gt; - path to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to run the script either &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;export&lt;/code&gt; the env vars, or pass them using
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env&lt;/code&gt;, e.g.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;env &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;HUGO_BASE_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/path/to/hugo &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;ORG_ROAM_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/path/to/org-notes &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;ORG_ROAM_DB&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/path/to/org-roam.db &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    export-roam-notes-to-hugo.el
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Upon successful completion it will generate the following directories.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$HUGO_BASE_DIR/content/notes&lt;/code&gt; - contains the generated Markdown files&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$HUGO_BASE_DIR/static/ox-hugo&lt;/code&gt; - contains static files, e.g. images&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point you can start up your Hugo instance and browse your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt;
notes.&lt;/p&gt;

&lt;p&gt;Another alternative to Hugo, which I actually like is
&lt;a href=&quot;https://quartz.jzhao.xyz&quot;&gt;Quartz&lt;/a&gt;. Quartz comes with plugins like Graph View,
backlinks, and many others.&lt;/p&gt;

&lt;p&gt;In case you are building a site to host your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; notes with Quartz you
should check out the &lt;a href=&quot;https://www.asterhu.com/post/20240220-publish-org-roam-with-quartz-oxhugo/&quot;&gt;Publish org-roam notes to personal wiki using ox-hugo and Quartz&lt;/a&gt; post,
which provides details on how to get started with Quartz.&lt;/p&gt;

&lt;p&gt;Using the Emacs Lisp script from this post you can bulk-export your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt;
notes and serve them with Quartz. One thing to keep in mind is that when syncing
your generated Markdown files and static files you need to copy them here.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$HUGO_BASE_DIR/content/notes&lt;/code&gt; - needs to be synced at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$QUARTZ_BASE_DIR/content/notes&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$HUGO_BASE_DIR/static/ox-hugo&lt;/code&gt; - needs to be synced at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$QUARTZ_BASE_DIR/content/ox-hugo&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After that, simply start your Quartz instance using the command below.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx quartz build &lt;span class=&quot;nt&quot;&gt;--serve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here is an example of Quartz serving my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; notes.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/quartz-node.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/quartz-node.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here we can see the graph view of Quartz.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/quartz-graph-view.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/quartz-graph-view.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also deploy your site to any of the supported &lt;a href=&quot;https://quartz.jzhao.xyz/hosting&quot;&gt;hosting providers&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
        <link>https://dnaeon.github.io/exporting-org-roam-notes-hugo/</link>
        <guid isPermaLink="true">https://dnaeon.github.io/exporting-org-roam-notes-hugo/</guid>
      </item>
    
      <item>
        <title>Fixing the clicking fan noise of Synology DS224+</title>
        <description>&lt;p&gt;Couple of days ago I got my brand new Synology &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DS224+&lt;/code&gt; disk station, which was
meant to replace my current (and old) Synology &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DS213j&lt;/code&gt; system.&lt;/p&gt;

&lt;p&gt;My old &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DS213j&lt;/code&gt; system has served me well for the past ten years, and I have
been pretty happy with it. Synology also did an awesome job by providing
security updates for all these years to old devices such as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DS213j&lt;/code&gt; I
have. However, it was time for an upgrade.&lt;/p&gt;

&lt;p&gt;After some researching I’ve decided to grab the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DS224+&lt;/code&gt;, which is a great
improvement over my current &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DS213j&lt;/code&gt;. It runs with with 4 cores Intel Celeron
J4125 CPU and comes with 2GB of memory, and you have the option to upgrade the
memory to 6GB in total. And it also has dual-port NICs. Sweet.&lt;/p&gt;

&lt;p&gt;For comparison my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DS213j&lt;/code&gt; runs with a single core Marvell Kirkwood 1.2GHz and
has 512MB of memory. And it has a single NIC.&lt;/p&gt;

&lt;p&gt;Like I’ve mentioned – my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DS213j&lt;/code&gt; has served me well for the past years, and I
still keep it around. One of the things that I really like about the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DS213j&lt;/code&gt; is
how quiet it actually is.&lt;/p&gt;

&lt;p&gt;So, after getting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DS224+&lt;/code&gt; and installing latest DSM 7.2.2 one thing
immediately caught my attention – the noise coming from the fan. And that noise
was simply annoying and clicking. I’ve &lt;a href=&quot;/files/ds224-plus-fan-noise.mp4&quot;&gt;recorded a video of the fan noise&lt;/a&gt;,
coming out of my brand new DS224+, which you can also listen to.&lt;/p&gt;

&lt;p&gt;The first I did was to make sure that the fan is configured in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Quiet Mode&lt;/code&gt; and
to my surprise – that was already the case.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/ds224plus-info.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/ds224plus-info.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, it turned out that I’m not the only one having such issues with
the DS224+. A number of people have already reported similar issues, which you
can find in the links below.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/synology/comments/1bwq0wi/ds224_fan_noise_is_driving_me_crazy/&quot;&gt;ds224+ fan noise is driving me crazy&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/synology/comments/193vsjl/ds224_disk_noise_and_slowness/&quot;&gt;Ds224+ disk noise and slowness?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/synology/comments/1bfwxa1/ds224_fan_noise_at_low_speed/&quot;&gt;DS224+ fan noise at low speed&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/synology/comments/1kgf614/ds224_constant_clicking_sound_that_doesnt_seem/&quot;&gt;DS224+ constant “clicking” sound that doesn’t seem related to the drives&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Interestingly, the only way you can make that annoying noise disappear is if you
simply switch from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Quiet Mode&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Full-speed mode&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fan Speed Mode&lt;/code&gt;
settings. However, that’s not what I really want to do.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DS213j&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DS224+&lt;/code&gt; while being different in terms of CPUs, architecture,
amount of memory, etc., are identical in one thing – and that is the fan they
run with.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://www.synology.com/en-global/products/spare_parts?search_by=category&amp;amp;category=Fan&quot;&gt;Synology Spare Parts&lt;/a&gt; page confirms that as well.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/syno-fan-parts.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/syno-fan-parts.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back in the day I had to replace the fan of my old DS213j and I’ve used a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Noctua NF-A9 FLX / 3 pins&lt;/code&gt;, which to this day is still running. So, I thought
about doing the same with my DS224+ as well – replace the stock fan with a
NF-A9 FLX 3-pin fan, however some people have reported issues when using this
fan in particular with DSM 7.2.x (check the links above for more details).&lt;/p&gt;

&lt;p&gt;Another Noctua fan, for which people have reported good results in replacing
their stock fan is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Noctua NF-B9-redux-1600, 92 x 92 x 25 mm / 3 pins&lt;/code&gt;,
however I didn’t have a spare one I could use immediately, and I had to order
one.&lt;/p&gt;

&lt;p&gt;And while waiting for the NF-B9-redux-1600 to arrive I had to keep listening to
that annoying, clicking sound coming from the DS224+ stock fan. Needless to say,
I had time to tinker with the new DS224+ and see what can be done to fix this.&lt;/p&gt;

&lt;p&gt;After some researching and testing various things I eventually found out about
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/syno/etc.defaults/scemd.xml&lt;/code&gt;, which is the configuration for the fan
curve. This file is parsed by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scemd&lt;/code&gt; service and is used to configure
various disk and CPU temperature thresholds, which when reached will configure
the fan to run either at low and high speeds, so that the system can cool down.&lt;/p&gt;

&lt;p&gt;Comparing this file on my DS224+ with the same file on my old DS213j showed some
differences, which I expected anyways. However, having the exact same fan on
both systems, I would at least expect that the fans behave the same (unless one
of them is actually broken).&lt;/p&gt;

&lt;p&gt;You can find the original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scemd.xml&lt;/code&gt; files for DS213j and DS224+, which I’ve
extracted from my systems here.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/files/scemd-ds213j.xml&quot;&gt;DS213j scemd.xml&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/files/scemd-ds224plus.xml&quot;&gt;DS224+ scemd.xml&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some time later I’ve also found a post from a folk at Reddit, who has provided
details about how he/she fixed the same annoying noise by adjusting the fan
curve profile in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/syno/etc.defaults/scemd.xml&lt;/code&gt;. You can find the
&lt;a href=&quot;https://www.reddit.com/r/synology/comments/1lbvdp6/solution_for_ds224_fan_noise/&quot;&gt;post here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve tried the instructions he left and it actually made a difference on my
DS224+, however there was still a minor low frequency noise coming out of that
fan, which I was not happy with.&lt;/p&gt;

&lt;p&gt;Then I’ve decided to simply adapt my existing DS213j config for DS224+. Turned
out it was pretty simple, and I didn’t have to deal with any of the conversions
from frequency (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hz&lt;/code&gt;) to RPM values.&lt;/p&gt;

&lt;p&gt;A snippet of changes I did to my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/syno/etc.defaults/scemd.xml&lt;/code&gt; file are
provided below.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;nt&quot;&gt;&amp;lt;fan_config&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;period=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;20&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;threshold=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;6&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DUAL_MODE_HIGH&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hibernation_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UNKNOWN&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;disk_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ULTRA_LOW&quot;&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/disk_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;disk_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;VERY_LOW&quot;&lt;/span&gt;   &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;33&lt;span class=&quot;nt&quot;&gt;&amp;lt;/disk_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;disk_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LOW&quot;&lt;/span&gt;        &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;41&lt;span class=&quot;nt&quot;&gt;&amp;lt;/disk_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;disk_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;VERY_HIGH&quot;&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;47&lt;span class=&quot;nt&quot;&gt;&amp;lt;/disk_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;disk_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ULTRA_HIGH&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;53&lt;span class=&quot;nt&quot;&gt;&amp;lt;/disk_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;disk_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ULTRA_HIGH&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SHUTDOWN&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;61&lt;span class=&quot;nt&quot;&gt;&amp;lt;/disk_temperature&amp;gt;&lt;/span&gt;

		&lt;span class=&quot;nt&quot;&gt;&amp;lt;cpu_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ULTRA_LOW&quot;&lt;/span&gt;   &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/cpu_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;cpu_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LOW&quot;&lt;/span&gt;         &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;55&lt;span class=&quot;nt&quot;&gt;&amp;lt;/cpu_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;cpu_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HIGH&quot;&lt;/span&gt;        &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;65&lt;span class=&quot;nt&quot;&gt;&amp;lt;/cpu_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;cpu_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ULTRA_HIGH&quot;&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SHUTDOWN&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;95&lt;span class=&quot;nt&quot;&gt;&amp;lt;/cpu_temperature&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;/fan_config&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;fan_config&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;period=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;20&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;threshold=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;6&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DUAL_MODE_LOW&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hibernation_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UNKNOWN&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;disk_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ULTRA_LOW&quot;&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/disk_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;disk_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;VERY_LOW&quot;&lt;/span&gt;   &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;38&lt;span class=&quot;nt&quot;&gt;&amp;lt;/disk_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;disk_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LOW&quot;&lt;/span&gt;        &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;46&lt;span class=&quot;nt&quot;&gt;&amp;lt;/disk_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;disk_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HIGH&quot;&lt;/span&gt;       &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;52&lt;span class=&quot;nt&quot;&gt;&amp;lt;/disk_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;disk_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;VERY_HIGH&quot;&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;55&lt;span class=&quot;nt&quot;&gt;&amp;lt;/disk_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;disk_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ULTRA_HIGH&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;58&lt;span class=&quot;nt&quot;&gt;&amp;lt;/disk_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;disk_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ULTRA_HIGH&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SHUTDOWN&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;61&lt;span class=&quot;nt&quot;&gt;&amp;lt;/disk_temperature&amp;gt;&lt;/span&gt;

		&lt;span class=&quot;nt&quot;&gt;&amp;lt;cpu_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ULTRA_LOW&quot;&lt;/span&gt;   &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/cpu_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;cpu_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LOW&quot;&lt;/span&gt;         &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;55&lt;span class=&quot;nt&quot;&gt;&amp;lt;/cpu_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;cpu_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HIGH&quot;&lt;/span&gt;        &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;65&lt;span class=&quot;nt&quot;&gt;&amp;lt;/cpu_temperature&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;cpu_temperature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fan_speed=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ULTRA_HIGH&quot;&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SHUTDOWN&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;95&lt;span class=&quot;nt&quot;&gt;&amp;lt;/cpu_temperature&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;/fan_config&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also find the
&lt;a href=&quot;/files/scemd-ds224plus-updated.xml&quot;&gt;full updated scemd.xml file here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to adjust the threshold temperatures for disk and CPU to other values,
if needed.&lt;/p&gt;

&lt;p&gt;After you make changes to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/syno/etc.defaults/scemd.xml&lt;/code&gt; file make sure
that you restart the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scemd&lt;/code&gt; service.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl restart scemd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And with these changes made to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scemd.xml&lt;/code&gt; the clicking noise coming from the
DS224+ fan went away for me, so now I don’t even have to replace it.&lt;/p&gt;
</description>
        <pubDate>Tue, 19 Aug 2025 00:00:00 +0000</pubDate>
        <link>https://dnaeon.github.io/ds224-plus-clicking-fan-noise/</link>
        <guid isPermaLink="true">https://dnaeon.github.io/ds224-plus-clicking-fan-noise/</guid>
      </item>
    
      <item>
        <title>Remote SDR setup with SDRplay &amp; Raspberry Pi</title>
        <description>&lt;p&gt;One of the things I like doing in my spare time is messing around with
&lt;a href=&quot;https://en.wikipedia.org/wiki/Software-defined_radio&quot;&gt;Software-defined radio
(SDR)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Few years ago I started out with a simple setup – got myself an &lt;a href=&quot;https://www.rtl-sdr.com/&quot;&gt;RTL-SDR
dongle&lt;/a&gt;, hooked it up to the computer and connected an
antenna. My choice of operating system is GNU/Linux and (btw) I run &lt;a href=&quot;https://archlinux.org/&quot;&gt;Arch
Linux&lt;/a&gt; as my daily driver. Next thing was to &lt;a href=&quot;https://wiki.archlinux.org/title/RTL-SDR&quot;&gt;configure
the RTL-SDR driver&lt;/a&gt;, install
&lt;a href=&quot;https://www.gqrx.dk/&quot;&gt;gqrx&lt;/a&gt; and start it up. Pure joy.&lt;/p&gt;

&lt;p&gt;Ever since I’ve started with SDR I’ve been using it to listen to random ham
operator conversations, track satellites, listen to distant short-wave stations,
receive and decode &lt;a href=&quot;https://en.wikipedia.org/wiki/Radiofax&quot;&gt;radio faxes&lt;/a&gt;,
&lt;a href=&quot;https://www.ariss.org/contact-the-iss.html&quot;&gt;listening to the International Space Station
(ISS)&lt;/a&gt;, etc.&lt;/p&gt;

&lt;p&gt;It’s a fun thing to do with lots of possibilities! My wife thinks differently,
though. She thinks that “I’m just playing with some toys”. And in a way she
might be right, but it’s still a fun thing to mess with.&lt;/p&gt;

&lt;p&gt;This is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Radiofax&quot;&gt;radio fax&lt;/a&gt; transmitted by the
&lt;a href=&quot;https://de.wikipedia.org/wiki/Deutscher_Wetterdienst&quot;&gt;Deutscher Wetterdienst
(DWD)&lt;/a&gt;, which is more than
1000 kilometers away from where I live and captured the broadcast.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/sdr/wefax_20230609_083939_12789999_gui.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/sdr/wefax_20230609_083939_12789999_gui.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At some later point I got a &lt;a href=&quot;https://support.nooelec.com/hc/en-us/articles/360005812474-Ham-It-Up-Upconverter&quot;&gt;Ham it Up Plus
Upconverter&lt;/a&gt;,
which you can use in combination with your RTL-SDR dongle for short-wave
listening.&lt;/p&gt;

&lt;p&gt;Then an &lt;a href=&quot;https://airspy.com/airspy-r2/&quot;&gt;Airspy R2&lt;/a&gt; with a &lt;a href=&quot;https://airspy.com/spyverter-r2/&quot;&gt;Spyverter
R2&lt;/a&gt;. Compared to RTL-SDR, the Airspy R2 is the
better choice, at least to me. I was getting better reception with the Airspy,
than I was getting with the RTL-SDR dongle.&lt;/p&gt;

&lt;p&gt;I should also mention that I live in
&lt;a href=&quot;https://en.wikipedia.org/wiki/Sofia&quot;&gt;Sofia&lt;/a&gt;, which is the capital of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Bulgaria&quot;&gt;Bulgaria&lt;/a&gt;. I also live in an apartment,
which means that I don’t have much spare space to practice my hobby. I can’t
just install an antenna pole on the balcony here or spread out some long antenna
wires across the buildings. So I have to plan things a bit.&lt;/p&gt;

&lt;p&gt;I’m using one &lt;a href=&quot;https://www.amazon.com/dp/B07RRSBTVW?ref=ppx_yo2ov_dt_b_fed_asin_title&quot;&gt;GRA-D220 144/440/900/1200MHz Mini Discone
Antenna&lt;/a&gt;,
which is a multi-band antenna and is good enough for listening on 2m and 70cm
bands.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/sdr/gra-d220.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/sdr/gra-d220.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The other antenna I use is the &lt;a href=&quot;https://www.amazon.com/dp/B095K89WND?ref=ppx_yo2ov_dt_b_fed_asin_title&amp;amp;th=1&quot;&gt;MLA-30+ Loop Antenna,0.5-30MHz Active Receiving
Antenna&lt;/a&gt;,
which is used for short-wave listening.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/sdr/mla30-plus.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/sdr/mla30-plus.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the challenges I’ve always had with my SDR setup is that each time I
wanted to start it up and just scan through the frequencies I had to take out
the SDR receivers, get the long coax cable from the balcony and take it inside
the room with me, connect it to the SDR, and then plug into the computer. This
takes time, then I have cables crossing the room, and later I’ll have to undo
everything I did once I’m done with it.&lt;/p&gt;

&lt;p&gt;I managed to save myself some time by mounting the SDRs on a wooden plate, which
I then mounted next to my computer, but still the problem with dragging the
antenna cables with me inside the room was bugging me. And drilling holes
through the wall was not an option.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/sdr/airspy-ham-it-up-rtlsdr.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/sdr/airspy-ham-it-up-rtlsdr.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This setup worked well for some time, but it was about time to take things to
the next level.&lt;/p&gt;

&lt;p&gt;Using &lt;a href=&quot;https://github.com/pinkavaj/rtl-sdr/blob/master/src/rtl_tcp.c&quot;&gt;rtl_tcp&lt;/a&gt;
allows you to stream your SDR over the network, so if you have an SDR running on
a remote location, you could connect to it via the network from a remote
client. That means I could just leave the SDRs in a waterproof box on the
balcony and then connect to them over the network. And that also means no more
cables running across the room. I could also access my SDRs from elsewhere by
simply connecting to my home network via
&lt;a href=&quot;https://www.wireguard.com/&quot;&gt;WireGuard&lt;/a&gt;. Easy as that.&lt;/p&gt;

&lt;p&gt;Okay, so what do I need to get this up and running?&lt;/p&gt;

&lt;p&gt;I’ve got the SDRs already, and the antennas, I would also need a computer as
well, which I can simply put inside the box with the rest of the SDRs. The
obvious choice here is to use a &lt;a href=&quot;https://www.raspberrypi.com/products/raspberry-pi-5/&quot;&gt;Raspberry
Pi&lt;/a&gt;, first because of it’s
dimensions, and second because it’s powerful enough to serve this purpose.&lt;/p&gt;

&lt;p&gt;However, putting all these things in a single box ended up as quite a challenge
and eventually I started looking for a new compact SDR, with dual antenna ports,
which I could put inside the box along with the Raspberry Pi.&lt;/p&gt;

&lt;p&gt;Eventually, I ended with the &lt;a href=&quot;https://www.sdrplay.com/rspduo/&quot;&gt;RSPduo from
SDRplay&lt;/a&gt;. The RSPduo can operate between 1kHz
and 2GHz, which meant that I don’t actually need an upconverter anymore, and
that also means less space is required to fit all these components in a single
waterproof box.&lt;/p&gt;

&lt;p&gt;With all these details out of the way it was time to get some work done.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/sdr/rpi-rspduo-parts.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/sdr/rpi-rspduo-parts.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a list of the components I’ve used.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.raspberrypi.com/products/raspberry-pi-5/&quot;&gt;Raspberry Pi 5&lt;/a&gt; - 16GB (8GB is more than enough, but I’ve got the 16GB anyways)&lt;/li&gt;
  &lt;li&gt;SanDisk SD Card&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://odseven.com/collections/micro-bit/products/heavy-duty-aluminum-passive-cooling-case-for-raspberry-pi-5&quot;&gt;Aluminium Passive Cooling Case&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.raspberrypi.com/products/27w-power-supply/&quot;&gt;Raspberry Pi 27W USB-C Power Supply&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.sdrplay.com/rspduo/&quot;&gt;RSPduo&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;USB-A to USB-B cable (RSPduo doesn’t come with USB cables)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After putting all these components in a box it looks like this. This is the
Raspberry Pi in it’s aluminium case.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/sdr/rpi-case.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/sdr/rpi-case.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All components packed nicely in the box. I’ve used some velcro strips to get the
components &lt;em&gt;mounted&lt;/em&gt; in the box, which I think ended up good enough.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/sdr/sdr01-box-open.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/sdr/sdr01-box-open.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/sdr/sdr01-box-open2.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/sdr/sdr01-box-open2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this is how the box looks like when closed.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/sdr/sdr01-box-closed.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/sdr/sdr01-box-closed.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having all the components in place it was time to install the software. The
Raspberry Pi runs with
&lt;a href=&quot;https://sourceforge.net/projects/dragonos-pi64/&quot;&gt;DragonOS&lt;/a&gt;, which comes with
many SDR-related tools pre-installed. In order to install it download the
&lt;a href=&quot;https://sourceforge.net/projects/dragonos-pi64/&quot;&gt;DragonOS_Pi64&lt;/a&gt; image and
install it on your SD card using the &lt;a href=&quot;https://www.raspberrypi.com/software/&quot;&gt;Raspberry Pi
Imager&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once installed boot your Raspberry Pi and login to it. The default username and
password in DragonOS are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Username: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ubuntu&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Password: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dragon&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now it’s time to configure the Pi. You can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raspi-config&lt;/code&gt; tool to
configure various system settings such as hostname, password, WiFi, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/sdr/raspi-config.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/sdr/raspi-config.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure that your system is up-to-date as well.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get update
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get upgrade
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In case you need any other packages, now is the time to install them as well,
e.g. install command-line editors (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mg&lt;/code&gt;), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo(8)&lt;/code&gt; access, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lm-sensors&lt;/code&gt;
for monitoring the sensors of your Pi, etc.&lt;/p&gt;

&lt;p&gt;Once you are done with updating and configuring your Pi we need to reboot it.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: In case you are connecting to your Pi using VNC and experiencing
strange issues such as blank screen you can follow the steps below to get VNC up
and running. If you don’t use VNC at all, or don’t experience such issue, you
can safely skip the next few steps, which discuss how to fix the VNC connection
to your Pi.&lt;/p&gt;

&lt;p&gt;Check the status of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vncserver-x11-serviced&lt;/code&gt; service.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ubuntu@sdr01:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;journalctl &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; vncserver-x11-serviced.service
May 21 06:45:45 ubuntu systemd[1]: Started vncserver-x11-serviced.service - VNC Server &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;Service Mode daemon.
May 21 06:45:46 ubuntu vncserver-x11[2568]: ServerManager: Server started
May 21 06:45:48 ubuntu vncserver-x11[2568]: ConsoleDisplay: Cannot find a running X server on vt1
May 21 06:46:08 ubuntu vncserver-x11[2568]: ConsoleDisplay: Found running X server &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3471, &lt;span class=&quot;nv&quot;&gt;binary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/usr/lib/xorg/Xorg&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
May 29 11:25:12 ubuntu vncserver-x11[2568]: Connections: connected: 192.168.88.70::63330 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;TCP&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
May 29 11:25:12 ubuntu vncserver-x11[2568]: Connections: disconnected: 192.168.88.70::63330 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;TCP&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;([&lt;/span&gt;ConnFailed] No configured security &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;is supported by 3.3 VNC Viewer&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
May 29 11:25:32 ubuntu vncserver-x11[2568]: Connections: connected: 192.168.88.70::63335 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;TCP&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
May 29 11:25:32 ubuntu vncserver-x11[2568]: Connections: disconnected: 192.168.88.70::63335 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;TCP&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;([&lt;/span&gt;ConnFailed] No configured security &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;is supported by 3.3 VNC Viewer&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
May 29 11:26:51 ubuntu vncserver-x11[2568]: Connections: connected: 192.168.88.70::63352 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;TCP&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
May 29 11:26:51 ubuntu vncserver-x11[2568]: Connections: disconnected: 192.168.88.70::63352 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;TCP&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;([&lt;/span&gt;ConnFailed] No configured security &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;is supported by 3.3 VNC Viewer&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I was having issues with VNC on my Pi, and from the logs above you can see that
as soon as the session is established it is followed immediately by closing the
session. Here is how I’ve fixed it on my Pi.&lt;/p&gt;

&lt;p&gt;First, stop and disable the VNC services.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl stop vncserver-virtuald.service
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl stop vncserver-x11-serviced.service
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl disable vncserver-virtuald.service
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl disable vncserver-x11-serviced.service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;realvnc-vnc-server&lt;/code&gt; server package.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get remove realvnc-vnc-server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we will install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tigervnc&lt;/code&gt; server instead, which works just fine.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;tigervnc-standalone-server tigervnc-xorg-extension tigervnc-tools
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tigervnc&lt;/code&gt; we need to configure user mappings in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/tigervnc/vncserver.users&lt;/code&gt; file.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# TigerVNC User assignment&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# This file assigns users to specific VNC display numbers.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# The syntax is &amp;lt;display&amp;gt;=&amp;lt;username&amp;gt;. E.g.:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# :2=andrew&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# :3=lisa&lt;/span&gt;
:1&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ubuntu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the config above we have mapped user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ubuntu&lt;/code&gt; to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:1&lt;/code&gt; display on port
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5901&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create a VNC password for your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ubuntu&lt;/code&gt; user.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vncpasswd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Type and verify your password. The password will be hashed and stored in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.vnc/passwd&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Password:
Verify:
Would you like to enter a view-only password &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;y/n&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;? n
A view-only password is not used
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Enable and start an instance of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tigervncserver&lt;/code&gt; service.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl start tigervncserver@:1.service
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl &lt;span class=&quot;nb&quot;&gt;enable &lt;/span&gt;tigervncserver@:1.service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You should be able to verify that the VNC server is up and running and it’s
already listening for new connections.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ubuntu@sdr01:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;netstat &lt;span class=&quot;nt&quot;&gt;-tlpan&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;5901
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
tcp        0      0 127.0.0.1:5901          0.0.0.0:&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;               LISTEN      248895/Xtigervnc
tcp6       0      0 ::1:5901                :::&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;                    LISTEN      248895/Xtigervnc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that it listens on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt; interface only, which is fine with me, since
I don’t want it exposed anyways. In order to access the VNC server I’m doing it
over an SSH tunnel.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh &lt;span class=&quot;nt&quot;&gt;-L&lt;/span&gt; 5901:localhost:5901 sdr01.internal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we can start up a VNC client and connect to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost:5901&lt;/code&gt; using the
password we’ve set before with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vncpasswd&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For additional information about TigerVNC, please refer to the &lt;a href=&quot;https://wiki.archlinux.org/title/TigerVNC&quot;&gt;TigerVNC Arch
Linux Wiki&lt;/a&gt; page.&lt;/p&gt;

&lt;p&gt;With the VNC issues out of the way, we can proceed and install the &lt;a href=&quot;https://www.sdrplay.com/api/&quot;&gt;SDRplay API&lt;/a&gt;
and &lt;a href=&quot;https://www.sdrplay.com/sdrconnect/&quot;&gt;SDRconnect&lt;/a&gt; on our Pi.&lt;/p&gt;

&lt;p&gt;You can also watch this nice &lt;a href=&quot;https://www.youtube.com/watch?v=kHOVAJi1JBs&quot;&gt;intro about SDRconnect in this video
here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At the time of writing this document the version of SDRconnect I’m installing is
release &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;83273bcd8&lt;/code&gt;, which was downloaded from the SDRplay website.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ubuntu@sdr01:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;wget https://www.sdrplay.com/software/SDRconnect_linux-arm64_83273bcd8.run
ubuntu@sdr01:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./SDRconnect_linux-arm64_83273bcd8.run
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once SDRconnect is successfully installed you should see output similar to the
one below.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Press y and RETURN to accept the license agreement and &lt;span class=&quot;k&quot;&gt;continue &lt;/span&gt;with
the installation, or press n and RETURN to &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;the installer &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;y/n] y

Testing &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;arm64 compliance.......Done

Checking &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;package tools...apt found

Checking &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;libusb-1.0...found
Checking &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;libasound2...found
Checking &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;libuuid1...found
Checking &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;libudev1...found

udev directory found, adding rules &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;RSPs...Done

SDRconnect will be installed into /opt/sdrconnect
To change the installation path, enter the new location at the prompt.
Installation location &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;/opt/sdrconnect]:

SDRconnect will be installed to: /opt/sdrconnect
To start the installation press y and RETURN or n and RETURN to &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;y/n] y
Installing to /opt/sdrconnect.............Done

Create a system menu entry to launch SDRconnect client? &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;y/n] y
Installing menu entry......Done

Checking SDRconnect Server Service status...not installed

Do you want this installation to operate the SDRconnect server as a continuous
background service? &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;y/n] y
SDRconnect Server Service options file installed.
 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /opt/sdrconnect/server_options.txt

SDRconnect Server Service file created.
Installing service... &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;may take a &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;, please wait...&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
SDRconnect Server Service started.

To change the server service &lt;span class=&quot;nb&quot;&gt;command &lt;/span&gt;line options, edit the
/opt/sdrconnect/server_options.txt file and start the service again.

To stop and start the service, use...
      &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl stop sdrconnect
      &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl start sdrconnect

To disable the service &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;e.g. to stop the service starting after a reboot&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; and
&lt;span class=&quot;k&quot;&gt;then &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;enable &lt;/span&gt;it again, use...
      &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl disable sdrconnect.service
      &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl &lt;span class=&quot;nb&quot;&gt;enable &lt;/span&gt;sdrconnect.service

Make a note of this information!
Important Note: When the server service is running, the RSP that it is using
cannot be used by another application on this host. You will need to stop the
server service to make that RSP available to other applications.

Press any key to &lt;span class=&quot;k&quot;&gt;continue

&lt;/span&gt;To add the new installation folder to your search path, please add the following
line to the bottom of your ~/.bashrc file and restart your terminal window...

&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/opt/sdrconnect:&lt;span class=&quot;nv&quot;&gt;$PATH&lt;/span&gt;

Warning:
System files have been changed on your system, so it is recommended that you
reboot your system &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;correct operation.

SDRconnect installation finished.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/opt/sdrconnect&lt;/code&gt; to your PATH as mentioned in the output above.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;export PATH=/opt/sdrconnect:$PATH&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Time to install SDRplay RSP API.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ubuntu@sdr01:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
ubuntu@sdr01:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./SDRplay_RSP_API-Linux-3.15.2.run
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is what the output from the install script looks like.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Verifying archive integrity... All good.
Uncompressing SDRplay RSP API Install Package V3.15 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;installer v2&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
SDRplay API 3.15 Installation
&lt;span class=&quot;o&quot;&gt;=============================&lt;/span&gt;

This installation requires to either be run as root, or it will request root
access through the &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;program. This is required to &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;the daemon into
the system files.

Press RETURN to view the license agreement

...

Press y and RETURN to accept the license agreement and &lt;span class=&quot;k&quot;&gt;continue &lt;/span&gt;with
the installation, or press n and RETURN to &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;the installer &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;y/n] y

A copy of the license agreement can be found here: /home/ubuntu/sdrplay_license.txt


Architecture reported as being 64 bit
System reports 64 bit files found
System is also setup to produce 64 bit files
Architecture reports machine as being arm64 compliant


Checking &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;root permissions. You may be prompted &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;your password...

The rest of the installation will &lt;span class=&quot;k&quot;&gt;continue &lt;/span&gt;with root permission...


This API requires the following system dependencies...
libusb and lidudev
If the installer cannot detect the presence of these, you will
have the option to &lt;span class=&quot;k&quot;&gt;continue &lt;/span&gt;and you will need to &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;them

Checking &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;package tools...
apt found

Checking &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;packages...
libusb-1.0 found
libudev1 found
Udev directory found, adding rules...rules added
Adding SDRplay devices to the &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;hardware database...Done
USB name database not found, continuing...

Installing API files, the default locations are...
API service : /opt/sdrplay_api
API header files : /usr/local/include
API shared library : /usr/local/lib
Daemon start scripts : /etc/systemd/system
Daemon start system : SystemD

To &lt;span class=&quot;k&quot;&gt;continue &lt;/span&gt;the installation with these defaults press y and RETURN
or press n and RETURN to change them &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;y/n] y
Cleaning old API files...Done.
Installing /usr/local/lib/libsdrplay_api.so.3.15...Done
Installing header files &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; /usr/local/include...Done
Installing API Service &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; /opt/sdrplay_api...Done
Installing Service scripts and starting daemon...Created symlink /etc/systemd/system/multi-user.target.wants/sdrplay.service → /etc/systemd/system/sdrplay.service.
Done

The API Service has been installed and started. After the installation has
finished, please reboot this device.

To start and stop the API service, use the following commands...

&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl start sdrplay
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl stop sdrplay

If supported on your system, lsusb will now show the RSP name

SDRplay API 3.15 Installation Finished
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Time to reboot the system as mentioned in the output above.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Verify that you can see your SDRPlay device after rebooting and connecting your SDR.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ubuntu@sdr01:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 002: ID 1df7:3020 SDRplay RSPduo
Bus 003 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 004 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 005 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In order to connect remotely via SDRConnect the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sdrconnect&lt;/code&gt; service should
already be running and listening on port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;50000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Verify that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sdrconnect.service&lt;/code&gt; is up and running.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ubuntu@sdr01:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl status sdrconnect
● sdrconnect.service - SDRconnect Server
     Loaded: loaded &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;/etc/systemd/system/sdrconnect.service&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; enabled&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; preset: enabled&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
     Active: active &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;running&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; since Sun 2025-07-13 13:19:50 EEST&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 26min ago
    Process: 2052 &lt;span class=&quot;nv&quot;&gt;ExecStartPre&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/bin/sleep 30 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;exited, &lt;span class=&quot;nv&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0/SUCCESS&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
   Main PID: 6096 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;startServer.sh&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
      Tasks: 24 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;limit: 18709&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
     Memory: 38.9M &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;peak: 39.3M&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        CPU: 9.191s
     CGroup: /system.slice/sdrconnect.service
             ├─6096 /bin/sh /opt/sdrconnect/startServer.sh
             └─6098 /opt/sdrconnect/SDRconnect &lt;span class=&quot;nt&quot;&gt;--server&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;50000

Jul 13 13:19:52 sdr01.internal startServer.sh[6098]: RF Notch Disabled
Jul 13 13:19:52 sdr01.internal startServer.sh[6098]: DAB Notch Disabled
Jul 13 13:19:52 sdr01.internal startServer.sh[6098]: Hardware Control: 1st Client can control the hardware
Jul 13 13:19:52 sdr01.internal startServer.sh[6098]: Maximum number of clients: 8
Jul 13 13:19:52 sdr01.internal startServer.sh[6098]: PPM Correction: 0
Jul 13 13:19:52 sdr01.internal startServer.sh[6098]: Full IQ: Allowed
Jul 13 13:19:52 sdr01.internal startServer.sh[6098]: Audio mode: Allowed
Jul 13 13:19:52 sdr01.internal startServer.sh[6098]: Virtual Receivers &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;VRXs&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: Allowed
Jul 13 13:19:52 sdr01.internal startServer.sh[6098]: Server started
Jul 13 13:19:52 sdr01.internal startServer.sh[6098]: Press CTRL-C to stop the server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And confirm that it’s listening on port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;50000&lt;/code&gt; port.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ubuntu@sdr01:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;netstat &lt;span class=&quot;nt&quot;&gt;-tlpan&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;50000
tcp        0      0 0.0.0.0:50000           0.0.0.0:&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;               LISTEN      6098/SDRconnect
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: In case you want to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gqrx&lt;/code&gt; with your RSPduo device and upon
starting up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gqrx&lt;/code&gt; you don’t see the RSPduo in the device list, then make sure
to stop the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sdrconnect&lt;/code&gt; service as the device is already in use by it.&lt;/p&gt;

&lt;p&gt;So, to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gqrx&lt;/code&gt; with your RSPduo first stop the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sdrconnect&lt;/code&gt; service.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl stop sdrconnect
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Start up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gqrx&lt;/code&gt; now and you should see the RSPduo in the device list. If you
don’t intend to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gqrx&lt;/code&gt; and want to stick to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SDRconnect&lt;/code&gt; you should leave
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sdrconnect&lt;/code&gt; service up and running.&lt;/p&gt;

&lt;p&gt;In order to connect to your Pi using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SDRconnect&lt;/code&gt; you should first add a new
remote device. Go ahead and add your Pi to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SDRconnect&lt;/code&gt; remote devices list,
test the connection and save.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/sdr/sdrconnect-remote-devices.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/sdr/sdrconnect-remote-devices.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve saved your device you need to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Refresh Device List&lt;/code&gt; as well.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/sdr/sdrconnect-refresh-device-list.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/sdr/sdrconnect-refresh-device-list.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now you should see your device and be able to connect to it.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/sdr/sdrconnect-device-list.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/sdr/sdrconnect-device-list.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a screenshot of SDRconnect receiving from some local WFM broadcast
stations.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/sdr/sdrconnect-wfm.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/sdr/sdrconnect-wfm.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately at the time of writing this document there was no activity on the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2m&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;70cm&lt;/code&gt; bands, so I could not take some screenshots of them as well.&lt;/p&gt;

&lt;p&gt;Well, that’s all for now.&lt;/p&gt;

&lt;p&gt;LZ1DFB, 73!&lt;/p&gt;
</description>
        <pubDate>Sun, 13 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://dnaeon.github.io/remote-sdr-setup/</link>
        <guid isPermaLink="true">https://dnaeon.github.io/remote-sdr-setup/</guid>
      </item>
    
      <item>
        <title>A new member of the homelab -- HP Pro Mini 400 G9</title>
        <description>&lt;p&gt;My homelab setup hasn’t changed much for the last few years.&lt;/p&gt;

&lt;p&gt;I have been happily running a couple of Intel NUC systems over the
years. Initially they have been running ESXi, then they got migrated to &lt;a href=&quot;https://wiki.archlinux.org/title/KVM&quot;&gt;Arch
Linux and KVM&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The workloads running on these systems vary, depending on what I’m actually
working on or testing at a given point in time, but the ones that keep running
on them at all times are
&lt;a href=&quot;https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-issuer-discovery&quot;&gt;Kubernetes&lt;/a&gt;,
&lt;a href=&quot;https://adguard.com/en/welcome.html&quot;&gt;AdGuard&lt;/a&gt;,
&lt;a href=&quot;https://syncthing.net/&quot;&gt;SyncThing&lt;/a&gt;,
&lt;a href=&quot;https://wiki.archlinux.org/title/Plex&quot;&gt;Plex&lt;/a&gt;, &lt;a href=&quot;https://about.gitea.com/&quot;&gt;Gitea&lt;/a&gt;
and some &lt;a href=&quot;https://docs.gitea.com/usage/actions/act-runner&quot;&gt;Gitea Action
Runners&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For running Kubernetes I’m using &lt;a href=&quot;https://www.talos.dev/&quot;&gt;Talos Linux&lt;/a&gt; nodes,
and I really like the way Talos handles the Kubernetes lifecycle management
using minimal and immutable nodes.&lt;/p&gt;

&lt;p&gt;I should mention that the Intel NUC systems are quite old. One of them is
running an Intel i3 5th generation, and the other one is Intel NUC i5 8th
generation. Over the years the only maintenance I had to do on these systems is
to make sure to replace their fans, but besides that they have been rock solid.&lt;/p&gt;

&lt;p&gt;While these systems do serve their purpose just fine for my humble homelab
requirements, I thought it was about time I upgrade it a bit, so that I can
future-proof it for the years to come.&lt;/p&gt;

&lt;p&gt;In case you haven’t heard of this yet, Intel has discontinued development of the
NUC systems. Future development of NUC is now handled by ASUS, so before I bite
the bullet and order a new NUC, I’ve decided to do a bit of research about these
new ASUS NUCs.&lt;/p&gt;

&lt;p&gt;Unfortunately, when reading reviews for the ASUS NUCs you will find lots of
mixed feelings – some people claim they are good, others mention that they run
pretty hot and thermal throttle. One of the most concerning things to me is the
fact that I’ve been reading a lot of reviews about the ASUS NUC 14 (and 15) Pro
from users complaining about the loud fan noise. And that is not something I’d
like to have.&lt;/p&gt;

&lt;p&gt;With the old Intel NUCs it was easy – the CPUs back in the day were not that
powerful and with low enough TDP, making them a no brainer when it comes to
picking up an Intel NUC for your homelab.&lt;/p&gt;

&lt;p&gt;Eventually, I gave up on the idea of having an ASUS NUC as my new homelab node,
and after some time spent on researching I decided to go with &lt;a href=&quot;https://www.hp.com/us-en/shop/mdp/prodesk-400-mini&quot;&gt;HP Pro Mini 400
G9&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I went with the following specs.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Intel Core i5-13500T&lt;/li&gt;
  &lt;li&gt;64GB memory&lt;/li&gt;
  &lt;li&gt;2TB Samsung 990 Evo Plus NVME&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additional specs about this machine can be found in the &lt;a href=&quot;https://h20195.www2.hp.com/v2/getpdf.aspx/c08017709.pdf&quot;&gt;HP Pro Series 400 G9
specs&lt;/a&gt; page.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/hp-pro-mini-400-g9.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/hp-pro-mini-400-g9.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compared to my old Intel NUCs this system is more powerful and capable of
replacing my two NUC nodes. The case is all metal, which is better than any
plastic case, and helps with thermal dissipation.&lt;/p&gt;

&lt;p&gt;Besides the NVME drive you can also install a 2.5 inch SATA SSD as well. One
thing that was not clear to me when ordering this machine is the fact that when
you purchase this system with an NVME disk it will arrive &lt;em&gt;without&lt;/em&gt; the 2.5 inch
SATA Drive Bay kit.&lt;/p&gt;

&lt;p&gt;So, if you are planning on installing a 2.5 inch SATA disk you will need to
order the &lt;a href=&quot;https://www.hp.com/ie-en/products/accessories/product-details/38000177&quot;&gt;HP Desktop Mini 2.5” SATA Drive Bay kit v2
(13L70AA)&lt;/a&gt;
separately. I got my brand new 2.5 inch SATA drive bay from
&lt;a href=&quot;https://www.ebay.de/itm/205496237120&quot;&gt;eBay&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/hp-13l70aa-1.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/hp-13l70aa-1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/hp-13l70aa-2.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/hp-13l70aa-2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/hp-13l70aa-3.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/hp-13l70aa-3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve also got two &lt;a href=&quot;https://www.solidigm.com/products/data-center/d3/s4520.html#form=2.5%22%207mm&amp;amp;cap=1.92%20TB&quot;&gt;SOLIDIGM D3-S4520 SATA
SSD&lt;/a&gt;
disks. These are enterprise SATA SSD disks with Power Loss Protection (PLP) and
should serve me well for the coming years.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/solidigm-d3-s4520.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/solidigm-d3-s4520.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the system only has a single drive bay one of these disks goes into the drive
bay, and the other one will be connected to the machine via a USB Type C
enclosure. Then both disks will be part of a ZFS mirror pool. The NVME disk will
be added to a separate ZFS pool and will be used for workloads which have faster
I/O needs.&lt;/p&gt;

&lt;p&gt;Another nice thing about this system is how easy it is to access the mainboard,
throw some RAM in it, replace an SSD or fan, which makes maintenance an easy and
hassle-free job. The installation of the drive bay was easy and nicely fits into
the chassis.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/hp-13l70aa-4.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/hp-13l70aa-4.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One thing I really like about the HP Pro Mini 400 G9 is how quite it actually
is, even under load.&lt;/p&gt;

&lt;p&gt;I’ve installed &lt;a href=&quot;https://www.proxmox.com/en/&quot;&gt;Proxmox VE&lt;/a&gt; on this system with
&lt;a href=&quot;https://openzfs.org/wiki/Main_Page&quot;&gt;OpenZFS &lt;/a&gt; and have already migrated my
workloads from the Intel NUC systems to it.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;zpool status
  pool: rpool
 state: ONLINE
  scan: resilvered 38.3G &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;00:01:42 with 0 errors on Mon Jul 14 21:43:27 2025
config:

        NAME                                                     STATE     READ WRITE CKSUM
        rpool                                                    ONLINE       0     0     0
          mirror-0                                               ONLINE       0     0     0
            ata-SOLIDIGM_SSDSC2KB019TZ_PHYI513202JB1P9DGN-part2  ONLINE       0     0     0
            ata-SOLIDIGM_SSDSC2KB019TZ_PHYI513202F91P9DGN-part2  ONLINE       0     0     0

errors: No known data errors
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here’s the system installed in the homelab rack.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/hp-pro-mini-400-g9-rack.jpg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/hp-pro-mini-400-g9-rack.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the picture above you will see one of the Intel NUCs, the new HP Pro Mini 400
G9 next to it, a &lt;a href=&quot;https://mikrotik.com/products/compare/RB3011UiAS-RM&quot;&gt;MikroTik RouterBOARD RB3011
UIAS-RM&lt;/a&gt;,
&lt;a href=&quot;https://jetkvm.com/&quot;&gt;JetKVM&lt;/a&gt; currently connected to the Intel NUC and my old
Synology DS213j storage server, which needs an upgrade as well.&lt;/p&gt;
</description>
        <pubDate>Mon, 07 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://dnaeon.github.io/hp-pro-mini-400-g9/</link>
        <guid isPermaLink="true">https://dnaeon.github.io/hp-pro-mini-400-g9/</guid>
      </item>
    
      <item>
        <title>Using JWT Authentication Method with the Go SDK for Vault</title>
        <description>&lt;p&gt;In the &lt;a href=&quot;https://dnaeon.github.io/kubernetes-vault-oidc/&quot;&gt;Setting up a Kubernetes cluster with Vault and OIDC
trust&lt;/a&gt; post we’ve seen how to
configure identity federation between a Kubernetes cluster and Vault.&lt;/p&gt;

&lt;p&gt;Once you have the &lt;a href=&quot;https://developer.hashicorp.com/vault/docs/auth/jwt&quot;&gt;JWT Authentication
Method&lt;/a&gt; configured properly
in Vault, we can get access to Vault by first authenticating against the auth
method’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/login&lt;/code&gt; endpoint. This is how you would do it when using the Vault
CLI.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vault write auth/jwt/login &lt;span class=&quot;nv&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;my-role-name &lt;span class=&quot;nv&quot;&gt;jwt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;@/path/to/my/token
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On successful authentication Vault will give us back a token, which we can use
in subsequent calls to Vault, e.g. for retrieving secrets, adding new secrets,
etc.&lt;/p&gt;

&lt;p&gt;And what about the SDK? How do authenticate against our JWT/OIDC Auth Method
when using the Go SDK?&lt;/p&gt;

&lt;p&gt;The Vault SDK for Go can be found in the
&lt;a href=&quot;https://github.com/hashicorp/vault/tree/main/api&quot;&gt;github.com/hashicorp/vault/api&lt;/a&gt;
package. There’s also a &lt;a href=&quot;https://github.com/hashicorp/vault-examples/blob/main/examples/_quick-start/go/example.go&quot;&gt;quick start example for
Go&lt;/a&gt;
in the
&lt;a href=&quot;https://github.com/hashicorp/vault-examples/tree/main&quot;&gt;github.com/hashicorp/vault-examples&lt;/a&gt;
repo. And in the same repo there’s also an &lt;a href=&quot;https://github.com/hashicorp/vault-examples/blob/main/examples/auth-methods/kubernetes/go/example.go&quot;&gt;example of using JWT Auth Method
from a Kubernetes
cluster&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But what about a more generic JWT/OIDC Auth Method support in the Go SDK? You
know, one that doesn’t actually rely on &lt;a href=&quot;https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-issuer-discovery&quot;&gt;Kubernetes Service Account
Tokens&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Well, it turns out that a more generic &lt;a href=&quot;https://github.com/hashicorp/vault/issues/28912&quot;&gt;JWT Auth Method does not exist in the
SDK yet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Technically speaking the
&lt;a href=&quot;https://github.com/hashicorp/vault/blob/0ee8d99d9c7d5ce0e1a4638e26c05a716032b3c5/api/auth/kubernetes/kubernetes.go#L14-L18&quot;&gt;auth.KubernetesAuth&lt;/a&gt;
implementation of the
&lt;a href=&quot;https://github.com/hashicorp/vault/blob/0ee8d99d9c7d5ce0e1a4638e26c05a716032b3c5/api/auth.go#L16-L18&quot;&gt;vault.AuthMethod&lt;/a&gt;
interface does that already, and you could use it outside of Kubernetes by
tweaking the various options such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithServiceAccountToken&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithServiceAccountTokenPath&lt;/code&gt;, etc., but at the same time this implementation
contains too much Kubernetes details such as service accounts, default token
path in a Kubernetes pod, etc.&lt;/p&gt;

&lt;p&gt;Besides, the name of the Auth Method implementation might confuse people when
reading your code making them think that Kubernetes is a requirement, while
actually it is not.&lt;/p&gt;

&lt;p&gt;So, what can we do? Well, we can implement our own
&lt;a href=&quot;https://github.com/hashicorp/vault/blob/0ee8d99d9c7d5ce0e1a4638e26c05a716032b3c5/api/auth.go#L16-L18&quot;&gt;vault.AuthMethod&lt;/a&gt;
based on the existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;auth.KubernetesAuth&lt;/code&gt; implementation. And this is what we
are going to do in this post.&lt;/p&gt;

&lt;p&gt;Will start off by creating a new Go module for our JWT Auth Method
implementation.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir &lt;/span&gt;vault-jwt-auth-method
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;vault-jwt-auth-method
go mod init vault-jwt-auth-method
go get &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; github.com/hashicorp/vault/api
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Our JWT Auth Method implementation will reside in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;auth/jwt&lt;/code&gt; package,
similar to the way how the existing Auth Method implementations reside in the
upstream Vault SDK repo.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; pkg/auth/jwt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg/auth/jwt/jwt.go&lt;/code&gt; in your favorite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$EDITOR&lt;/code&gt;. And this is what the
JWT Auth Method implementation looks like.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jwt&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;context&quot;&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;errors&quot;&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;os&quot;&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;strings&quot;&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;github.com/hashicorp/vault/api&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// DefaultMountPath specifies the default mount path for the JWT&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// Authentication Method.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DefaultMountPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;jwt&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// ErrNoToken is an error, which is returned when [JWTAuth] is configured&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// with an empty token.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ErrNoToken&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;no token specified&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// ErrInvalidMountPath is an error, which is returned when configuring [JWTAuth]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// to use an invalid mount path for a Vault Authentication Method.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ErrInvalidMountPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;invalid auth method mount path specified&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// JWTAuth implements support for the [JWT Authentication Method].&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// [JWT Authentication Method]: https://developer.hashicorp.com/vault/docs/auth/jwt&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JWTAuth&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// roleName specifies the name of the role to use.&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;roleName&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;

	&lt;span class=&quot;c&quot;&gt;// mountPath specifies the mount path for the JWT Authentication Method.&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;mountPath&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;

	&lt;span class=&quot;c&quot;&gt;// token specifies the JWT token which will be used for authenticating&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// against the Vault Authentication Method endpoint.&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AuthMethod&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JWTAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// Option is a function which configures [JWTAuth].&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JWTAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// New creates a new [JWTAuth] and configures it with the given options.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// The default mount path for the JWT Authentication Method is&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// [DefaultMountPath]. In order to configure a different mount path for the&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// Authentication Method you can use the [WithMountPath] option.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// The JWT token which will be used for authentication against the Vault&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// Authentication Method login endpoint may be specified either as a string,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// from path, or via an environment variable. In order to configure the token&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// for authentication use the [WithToken], [WithTokenFromPath] or&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// [WithTokenFromEnv] options.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roleName&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JWTAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;jwtAuth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JWTAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;roleName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;roleName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DefaultMountPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jwtAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jwtAuth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ErrNoToken&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jwtAuth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mountPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ErrInvalidMountPath&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jwtAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// Login implements the [vault.AuthMethod] interface.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JWTAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Secret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sprintf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;auth/%s/login&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;s&quot;&gt;&quot;jwt&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;s&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roleName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Logical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WriteWithContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// WithToken is an [Option], which configures [JWTAuth] to use the given token&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// when authenticating against the Vault JWT Authentication Method.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WithToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JWTAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;

		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// WithTokenFromPath is an [Option], which configures [JWTAuth] to read the&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// token from the given path.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WithTokenFromPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JWTAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReadFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// WithTokenFromEnv is an [Option], which configures [JWTAuth] to read the token&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// from the given environment variable.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WithTokenFromEnv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JWTAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Getenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;

		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// WithMountPath is an [Option], which configures [JWTAuth] to use the given&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// mount path for the Vault Authentication Method.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WithMountPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mountPath&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JWTAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;c&quot;&gt;// Remove any trailing slashes from the given mount path&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mountPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TrimRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here is a simple example of how we can use our JWT Auth Method in Go.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;context&quot;&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;errors&quot;&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;os&quot;&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;github.com/hashicorp/vault/api&quot;&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;jwtauth&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;vault-jwt-auth-method/pkg/auth/jwt&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;printErr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

	&lt;span class=&quot;c&quot;&gt;// Create a JWT Authentication Method using the token from the following&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// role and token stored in the path&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;authMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jwtauth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;s&quot;&gt;&quot;my-role-name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;jwtauth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WithTokenFromPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/path/to/my/token&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;printErr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;c&quot;&gt;// Create Vault client and authenticate using our JWT Authentication&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// Method implementation&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vault&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;printErr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;c&quot;&gt;// Login using our Auth Method&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;authSecret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Auth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;printErr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authSecret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;printErr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;no auth info returned after login&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;c&quot;&gt;// Read a sample KV v2 secret&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;kvv2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KVv2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kvv2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kvv2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;my/test/secret&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;printErr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Additional improvement to the example code above would be to renew the auth
secret using a
&lt;a href=&quot;https://pkg.go.dev/github.com/hashicorp/vault/api#LifetimeWatcher&quot;&gt;vault.LifetimeWatcher&lt;/a&gt;,
so that our API client is kept authenticated, especially if we are working on a
service, which is supposed to be long-running.&lt;/p&gt;

&lt;p&gt;Btw, in case you have missed this one, just want to remind the readers that
Hashicorp Vault got forked a couple of years ago as a response to Hashicorp
relicensing their OSS projects.&lt;/p&gt;

&lt;p&gt;The new OSS friendly fork is now known as &lt;a href=&quot;https://openbao.org/&quot;&gt;OpenBao&lt;/a&gt;. The
code from this post has been adapted and &lt;a href=&quot;https://github.com/openbao/openbao/pull/1526&quot;&gt;submitted to OpenBao as a
feature&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Thu, 03 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://dnaeon.github.io/vault-sdk-jwt-auth-method/</link>
        <guid isPermaLink="true">https://dnaeon.github.io/vault-sdk-jwt-auth-method/</guid>
      </item>
    
      <item>
        <title>Setting up a Kubernetes cluster with Vault and OIDC trust</title>
        <description>&lt;p&gt;In this post we will see how to create and configure a development Kubernetes
cluster with Vault running in it and setup &lt;a href=&quot;https://en.wikipedia.org/wiki/OpenID&quot;&gt;OpenID
Connect&lt;/a&gt; trust between Vault and
Kubernetes workloads.&lt;/p&gt;

&lt;h2 id=&quot;create-the-kubernetes-cluster&quot;&gt;Create the Kubernetes cluster&lt;/h2&gt;

&lt;p&gt;First, create a new Kubernetes cluster using &lt;a href=&quot;https://minikube.sigs.k8s.io/docs/&quot;&gt;minikube&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;env &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KUBECONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;/kubeconfig minikube start &lt;span class=&quot;nt&quot;&gt;--profile&lt;/span&gt; vault-k8s-dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you are using &lt;a href=&quot;https://direnv.net/&quot;&gt;direnv&lt;/a&gt;, you can also create a local
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.envrc&lt;/code&gt; file to point to our cluster and kubeconfig when you enter the
directory where your development cluster &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeconfig&lt;/code&gt; is stored.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KUBECONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;/kubeconfig
minikube profile vault-k8s-dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Enable and reload the config.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;direnv allow
direnv reload
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once the cluster is up and running, verify that it runs with enabled &lt;a href=&quot;https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-issuer-discovery&quot;&gt;Service
account issuer
discovery&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl get &lt;span class=&quot;nt&quot;&gt;--raw&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/.well-known/openid-configuration&apos;&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The command above will print output similar to the one below. The
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/.well-known/openid-configuration&lt;/code&gt; endpoint provides the
&lt;a href=&quot;https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata&quot;&gt;OpenID Connect Provider Metadata&lt;/a&gt;. It includes information about the issuer, where to find the public keys for
verifying JWT tokens, the supported signing algorithms, etc.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;issuer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://kubernetes.default.svc.cluster.local&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;jwks_uri&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://10.0.2.15:8443/openid/v1/jwks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;response_types_supported&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;id_token&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;subject_types_supported&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;public&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id_token_signing_alg_values_supported&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;RS256&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, simply check the options, which are used to start the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube-apiserver&lt;/code&gt; static pod, e.g.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt; kube-system describe pod kube-apiserver-vault-k8s-dev | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;service-account
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Above command should print the following output.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;--service-account-issuer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://kubernetes.default.svc.cluster.local
&lt;span class=&quot;nt&quot;&gt;--service-account-key-file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/lib/minikube/certs/sa.pub
&lt;span class=&quot;nt&quot;&gt;--service-account-signing-key-file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/lib/minikube/certs/sa.key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We will also enable anonymous access to the API endpoints related to OpenID
Connect, since these API endpoints don’t serve any sensitive data and should be
public.&lt;/p&gt;

&lt;p&gt;In order to do that we will bind the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;system:service-account-issuer-discovery&lt;/code&gt;
cluster role to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;system:anonymous&lt;/code&gt; user. This is what the cluster role
represents.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; kubectl describe clusterrole system:service-account-issuer-discovery
Name:         system:service-account-issuer-discovery
Labels:       kubernetes.io/bootstrapping&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;rbac-defaults
Annotations:  rbac.authorization.kubernetes.io/autoupdate: &lt;span class=&quot;nb&quot;&gt;true
&lt;/span&gt;PolicyRule:
  Resources  Non-Resource URLs                     Resource Names  Verbs
  &lt;span class=&quot;nt&quot;&gt;---------&lt;/span&gt;  &lt;span class=&quot;nt&quot;&gt;-----------------&lt;/span&gt;                     &lt;span class=&quot;nt&quot;&gt;--------------&lt;/span&gt;  &lt;span class=&quot;nt&quot;&gt;-----&lt;/span&gt;
             &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;/.well-known/openid-configuration/]  &lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;              &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;get]
             &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;/.well-known/openid-configuration]   &lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;              &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;get]
             &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;/openid/v1/jwks/]                    &lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;              &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;get]
             &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;/openid/v1/jwks]                     &lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;              &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;get]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Create the cluster role binding.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl create clusterrolebinding system:anonymous-service-account-issuer-discovery &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--clusterrole&lt;/span&gt; system:service-account-issuer-discovery &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;system:anonymous
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Start a test pod in the cluster and verify that you can access the OpenID
Discovery endpoint, e.g.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; GET https://kubernetes.default.svc.cluster.local/.well-known/openid-configuration
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;deploy-vault-in-the-kubernetes-cluster&quot;&gt;Deploy Vault in the Kubernetes Cluster&lt;/h2&gt;

&lt;p&gt;Next, we will create the Kubernetes resources, which describe our Vault
configuration, service and statefulset. There are many different ways to get
Vault installed in your Kubernetes, but in order to keep things simple and
straight forward we will use these simple resources.&lt;/p&gt;

&lt;p&gt;This is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigMap&lt;/code&gt; providing the Vault config.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;config.json&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;storage&quot;: {&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;file&quot;: {&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;&quot;path&quot;: &quot;/vault/file&quot;&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;listener&quot;: [&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;&quot;tcp&quot;: {&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;address&quot;: &quot;0.0.0.0:8200&quot;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;tls_disable&quot;: true&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;],&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;default_lease_ttl&quot;: &quot;168h&quot;,&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;max_lease_ttl&quot;: &quot;720h&quot;,&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;ui&quot;: true&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ConfigMap&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;creationTimestamp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault-config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Service&lt;/code&gt; resource.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8200&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ClusterIP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StatefulSet&lt;/code&gt; resource.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;StatefulSet&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;serviceName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;vault&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hashicorp/vault:1.20&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;server&apos;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;securityContext&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;capabilities&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;IPC_LOCK&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;livenessProbe&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;failureThreshold&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;httpGet&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ui/sys/health&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8200&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;readinessProbe&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;failureThreshold&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;httpGet&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ui/sys/health&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8200&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;containerPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8200&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;volumeMounts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault-data&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/vault/file&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault-config&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/vault/config&lt;/span&gt;

      &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault-config&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;configMap&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault-config&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;volumeClaimTemplates&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault-data&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;accessModes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ReadWriteOnce&quot;&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;5Gi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Apply these resources and confirm that Vault is up and running.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; kubectl get pods,services
  NAME          READY   STATUS    RESTARTS   AGE
  pod/vault-0   1/1     Running   0          24m

  NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;S&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;    AGE
  service/kubernetes   ClusterIP   10.96.0.1      &amp;lt;none&amp;gt;        443/TCP    64m
  service/vault        ClusterIP   10.108.56.80   &amp;lt;none&amp;gt;        8200/TCP   48m
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;initialize-and-unseal-vault&quot;&gt;Initialize and unseal Vault&lt;/h2&gt;

&lt;p&gt;Once Vault is up and running we need to initialize it. In order to make things
simpler when interfacing with Vault we will port-forward it’s port to our local
system.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;VAULT_ADDR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;http://localhost:8200/
kubectl port-forward service/vault 8200:8200
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And now, we can initialize it.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vault operator init
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Above command will print the unseal keys, e.g.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Unseal Key 1: 2K/...
Unseal Key 2: xEKFbr...
Unseal Key 3: VFiwfbI43...
Unseal Key 4: 8Je/INW6lQOI...
Unseal Key 5: /1AFwxmvgw7UnFa...

Initial Root Token: hvs...

Vault initialized with 5 key shares and a key threshold of 3. Please securely
distribute the key shares printed above. When the Vault is re-sealed,
restarted, or stopped, you must supply at least 3 of these keys to unseal it
before it can start servicing requests.

Vault does not store the generated root key. Without at least 3 keys to
reconstruct the root key, Vault will remain permanently sealed!

It is possible to generate new unseal keys, provided you have a quorum of
existing unseal keys shares. See &lt;span class=&quot;s2&quot;&gt;&quot;vault operator rekey&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;more information.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, we can unseal the Vault. Repeat the command below at least 3 times in order
to unseal it.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vault operator unseal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We will also mount the KV v2 secrets engine, which we’ll later use.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vault secrets &lt;span class=&quot;nb&quot;&gt;enable&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2 &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; kvv2 kv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;List the enabled secret engines and we should see our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kvv2&lt;/code&gt; mount.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; vault secrets list
Path          Type         Accessor              Description
&lt;span class=&quot;nt&quot;&gt;----&lt;/span&gt;          &lt;span class=&quot;nt&quot;&gt;----&lt;/span&gt;         &lt;span class=&quot;nt&quot;&gt;--------&lt;/span&gt;              &lt;span class=&quot;nt&quot;&gt;-----------&lt;/span&gt;
cubbyhole/    cubbyhole    cubbyhole_f86afe98    per-token private secret storage
identity/     identity     identity_fa281c9c     identity store
kvv2/         kv           kv_7e911817           n/a
sys/          system       system_f6068170       system endpoints used &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;control, policy and debugging
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;enable-jwtoidc-authentication-method-in-vault&quot;&gt;Enable JWT/OIDC authentication method in Vault&lt;/h2&gt;

&lt;p&gt;Now, we can continue with enabling the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jwt&lt;/code&gt; authentication method.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vault auth &lt;span class=&quot;nb&quot;&gt;enable &lt;/span&gt;jwt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;List the enabled authentication methods.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; vault auth list
Path      Type     Accessor               Description                Version
&lt;span class=&quot;nt&quot;&gt;----&lt;/span&gt;      &lt;span class=&quot;nt&quot;&gt;----&lt;/span&gt;     &lt;span class=&quot;nt&quot;&gt;--------&lt;/span&gt;               &lt;span class=&quot;nt&quot;&gt;-----------&lt;/span&gt;                &lt;span class=&quot;nt&quot;&gt;-------&lt;/span&gt;
jwt/      jwt      auth_jwt_cbaae747      n/a                        n/a
token/    token    auth_token_468286d4    token based credentials    n/a
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Configure the authentication method for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jwt&lt;/code&gt; by using the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/auth/&amp;lt;auth-method&amp;gt;/config&lt;/code&gt; endpoint.&lt;/p&gt;

&lt;p&gt;You can check the additional parameters, which can be set in the &lt;a href=&quot;https://developer.hashicorp.com/vault/api-docs/auth/jwt&quot;&gt;JWT/OIDC auth
method (API)&lt;/a&gt;
documentation.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vault write auth/jwt/config &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;oidc_discovery_ca_pem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;Kubernetes-API-Server-CA&amp;gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;oidc_client_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;oidc_client_secret&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;default_role&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;k8s-dev&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Kubernetes API Server CA bundle can also be obtained from the
running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vault-0&lt;/code&gt; pod and the location where the service account token is
mounted, which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/run/secrets/kubernetes.io/serviceaccount/ca.crt&lt;/code&gt;. Since
we are running our development Kubernetes cluster in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;minikube&lt;/code&gt; the CA bundle
can also be found in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.minikube/ca.crt&lt;/code&gt; on your local system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If your Kubernetes cluster uses a certificate signed an official Certificate
Authority such as DigiCert, Let’s Encrypt, etc. then you don’t need to specify
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oidc_discovery_ca_pem&lt;/code&gt; parameter, but since this is a development cluster
it comes with self-signed certificate and it’s own CA, which is why we need to
specify it here.&lt;/p&gt;

&lt;p&gt;The previous command can also be executed against the running pod container like
this.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;vault-0 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; vault write auth/jwt/config &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;oidc_discovery_ca_pem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;oidc_client_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;oidc_client_secret&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;default_role&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;k8s-dev&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Verify that the authentication method has been correctly configured.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; vault &lt;span class=&quot;nb&quot;&gt;read &lt;/span&gt;auth/jwt/config
Key                                     Value
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;                                     &lt;span class=&quot;nt&quot;&gt;-----&lt;/span&gt;
bound_issuer                            n/a
default_role                            k8s-dev
jwks_ca_pem                             n/a
jwks_pairs                              &lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;
jwks_url                                n/a
jwt_supported_algs                      &lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;
jwt_validation_pubkeys                  &lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;
namespace_in_state                      &lt;span class=&quot;nb&quot;&gt;true
&lt;/span&gt;oidc_client_id                          n/a
oidc_discovery_ca_pem                   &lt;span class=&quot;nt&quot;&gt;-----BEGIN&lt;/span&gt; CERTIFICATE-----...
oidc_discovery_url                      https://kubernetes.default.svc.cluster.local
oidc_response_mode                      n/a
oidc_response_types                     &lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;
provider_config                         map[]
unsupported_critical_cert_extensions    &lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next thing we need to do is to create the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8s-dev&lt;/code&gt; role, which we’ve specified
in the previous step. This role will be associated with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8s-dev-writer&lt;/code&gt;
policy, allowing Kubernetes workloads read/write access to Vault for a given
path.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vault write auth/jwt/role/k8s-dev &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;bound_subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;system:serviceaccount:default:vault-writer&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;bound_audiences&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;vault-dev&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;user_claim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sub&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;policies&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;k8s-dev-writer &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;role_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;jwt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1h
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bound_subject&lt;/code&gt; parameter from the command above specifies a Kubernetes
service account named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vault-writer&lt;/code&gt; from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt; namespace. Kubernetes
workloads, which will access Vault should be running with this service account,
and also the service account token should be issued with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vault-dev&lt;/code&gt;
audience.&lt;/p&gt;

&lt;p&gt;Finally, we need to create the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8s-dev-writer&lt;/code&gt; policy, which will give our
Kubernetes workloads read/write access to Vault. This is what our simple policy
looks like.&lt;/p&gt;

&lt;div class=&quot;language-hcl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;kvv2/metadata/k8s-dev/*&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;capabilities&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;list&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;kvv2/data/k8s-dev/*&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;capabilities&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;create&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;update&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;patch&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;read&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;delete&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can now apply this policy.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vault policy write k8s-dev-writer /path/to/k8s-dev-writer-policy.hcl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Time to test things out. Let’s create the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vault-writer&lt;/code&gt; Kubernetes service
account.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl create sa vault-writer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can now create a test token for our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vault-writer&lt;/code&gt; service account and test
accessing Vault using this token.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl create token vault-writer &lt;span class=&quot;nt&quot;&gt;--audience&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;vault-dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Save the token somewhere, because we will need it a bit later. This is what the
payload of our JWT token looks like when decoded.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;aud&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;vault-dev&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;exp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1751300275&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;iat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1751296675&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;iss&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://kubernetes.default.svc.cluster.local&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;jti&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;066083b5-1219-4c91-8338-728365776f22&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;kubernetes.io&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;namespace&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;serviceaccount&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;vault-writer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;uid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bc5cffb5-65e6-4921-835c-a0a8cdaa69db&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;nbf&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1751296675&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;sub&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;system:serviceaccount:default:vault-writer&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In order to access Vault using our Kubernetes service account token, first we
need to get a Vault token. This step is similar to what &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc8693&quot;&gt;OAuth 2.0 Token
Exchange&lt;/a&gt; does. We use our
Kubernetes service account token in order to get a Vault token. And this is how
we do it.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;vault&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;auth/jwt/login&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;role=k&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;s-dev&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;jwt=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;k8s-sa-token&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On successful authentication we should see a similar output.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;-----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;hvs...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;token_accessor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;token_duration&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;token_renewable&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;token_policies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;k8s-dev-writer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;identity_policies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;policies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;k8s-dev-writer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;token_meta_role&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;s-dev&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And now we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;token&lt;/code&gt; from the output above in order to access Vault
and write a sample secret.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;VAULT_TOKEN=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;vault-token-goes-here&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;vault&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;kv&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;kvv&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/k&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;s-dev/foo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;bar=baz&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Read back the secret we’ve just created.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;vault&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;kv&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;kvv&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/k&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;s-dev/foo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;====&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Secret&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;====&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;kvv&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/data/k&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;s-dev/foo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;=======&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Metadata&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;=======&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;-----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;created_time&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2025-06-30&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;46.718550078&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;custom_metadata&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;nil&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;deletion_time&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;n/a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;destroyed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;===&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;===&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;-----&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;baz&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since our policy allows read/write access to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kvv2/data/k8s-dev/*&lt;/code&gt; path only
we should not be able to read or write anything else, so let’s try that out.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;vault&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;kv&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;kvv&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/some-secret&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;bar=baz&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;writing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;kvv&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/data/some-secret:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;making&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;API&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;request.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;URL:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;PUT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;http://localhost:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8200&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/v&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/kvv&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/data/some-secret&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Code:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;403&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Errors:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;occurred:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;permission&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;denied&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Looks good. Finally, we can create a test pod which uses our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vault-writer&lt;/code&gt;
service account and access Vault from it. The following example Kubernetes
manifest can be used to start a test pod using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vault-writer&lt;/code&gt; service
account.&lt;/p&gt;

&lt;p&gt;This example uses &lt;a href=&quot;https://kubernetes.io/docs/concepts/storage/projected-volumes/#serviceaccounttoken&quot;&gt;Service Account Token
projection&lt;/a&gt;
in order to inject a token into the running pod, using an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;audience&lt;/code&gt;, which
Vault expects our clients to have as part of their JWT claims.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Pod&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;creationTimestamp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault-client&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault-client&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sleep&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;infinity&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;VAULT_ADDR&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://vault:8200/&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hashicorp/vault:1.20&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault-client&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;volumeMounts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;token-vol&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/service-account&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;readOnly&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;token-vol&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;projected&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;serviceAccountToken&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;audience&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault-dev&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;expirationSeconds&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3600&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;token&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;dnsPolicy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ClusterFirst&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;restartPolicy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Always&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;serviceAccountName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vault-writer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Create the test pod and log into it.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; /path/to/vault-client.yaml
kubectl &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; vault-client &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; /bin/sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once you get a shell in the running pod, simply exchange the Kubernetes service
account token for Vault token, similar to the way it was done in the previous
examples.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vault write auth/jwt/login &lt;span class=&quot;nv&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;k8s-dev &lt;span class=&quot;nv&quot;&gt;jwt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;@/service-account/token
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;references&quot;&gt;References&lt;/h1&gt;

&lt;p&gt;Make sure to check the following documents for additional information on the
topic.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.hashicorp.com/vault/docs/get-started/developer-qs&quot;&gt;Vault: Developer quick start guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.hashicorp.com/vault/docs/auth/jwt&quot;&gt;Vault: Use JWT/OIDC authentication&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.hashicorp.com/vault/api-docs/auth/jwt&quot;&gt;Vault: JWT/OIDC auth method (API)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.hashicorp.com/vault/docs/auth/jwt/oidc-providers&quot;&gt;Vault: OIDC provider list&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.hashicorp.com/vault/tutorials/auth-methods/oidc-auth&quot;&gt;Vault: Secure workflows with OIDC authentication&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.hashicorp.com/vault/tutorials/get-started/introduction-policies&quot;&gt;Vault: Introduction to policies&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.hashicorp.com/vault/docs/auth/jwt/oidc-providers/kubernetes&quot;&gt;Vault: Use Kubernetes for OIDC authentication&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 01 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://dnaeon.github.io/kubernetes-vault-oidc/</link>
        <guid isPermaLink="true">https://dnaeon.github.io/kubernetes-vault-oidc/</guid>
      </item>
    
      <item>
        <title>Making Emacs and consult-imenu better with tree-sitter</title>
        <description>&lt;p&gt;Not long ago I have switched from
&lt;a href=&quot;https://github.com/emacs-lsp/lsp-mode&quot;&gt;lsp-mode&lt;/a&gt; to
&lt;a href=&quot;https://github.com/joaotavora/eglot&quot;&gt;eglot&lt;/a&gt; and
&lt;a href=&quot;https://www.masteringemacs.org/article/how-to-get-started-tree-sitter&quot;&gt;tree-sitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Approximately at the same time I’ve also moved from
&lt;a href=&quot;https://github.com/emacs-helm/helm&quot;&gt;helm&lt;/a&gt; to
&lt;a href=&quot;https://github.com/minad/vertico&quot;&gt;vertico&lt;/a&gt; and the complementary packages for
it such as &lt;a href=&quot;https://github.com/minad/marginalia&quot;&gt;marginalia&lt;/a&gt;,
&lt;a href=&quot;https://github.com/minad/consult&quot;&gt;consult&lt;/a&gt; and
&lt;a href=&quot;https://github.com/oantolin/orderless&quot;&gt;orderless&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So far I’ve been pretty happy with the transition. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eglot&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vertico&lt;/code&gt; feel
snappy and more responsive than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lsp-mode&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;helm&lt;/code&gt;. And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;marginalia&lt;/code&gt; is great
as well, because it provides nice annotations next to your completion
candidates.&lt;/p&gt;

&lt;p&gt;Also, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eglot&lt;/code&gt; is part of Emacs for quite some time already, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vertico&lt;/code&gt; (and
it’s complementary packages) build on top of the standard Emacs APIs such as
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;completing-read&lt;/code&gt;, which is another good reason to stick with them.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/minad/consult&quot;&gt;consult&lt;/a&gt; provides search and navigation
commands, and also comes with support for (amongst other things)
&lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_node/elisp/Imenu.html&quot;&gt;imenu&lt;/a&gt;
via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;M-x consult-imenu&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;consult&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;marginalia&lt;/code&gt; pair nicely together. Here’s an example of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;M-x
consult-imenu&lt;/code&gt; displaying the symbols for an Elisp buffer, which look great. The
extra annotations next to each completion candidate are provided by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;marginalia&lt;/code&gt;
itself.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/consult-imenu-elisp.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/consult-imenu-elisp.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You get a nice menu with entries grouped by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Types&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Functions&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Variables&lt;/code&gt;,
etc. You can also narrow down the search for entries of specific kind, e.g. when
you want to browse the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;functions&lt;/code&gt; only, simply type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f SPC&lt;/code&gt; in the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;consult-imenu&lt;/code&gt; prompt and it will give you the function definitions only.&lt;/p&gt;

&lt;p&gt;At &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$WORK&lt;/code&gt; I’m primary working on Go code these days, and one thing I’ve noticed
when using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;M-x consult-imenu&lt;/code&gt; is that the results are less impressive when
compared to the Elisp code.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/consult-imenu-eglot-imenu.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/consult-imenu-eglot-imenu.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see the results are nowhere near to what &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;M-x consult-imenu&lt;/code&gt;
(enriched by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;marginalia&lt;/code&gt;) displays for Elisp code.&lt;/p&gt;

&lt;p&gt;In this post we will see how to make &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;M-x consult-imenu&lt;/code&gt; look better when
working with Go code (or any other language with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tree-sitter&lt;/code&gt; support).&lt;/p&gt;

&lt;p&gt;After some time spent on reading documentation and manuals I was not able to fix
it myself, so I made a &lt;a href=&quot;https://www.reddit.com/r/emacs/comments/1ld119l/imenu_with_gomode/&quot;&gt;post at
r/emacs&lt;/a&gt;
asking whether someone else was having the same issues as I do. In the meantime
I kept digging.&lt;/p&gt;

&lt;p&gt;So, what turned out to be the issue for me, is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eglot&lt;/code&gt; provides it’s own
&lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_node/elisp/Imenu.html#index-imenu_002dcreate_002dindex_002dfunction&quot;&gt;imenu-create-index-function&lt;/a&gt;
called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eglot-imenu&lt;/code&gt;, which actually wraps &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;treesit-simple-imenu&lt;/code&gt;, and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;treesit-simple-imenu&lt;/code&gt; is tree-sitter’s approach to building the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imenu&lt;/code&gt; index.&lt;/p&gt;

&lt;p&gt;Here’s what the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imenu-create-index-function&lt;/code&gt; looks like, when set by
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eglot&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C-h v imenu-create-index-function&lt;/code&gt; (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;M-x describe-variable imenu-create-index-function&lt;/code&gt;).&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;imenu-create-index-function&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;defined&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;‘&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;imenu.el&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;Its&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;advice&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;eglot-imenu&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:before-until&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;treesit-simple-imenu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;Local&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;main.go&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; global value is&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;imenu-default-create-index-function&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;The&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;creating&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;an&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;alist&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer.&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;It&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;be&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;takes&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;no&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;arguments&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;returns&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;an&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;alist&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer.&lt;/span&gt;  &lt;span class=&quot;nv&quot;&gt;The&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;called&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;within&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;‘&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-excursion&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;See&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;‘&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;imenu--index-alist&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;alist.&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;Automatically&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;becomes&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-local&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;set.&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;This&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;may&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;be&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;risky&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;used&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;file-local&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;variable.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My current tree-sitter configuration looks like this. Make sure to check &lt;a href=&quot;https://www.masteringemacs.org/article/how-to-get-started-tree-sitter&quot;&gt;How to
Get Started with
Tree-Sitter&lt;/a&gt;
if you are just starting out with tree-sitter.&lt;/p&gt;

&lt;p&gt;After configuring tree-sitter you should also install the language grammars
using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;M-x treesit-install-language-grammar&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; tree-sitter settings&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;use-package&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;treesit&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:config&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; Language grammars and sources&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;treesit-language-source-alist&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;bash&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://github.com/tree-sitter/tree-sitter-bash&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://github.com/tree-sitter/tree-sitter-c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://github.com/tree-sitter/tree-sitter-go&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;go-mod&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://github.com/camdencheek/tree-sitter-go-mod&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://github.com/tree-sitter/tree-sitter-json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;python&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://github.com/tree-sitter/tree-sitter-python&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;;; Remap major modes to treesitter modes&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;major-mode-remap-alist&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;go-mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;go-ts-mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;python-mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;python-ts-mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;js-json-mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;json-ts-mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;json-mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;json-ts-mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sh-mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;bash-ts-mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In order to make &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eglot&lt;/code&gt; stop messing with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imenu&lt;/code&gt; we need to tell it so. My
current (simplified) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eglot&lt;/code&gt; config looks like this.&lt;/p&gt;

&lt;p&gt;Note that we are configuring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eglot-stay-out-of&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:config&lt;/code&gt; section, so
that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eglot&lt;/code&gt; doesn’t interfere with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imenu&lt;/code&gt; index generation.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; eglot settings&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;eglot-format-buffer-before-save&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;before-save-hook&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;eglot-format-buffer&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;-10&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;eglot-organize-imports-before-save&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;before-save-hook&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;call-interactively&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;eglot-code-action-organize-imports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;use-package&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;eglot&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:init&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;company&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;yasnippet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;go-mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:config&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; We want to use treesit-simple-imenu for imenu index creation&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-to-list&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;eglot-stay-out-of&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;imenu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;;; Shutdown LSP server when last project buffer is closed as well&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;eglot-autoshutdown&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;;; Enable eglot for the following modes&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dolist&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;go-mode-hook&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;go-ts-mode-hook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;eglot-ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;;; Buffer-local hooks which install before-save hooks&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;go-ts-mode-hook&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;eglot-format-buffer-before-save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;go-ts-mode-hook&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;eglot-organize-imports-before-save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; eglot-booster requires that `emacs-lsp-booster&apos; is installed [1]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; [1]: https://github.com/blahgeek/emacs-lsp-booster&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;use-package&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;eglot-booster&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;eglot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:config&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;eglot-booster-mode&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And my (simplified) settings for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go-ts-mode&lt;/code&gt; look like this.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; Go specific settings&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; Help out the builtin project find the Go module path&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;project-find-go-module&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;when-let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;locate-dominating-file&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dir&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;go.mod&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cons&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;go-module&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cl-defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;project-root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;go-module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cdr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;project-find-functions&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;project-find-go-module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;use-package&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;go-ts-mode&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:config&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;go-ts-mode-hook&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;company-mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eglot-imenu&lt;/code&gt; we will configure a hook for our
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go-ts-mode&lt;/code&gt; which sets &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imenu-index-create-function&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;treesit-simple-imenu&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;go-ts-mode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;setq-local&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;imenu-index-create-function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-simple-imenu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Later we will move this hook to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;use-package&lt;/code&gt; definition for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go-ts-mode&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, we need to tell &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;treesit-simple-imenu&lt;/code&gt; how to generate the imenu
index. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;treesit-simple-imenu&lt;/code&gt; relies on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;treesit-simple-imenu-settings&lt;/code&gt; variable
when generating the imenu index.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;treesit-simple-imenu-settings&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;defined&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;‘&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit.el&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;Its&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;Settings&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;‘&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-simple-imenu&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;It&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;be&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CATEGORY&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;REGEXP&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PRED&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;NAME-FN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;CATEGORY&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;category,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;like&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Function&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Class&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;etc.&lt;/span&gt;  &lt;span class=&quot;nv&quot;&gt;REGEXP&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;be&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;regexp&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;matching&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;nodes&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;that&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;belong&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;CATEGORY.&lt;/span&gt;  &lt;span class=&quot;nv&quot;&gt;PRED&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;be&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;either&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;takes&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;an&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;argument.&lt;/span&gt;  &lt;span class=&quot;nv&quot;&gt;It&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;non-nil&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;valid&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;CATEGORY,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;not.&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;CATEGORY&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;could&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;also&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;be&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;nil.&lt;/span&gt;  &lt;span class=&quot;nv&quot;&gt;In&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;entries&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;matched&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;by&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;REGEXP&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PRED&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;are&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;grouped&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;under&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;CATEGORY.&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;NAME-FN&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;be&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;either&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;takes&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;returns&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node.&lt;/span&gt;  &lt;span class=&quot;nv&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;NAME-FN&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;‘&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-defun-name&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;used.&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;‘&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-major-mode-setup&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;automatically&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;sets&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Imenu&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;this&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;non-nil.&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;Probably&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;introduced&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;before&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Emacs&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;30.1.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By default the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go-ts-mode.el&lt;/code&gt; defines &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;treesit-simple-imenu-settings&lt;/code&gt; like
this.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;c1&quot;&gt;;; Imenu.&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;setq-local&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;treesit-simple-imenu-settings&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Function&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`function_declaration\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Method&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`method_declaration\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Struct&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`type_declaration\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;go-ts-mode--struct-node-p&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Interface&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`type_declaration\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;go-ts-mode--interface-node-p&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Type&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`type_declaration\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;go-ts-mode--other-type-node-p&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Alias&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`type_declaration\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;go-ts-mode--alias-node-p&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The docstring of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;treesit-simple-imenu-settings&lt;/code&gt; shown above shows the structure
of the list. The second item in the each list as mentioned there is a regexp
which matches the type of the tree-sitter node.&lt;/p&gt;

&lt;p&gt;When making any changes to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;treesit-simple-imenu-settings&lt;/code&gt; it is best that you
review the AST tree as you go by using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;M-x treesit-explore-mode&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is what the AST looks like for our sample Go program.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/treesit-explore-mode.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/treesit-explore-mode.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, after spending some time configuring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;treesit-simple-imenu-settings&lt;/code&gt; with
the help of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;M-x treesit-explore-mode&lt;/code&gt; here’s what my current config looks like.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; Treesitter helpers to extract node names for various symbols such as consts,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; vars, imports, etc.&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my/go-ts-var-is-global-p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Predicate which tests whether a `var_spec&apos; node belongs to the global scope&quot;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; The parent node for `var_spec&apos; which is `var_declaration&apos; belongs to the&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; global scope, if the `var_declaration&apos; node belongs to a parent node of&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; type `source_file&apos;.&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;var-declaration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-parent-until&lt;/span&gt;
                           &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;
                           &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                             &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;string-equal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-type&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;var_declaration&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                           &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;var-declaration-parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-parent&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;var-declaration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;string-equal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-type&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;var-declaration-parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;source_file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my/go-ts-const-is-global-p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Predicate which tests whether a `const_spec&apos; node belongs to the global scope&quot;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; The parent node for `const_spec&apos; which is `const_declaration&apos; belongs to the&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; global scope, if the `const_declaration&apos; node belongs to a parent node of&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; type `source_file&apos;.&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;const-declaration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-parent-until&lt;/span&gt;
                             &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;
                             &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;string-equal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-type&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;const_declaration&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                             &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;const-declaration-parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-parent&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const-declaration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;string-equal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-type&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const-declaration-parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;source_file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my/go-ts-const-spec-node-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Returns the name of a `const_spec&apos; node&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;const-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-child-by-field-name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-text&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my/go-ts-var-spec-node-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Returns the name of a `var_spec&apos; node&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;var-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-child-by-field-name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-text&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;var-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my/go-ts-import-node-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Returns the name of a `import_spec&apos; node&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;import-path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-child-by-field-name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;import-alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-child-by-field-name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;package-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-search-subtree&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;import-path&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;interpreted_string_literal_content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cond&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;import-alias&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%s (%s)&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-text&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;package-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-text&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;import-alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-node-text&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;package-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my/treesit-simple-imenu-settings&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;setq-local&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;imenu-max-item-length&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;setq-local&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;imenu-index-create-function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;treesit-simple-imenu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;setq-local&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;treesit-simple-imenu-settings&lt;/span&gt;
              &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Imports&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`import_spec\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my/go-ts-import-node-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Functions&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`function_declaration\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Constants&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`const_spec\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my/go-ts-const-is-global-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my/go-ts-const-spec-node-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Variables&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`var_spec\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my/go-ts-var-is-global-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my/go-ts-var-spec-node-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Interfaces&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`type_declaration\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;go-ts-mode--interface-node-p&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Types&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`type_declaration\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;go-ts-mode--other-type-node-p&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Aliases&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`type_declaration\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;go-ts-mode--alias-node-p&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Structs&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`type_declaration\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;go-ts-mode--struct-node-p&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Methods&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\`method_declaration\\&apos;&quot;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;setq-local&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;consult-imenu-config&lt;/span&gt;
              &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;go-ts-mode&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:toplevel&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Imports&quot;&lt;/span&gt;
                            &lt;span class=&quot;ss&quot;&gt;:types&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?I&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Imports&quot;&lt;/span&gt;    &lt;span class=&quot;nv&quot;&gt;font-lock-constant-face&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?f&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Functions&quot;&lt;/span&gt;  &lt;span class=&quot;nv&quot;&gt;font-lock-function-name-face&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?c&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Constants&quot;&lt;/span&gt;  &lt;span class=&quot;nv&quot;&gt;font-lock-constant-face&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?v&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Variables&quot;&lt;/span&gt;  &lt;span class=&quot;nv&quot;&gt;font-lock-variable-name-face&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?i&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Interfaces&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;font-lock-type-face&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?t&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Types&quot;&lt;/span&gt;      &lt;span class=&quot;nv&quot;&gt;font-lock-type-face&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?a&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Aliases&quot;&lt;/span&gt;    &lt;span class=&quot;nv&quot;&gt;font-lock-type-face&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?s&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Structs&quot;&lt;/span&gt;    &lt;span class=&quot;nv&quot;&gt;font-lock-type-face&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?m&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Methods&quot;&lt;/span&gt;    &lt;span class=&quot;nv&quot;&gt;font-lock-function-name-face&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What is left now is to add a hook for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go-ts-mode&lt;/code&gt;, which configures everything for us.&lt;/p&gt;

&lt;p&gt;My updated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go-ts-mode&lt;/code&gt; config looks like this.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;use-package&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;go-ts-mode&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:config&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;go-ts-mode-hook&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my/treesit-simple-imenu-settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;go-ts-mode-hook&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;company-mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s how it looks like with the sample Go program shown before.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/consult-imenu-treesit.png&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/consult-imenu-treesit.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Things look much better now. We have various categories such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Imports&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Functions&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Variables&lt;/code&gt;, etc. And the index generation happens much faster now,
because it relies on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tree-sitter&lt;/code&gt; only.&lt;/p&gt;
</description>
        <pubDate>Tue, 17 Jun 2025 00:00:00 +0000</pubDate>
        <link>https://dnaeon.github.io/eglot-treesitter-imenu/</link>
        <guid isPermaLink="true">https://dnaeon.github.io/eglot-treesitter-imenu/</guid>
      </item>
    
      <item>
        <title>makefile-graph with Apache Echarts support</title>
        <description>&lt;p&gt;In a &lt;a href=&quot;https://dnaeon.github.io/makefile-graph/&quot;&gt;previous post&lt;/a&gt; I’ve talked about
&lt;a href=&quot;https://github.com/dnaeon/makefile&quot;&gt;makefile-graph&lt;/a&gt; - a tool which lets you
visualize your Makefile targets so you can better understand how targets relate
to others.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;makefile-graph&lt;/code&gt; tool initially supported two output formats - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dot&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tsort&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tsort&lt;/code&gt; format prints the topological order after performing a
&lt;a href=&quot;https://en.wikipedia.org/wiki/Topological_sorting&quot;&gt;topological sort&lt;/a&gt; of the
graph, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dot&lt;/code&gt; format outputs a representation of the graph in the &lt;a href=&quot;https://graphviz.org/doc/info/lang.html&quot;&gt;Dot
language&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Starting with version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.1.4&lt;/code&gt; of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;makefile-graph&lt;/code&gt; we can now render a graph of
the Makefile targets using &lt;a href=&quot;https://echarts.apache.org/en/index.html&quot;&gt;Apache
Echarts&lt;/a&gt;, which provides a better
interactive experience when inspecting your Makefile targets.&lt;/p&gt;

&lt;p&gt;In order to install the latest version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;makefile-graph&lt;/code&gt; execute the following
command.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;go &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;github.com/dnaeon/makefile-graph/cmd/makefile-graph@latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In order to render a graph using Apache Echarts we need to specify the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--format
echarts&lt;/code&gt; option, e.g.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;makefile-graph &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--makefile&lt;/span&gt; examples/Makefile &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--direction&lt;/span&gt; LR &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--format&lt;/span&gt; echarts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The command above will generate an HTML document, which you can save and open.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/makefile-graph-echarts-white.gif&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/makefile-graph-echarts-white.gif&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to use a different theme (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dark&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vintage&lt;/code&gt;, etc.) you can
specify the Echarts theme using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--theme&lt;/code&gt; option. The font size and style
can be controlled via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--font-size&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--font-style&lt;/code&gt; options
respectively. For example this command will render the graph using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dark&lt;/code&gt;
theme of Echarts.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;makefile-graph &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--makefile&lt;/span&gt; examples/Makefile &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--direction&lt;/span&gt; LR &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--theme&lt;/span&gt; dark &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--format&lt;/span&gt; echarts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/makefile-graph-echarts-dark.gif&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/makefile-graph-echarts-dark.gif&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For additional information and examples, please refer to the
&lt;a href=&quot;https://github.com/dnaeon/makefile-graph&quot;&gt;dnaeon/makefile-graph&lt;/a&gt; repo.&lt;/p&gt;
</description>
        <pubDate>Sat, 24 May 2025 00:00:00 +0000</pubDate>
        <link>https://dnaeon.github.io/makefile-graph-echarts/</link>
        <guid isPermaLink="true">https://dnaeon.github.io/makefile-graph-echarts/</guid>
      </item>
    
      <item>
        <title>Tracking Kubernetes resource origin with kustomize</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://kustomize.io/&quot;&gt;kustomize&lt;/a&gt; is a configuration management tool, which
allows you to manage Kubernetes resources in a template-free way.&lt;/p&gt;

&lt;p&gt;With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kustomize&lt;/code&gt; you can define a base set of resources describing the desired
state of an application, and then customize the configuration for a specific
environment by leveraging
&lt;a href=&quot;https://kubectl.docs.kubernetes.io/guides/introduction/kustomize/#2-create-variants-using-overlays&quot;&gt;overlays&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kustomize&lt;/code&gt; to &lt;a href=&quot;https://kubectl.docs.kubernetes.io/guides/config_management/secrets_configmaps/&quot;&gt;generate secrets and
configmaps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The template-free approach of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kustomize&lt;/code&gt; is something which I like, because it
keeps the whole configuration more explicit, and does not introduce complexity
via some magic template expansion. Another nice thing about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kustomize&lt;/code&gt; is that
it is already integrated into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In order to get familiar with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kustomize&lt;/code&gt; and the concepts it uses, please make
sure to check the &lt;a href=&quot;https://kubectl.docs.kubernetes.io/guides/introduction/kustomize/&quot;&gt;Introduction to
Kustomize&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this post we are going to see how we can track the origin of Kubernetes
resources produced by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kustomize&lt;/code&gt;, and then visualize them.&lt;/p&gt;

&lt;p&gt;Let’s create a sample kustomization based on the &lt;a href=&quot;https://github.com/kubernetes-sigs/kustomize/tree/master/examples/helloWorld&quot;&gt;kustomize
helloWorld&lt;/a&gt;
example.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kustomize.config.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Kustomization&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello-world&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://github.com/kubernetes-sigs/kustomize//examples/helloWorld/?timeout=120&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, let’s build it.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kustomize build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The output from the command above is this.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;altGreeting&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Good Morning!&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;enableRisky&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;false&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ConfigMap&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;the-map&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;the-service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8666&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TCP&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;targetPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8080&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;deployment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;LoadBalancer&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;the-deployment&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;deployment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;deployment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/hello&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--port=8080&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--enableRiskyFeature=$(ENABLE_RISKY)&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ALT_GREETING&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;valueFrom&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;configMapKeyRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;altGreeting&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;the-map&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ENABLE_RISKY&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;valueFrom&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;configMapKeyRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;enableRisky&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;the-map&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;monopole/hello:1&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;the-container&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;containerPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This fairly simple example shows how to create a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kustomization.yaml&lt;/code&gt;, which
is based on a remote repo. The result of our kustomization is just three
resources - a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deployment&lt;/code&gt;, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Service&lt;/code&gt; and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigMap&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Where things may become complicated is when your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kustomization.yaml&lt;/code&gt; builds on
top of multiple bases, each of which provides a lot of resources.&lt;/p&gt;

&lt;p&gt;In these situations understanding where a given resource comes from becomes a
challenge. Fortunately, in kustomize we can enable
&lt;a href=&quot;https://github.com/kubernetes-sigs/kustomize/blob/master/api/types/kustomization.go#L17-L30&quot;&gt;originAnnotations&lt;/a&gt;,
which helps in this regard.&lt;/p&gt;

&lt;p&gt;When we enable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;originAnnotations&lt;/code&gt; in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kustomization.yaml&lt;/code&gt; kustomize will
add annotations to each resource it produces, which describes where the
resources originate from.&lt;/p&gt;

&lt;p&gt;Let’s try it out.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kustomize.config.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Kustomization&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello-world&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;buildMetadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;originAnnotations&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://github.com/kubernetes-sigs/kustomize//examples/helloWorld/?timeout=120&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time when we &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kustomize build&lt;/code&gt; we get this output instead. Notice the newly
added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.kubernetes.io/origin&lt;/code&gt; annotation by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kustomize&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;altGreeting&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Good Morning!&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;enableRisky&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;false&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ConfigMap&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;config.kubernetes.io/origin&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;path: examples/helloWorld/configMap.yaml&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;repo: https://github.com/kubernetes-sigs/kustomize&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;the-map&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;config.kubernetes.io/origin&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;path: examples/helloWorld/service.yaml&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;repo: https://github.com/kubernetes-sigs/kustomize&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;the-service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8666&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TCP&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;targetPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8080&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;deployment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;LoadBalancer&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;config.kubernetes.io/origin&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;path: examples/helloWorld/deployment.yaml&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;repo: https://github.com/kubernetes-sigs/kustomize&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;the-deployment&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;deployment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;deployment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hello&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/hello&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--port=8080&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--enableRiskyFeature=$(ENABLE_RISKY)&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ALT_GREETING&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;valueFrom&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;configMapKeyRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;altGreeting&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;the-map&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ENABLE_RISKY&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;valueFrom&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;configMapKeyRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;enableRisky&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;the-map&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;monopole/hello:1&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;the-container&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;containerPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, this is much better as we can clearly see where a given resource came from.
However, when having lots of resources it’s still a challenge to visually parse
these annotations, but we can do better than that.&lt;/p&gt;

&lt;p&gt;Some time ago I’ve worked on creating a tool which parses the origin annotations
produced by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kustomize&lt;/code&gt; and renders a graph of the resources and their origins.&lt;/p&gt;

&lt;p&gt;You can find the code at the
&lt;a href=&quot;https://github.com/dnaeon/kustomize-dot&quot;&gt;dnaeon/kustomize-dot&lt;/a&gt; repo.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/kustomize-dot-1.svg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/kustomize-dot-1.svg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/dnaeon/kustomize-dot&quot;&gt;dnaeon/kustomize-dot&lt;/a&gt; can operate in
two modes - as a standalone CLI application, or as a &lt;a href=&quot;https://kubectl.docs.kubernetes.io/guides/extending_kustomize/containerized_krm_functions/&quot;&gt;KRM Function
plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In order to generate a graph of the Kubernetes resources and their origin when
building a kustomization target we need to enable the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;originAnnotations&lt;/code&gt; build
option in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kustomization.yaml&lt;/code&gt; file, just like we did for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;helloWorld&lt;/code&gt;
example.&lt;/p&gt;

&lt;p&gt;The tool is flexible and supports filtering of resources, highlighting of
resources or whole namespaces, setting graph layout direction, etc. This is
useful when we want to get a more focused view of the resulting graph.&lt;/p&gt;

&lt;p&gt;This here is the representation of all the resources for &lt;a href=&quot;https://github.com/prometheus-operator/kube-prometheus&quot;&gt;kube-prometheus
operator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/kustomize-dot-kube-prometheus-full.svg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/kustomize-dot-kube-prometheus-full.svg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The resulting graph is big, and could be confusing as well. In order to focus on
specific resources (or namespaces) we can ask &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kustomize-dot&lt;/code&gt; to filter out the
things we do (or don’t) need.&lt;/p&gt;

&lt;p&gt;This command here for example will keep only resources from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube-system&lt;/code&gt; namespaces.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kustomize-dot generate &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; kube-prometheus.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--keep-namespace&lt;/span&gt; default &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--keep-namespace&lt;/span&gt; kube-system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube-prometheus.yaml&lt;/code&gt; file can be found in
&lt;a href=&quot;https://github.com/dnaeon/kustomize-dot/blob/v1/pkg/fixtures/kube-prometheus.yaml&quot;&gt;pkg/fixtures/kube-prometheus.yaml&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is what the result looks like.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/kustomize-dot-kube-prometheus-1.svg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/kustomize-dot-kube-prometheus-1.svg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also apply some highlighting, if needed.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kustomize-dot generate &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; kube-prometheus.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--keep-namespace&lt;/span&gt; default &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--keep-namespace&lt;/span&gt; kube-system &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--highlight-namespace&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;pink &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--highlight-namespace&lt;/span&gt; kube-system&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;yellow
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/kustomize-dot-kube-prometheus-2.svg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/kustomize-dot-kube-prometheus-2.svg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here’s another example, which drops all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigMap&lt;/code&gt; resources, and keeps
everything else.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kustomize-dot generate &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; kube-prometheus.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--keep-namespace&lt;/span&gt; monitoring &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--drop-kind&lt;/span&gt; ConfigMap &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--highlight-kind&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;yellow &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--highlight-kind&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;servicemonitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;orange &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--highlight-kind&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;serviceaccount&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;lightgray &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--highlight-kind&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;deployment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;magenta &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--highlight-kind&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;prometheusrule&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;lightgreen &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--highlight-kind&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;networkpolicy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;cyan
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The result looks like this.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/kustomize-dot-kube-prometheus-3.svg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/kustomize-dot-kube-prometheus-3.svg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For additional information and examples, please refer to the
&lt;a href=&quot;https://github.com/dnaeon/kustomize-dot&quot;&gt;dnaeon/kustomize-dot&lt;/a&gt; repo.&lt;/p&gt;
</description>
        <pubDate>Fri, 15 Nov 2024 00:00:00 +0000</pubDate>
        <link>https://dnaeon.github.io/kustomize-dot/</link>
        <guid isPermaLink="true">https://dnaeon.github.io/kustomize-dot/</guid>
      </item>
    
      <item>
        <title>Generating graphs from your Makefiles</title>
        <description>&lt;p&gt;Anyone who has done any kind of programming at some point comes across
&lt;a href=&quot;https://www.gnu.org/software/make/&quot;&gt;GNU Make&lt;/a&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Makefile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So what is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make(1)&lt;/code&gt; and what are these &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Makefile&lt;/code&gt;s?&lt;/p&gt;

&lt;p&gt;Instead of trying to explain this I’ll simply quote here the official &lt;a href=&quot;https://www.gnu.org/software/make/&quot;&gt;GNU Make
documentation&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;GNU Make is a tool which controls the generation of executables and other
non-source files of a program from the program’s source files.&lt;/p&gt;

  &lt;p&gt;Make gets its knowledge of how to build your program from a file called the
makefile, which lists each of the non-source files and how to compute it from
other files. When you write a program, you should write a makefile for it, so
that it is possible to use Make to build and install the program.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you haven’t heard or used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make(1)&lt;/code&gt; before, I’d recommend that you go and
check the &lt;a href=&quot;https://www.gnu.org/software/make/&quot;&gt;GNU Make documentation&lt;/a&gt; for
introduction and a tutorial on creating your first Makefile.&lt;/p&gt;

&lt;p&gt;In the rest of this post I assume that you have already used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make(1)&lt;/code&gt; and know
what it does.&lt;/p&gt;

&lt;p&gt;Okay, with that out of the way it’s time to focus on the topic of this post.
Working with Makefiles can sometimes be challenging, especially if your project
is large enough to contain lots of
&lt;a href=&quot;https://www.gnu.org/software/make/manual/make.html#Makefile-Contents&quot;&gt;targets&lt;/a&gt;
and lots of dependencies between each of them.&lt;/p&gt;

&lt;p&gt;Navigating through these targets and their dependencies may not be an easy task.
For example &lt;a href=&quot;https://www.gnu.org/software/make/manual/html_node/Include.html&quot;&gt;including other
Makefiles&lt;/a&gt;
while it helps keeping things logically separated introduces additional
complexity when a person needs to understand the additional targets and
additional dependencies from the included Makefile.&lt;/p&gt;

&lt;p&gt;Okay, so what can we do about that? How to navigate through large Makefiles and get
a better understanding of all the targets and their dependencies?&lt;/p&gt;

&lt;p&gt;Internally, GNU Make (and other make implementations) are building a &lt;a href=&quot;https://en.wikipedia.org/wiki/Dependency_graph&quot;&gt;dependency
graph&lt;/a&gt;, where the targets
represent the &lt;em&gt;vertices&lt;/em&gt; and the &lt;em&gt;edges&lt;/em&gt; which connect them are represented by the
target
&lt;a href=&quot;https://www.gnu.org/software/make/manual/make.html#Prerequisite-Types&quot;&gt;prerequisite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Knowing this we can actually build a graph from our Makefiles and visualize
them, which will help us navigate through the jungle of targets and their
dependencies.&lt;/p&gt;

&lt;p&gt;Okay, first things first. How do we get the graph representation which &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make(1)&lt;/code&gt;
is using? This one is easy with GNU Make.&lt;/p&gt;

&lt;p&gt;All you have to do in order to dump make’s internal database is call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make(1)&lt;/code&gt; with these flags.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;make &lt;span class=&quot;nt&quot;&gt;--print-data-base&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;--no-builtin-rules&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;--no-builtin-variables&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;--dry-run&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;--always-make&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;--question&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Calling this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make(1)&lt;/code&gt; command inside a directory with a Makefile will dump the
internal database of GNU Make.&lt;/p&gt;

&lt;p&gt;Here’s a sample output from one of my Makefiles.&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# GNU Make 4.4.1
# Built for x86_64-pc-linux-gnu
# Copyright (C) 1988-2023 Free Software Foundation, Inc.
# License GPLv3+: GNU GPL version 3 or later &amp;lt;https://gnu.org/licenses/gpl.html&amp;gt;
# This is free software: you are free to change and redistribute it.
# There is NO WARRANTY, to the extent permitted by law.

# Make data base, printed on Sun Apr 28 16:38:33 2024

# Variables

# environment
TMUX_PANE = %0
# environment
GO111MODULE = on
# environment
QT_WAYLAND_RECONNECT = 1
# variable set hash-table stats:
# Load=109/1024=11%, Rehash=0, Collisions=6/141=4%

# Pattern-specific Variable Values

# No pattern-specific variable values.

# Directories

# . (device 65041, inode 3154786): 17 files, no impossibilities.

# 17 files, no impossibilities in 1 directories.

# Implicit Rules

# No implicit rules.

# Files

test_cover:
#  Phony target (prerequisite of .PHONY).
#  Implicit rule search has not been done.
#  File does not exist.
#  File has not been updated.
#  recipe to execute (from &apos;Makefile&apos;, line 8):
	go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...

# Not a target:
Makefile:
#  Implicit rule search has been done.
#  File is secondary (prerequisite of .SECONDARY).
#  Last modified 2022-08-19 12:15:43.700764449
#  File has been updated.
#  Successfully updated.

# Not a target:
.DEFAULT:
#  Implicit rule search has not been done.
#  Modification time never checked.
#  File has not been updated.

test:
#  Phony target (prerequisite of .PHONY).
#  Implicit rule search has not been done.
#  File does not exist.
#  File has not been updated.
#  recipe to execute (from &apos;Makefile&apos;, line 5):
	go test -v -race ./...

get:
#  Phony target (prerequisite of .PHONY).
#  Implicit rule search has not been done.
#  Implicit/static pattern stem: &apos;&apos;
#  File does not exist.
#  File has been updated.
#  Needs to be updated (-q is set).
# automatic
# @ := get
# automatic
# * :=
# automatic
# &amp;lt; :=
# automatic
# + :=
# automatic
# % :=
# automatic
# ^ :=
# automatic
# ? :=
# automatic
# | :=
# variable set hash-table stats:
# Load=8/32=25%, Rehash=0, Collisions=1/11=9%
#  recipe to execute (from &apos;Makefile&apos;, line 2):
	go get -v -t -d ./...

# Not a target:
.SUFFIXES:
#  Implicit rule search has not been done.
#  Modification time never checked.
#  File has not been updated.

.PHONY: get test test_cover
#  Implicit rule search has not been done.
#  Modification time never checked.
#  File has not been updated.

# files hash-table stats:
# Load=7/1024=1%, Rehash=0, Collisions=0/23=0%
# VPATH Search Paths

# No &apos;vpath&apos; search paths.

# No general (&apos;VPATH&apos; variable) search path.

# strcache buffers: 1 (0) / strings = 26 / storage = 226 B / avg = 8 B
# current buf: size = 8162 B / used = 226 B / count = 26 / avg = 8 B

# strcache performance: lookups = 36 / hit rate = 27%
# hash-table stats:
# Load=26/8192=0%, Rehash=0, Collisions=0/36=0%
# Finished Make data base on Sun Apr 28 16:38:33 2024

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you look closely you will see that this output contains multiple sections:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A header describing the version of the make tool&lt;/li&gt;
  &lt;li&gt;A section containing the environment variables&lt;/li&gt;
  &lt;li&gt;Pattern-specific Variable Values&lt;/li&gt;
  &lt;li&gt;No pattern-specific variable values&lt;/li&gt;
  &lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Within the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Files&lt;/code&gt; section we can also see our targets and this is exactly what
we need and what we are going to be parsing.&lt;/p&gt;

&lt;p&gt;The good news is that I’ve already implemented the parser and you can install it
as a CLI tool, or use it as a Go package in case you want to implement different
graph representations.&lt;/p&gt;

&lt;p&gt;You can find the code in the
&lt;a href=&quot;https://github.com/dnaeon/makefile-graph&quot;&gt;dnaeon/makefile-graph&lt;/a&gt; and here’s how
you can install it.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;go &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;github.com/dnaeon/makefile-graph/cmd/makefile-graph@latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you want to use the parser and do whatever you want to do with the parsed
graph you can install the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parser&lt;/code&gt; package.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;go get &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; github.com/dnaeon/makefile-graph/pkg/parser
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here are some screenshots of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;makefile-graph&lt;/code&gt; in action. The commands below are
using the following Makefile, from which we will be generating the graph
representation in &lt;a href=&quot;https://graphviz.org/doc/info/lang.html&quot;&gt;dot format&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-makefile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nl&quot;&gt;edit &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main.o kbd.o command.o display.o &lt;/span&gt;\
&lt;span class=&quot;nf&quot;&gt;       insert.o search.o files.o utils.o&lt;/span&gt;
	cc &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; edit main.o kbd.o command.o display.o &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
		   insert.o search.o files.o utils.o

&lt;span class=&quot;nl&quot;&gt;main.o &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main.c defs.h&lt;/span&gt;
	cc &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; main.c
&lt;span class=&quot;nl&quot;&gt;kbd.o &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;kbd.c defs.h command.h&lt;/span&gt;
	cc &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; kbd.c
&lt;span class=&quot;nl&quot;&gt;command.o &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;command.c defs.h command.h&lt;/span&gt;
	cc &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; command.c
&lt;span class=&quot;nl&quot;&gt;display.o &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;display.c defs.h buffer.h&lt;/span&gt;
	cc &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; display.c
&lt;span class=&quot;nl&quot;&gt;insert.o &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;insert.c defs.h buffer.h&lt;/span&gt;
	cc &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; insert.c
&lt;span class=&quot;nl&quot;&gt;search.o &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;search.c defs.h buffer.h&lt;/span&gt;
	cc &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; search.c
&lt;span class=&quot;nl&quot;&gt;files.o &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;files.c defs.h buffer.h command.h&lt;/span&gt;
	cc &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; files.c
&lt;span class=&quot;nl&quot;&gt;utils.o &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;utils.c defs.h&lt;/span&gt;
	cc &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; utils.c
&lt;span class=&quot;nl&quot;&gt;clean &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;rm &lt;/span&gt;edit main.o kbd.o command.o display.o &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
	   insert.o search.o files.o utils.o
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s how to render the graph for all targets from our sample Makefile.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;makefile-graph &lt;span class=&quot;nt&quot;&gt;--makefile&lt;/span&gt; examples/Makefile &lt;span class=&quot;nt&quot;&gt;--direction&lt;/span&gt; TB | dot &lt;span class=&quot;nt&quot;&gt;-Tsvg&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; graph.svg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And this is what it looks like.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/makefile-graph-1.svg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/makefile-graph-1.svg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In large enough Makefiles the generated graph may not be easy to understand as
well, and for that reason &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;makefile-graph&lt;/code&gt; supports highlighting specific
targets and their dependencies.&lt;/p&gt;

&lt;p&gt;For example this command will highlight the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;files.o&lt;/code&gt; target and it’s
dependencies.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;makefile-graph &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--makefile&lt;/span&gt; examples/Makefile &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--direction&lt;/span&gt; TB &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--target&lt;/span&gt; files.o &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--highlight&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--highlight-color&lt;/span&gt; lightgreen
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The results look like this.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/makefile-graph-2.svg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/makefile-graph-2.svg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if that is not good enough, we can always focus on a specific target and
it’s dependencies only.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;makefile-graph &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--makefile&lt;/span&gt; examples/Makefile &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--direction&lt;/span&gt; TB &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--target&lt;/span&gt; files.o &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--related-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The results look like this.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/makefile-graph-3.svg&quot; class=&quot;glightbox&quot;&gt;&lt;img src=&quot;/images/makefile-graph-3.svg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For additional information and examples, please refer to the
&lt;a href=&quot;https://github.com/dnaeon/makefile-graph&quot;&gt;dnaeon/makefile-graph&lt;/a&gt; repo.&lt;/p&gt;
</description>
        <pubDate>Sun, 28 Apr 2024 00:00:00 +0000</pubDate>
        <link>https://dnaeon.github.io/makefile-graph/</link>
        <guid isPermaLink="true">https://dnaeon.github.io/makefile-graph/</guid>
      </item>
    
  </channel>
</rss>