<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title>freakingpenguin blog</title><id>https://freakingpenguin.com/feeds/tags/guix.xml</id><subtitle>Tag: guix</subtitle><updated>2024-03-06T00:00:00Z</updated><link href="https://freakingpenguin.com/feeds/tags/guix.xml" rel="self" /><link href="https://freakingpenguin.com" /><entry><title>Guix Tricks: Managing secrets using password-store and run-time
decryption scripts.</title><id>https://freakingpenguin.com/blog/guix-tricks-managing-secrets-using-password-store-and-run-time-decryption-scripts.html</id><author><name>Richard Sent</name><email>richard@freakingpenguin.com</email></author><updated>2024-12-10T12:00:00Z</updated><link href="https://freakingpenguin.com/blog/guix-tricks-managing-secrets-using-password-store-and-run-time-decryption-scripts.html" rel="alternate" /><content type="html">&lt;h1&gt;Intro&lt;/h1&gt;&lt;p&gt;Guix is a package management tool with support for declarative
operation. By declarative, I mean I can write various objects—such as
an operating system configuration—in a text file. Guix will take that
text file and use it to create what I want. For example...&lt;/p&gt;&lt;div class=&quot;code-block&quot;&gt;&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-special&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;%simple-os&lt;/span&gt;
  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;operating-system&lt;/span&gt;
    &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;host-name&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;komputilo&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;timezone&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;America/New_York&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;locale&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;en_US.UTF-8&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;bootloader&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;bootloader-configuration&lt;/span&gt;
                 &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;bootloader&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;grub-bootloader&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
                 &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;targets&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;/dev/sdX&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;file-systems&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;cons&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;file-system&lt;/span&gt;
                          &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;device&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;file-system-label&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;my-root&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
                          &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;mount-point&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
                          &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;ext4&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;syntax-symbol&quot;&gt;%base-file-systems&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;cons&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;user-account&lt;/span&gt;
                  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;alice&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;Bob's sister&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;users&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;supplementary-groups&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;wheel&amp;quot;&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;audio&amp;quot;&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;video&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
                 &lt;span class=&quot;syntax-symbol&quot;&gt;%base-user-accounts&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Generally speaking, anything stored in these files or built from them
should be considered public knowledge. Even if I decide
not to share the declarative files, the outputs from building them are
placed in a location called the store, unencrypted and globally
readable on the hard drive. It would be trivial for an attacker to
capture private information from these files.&lt;/p&gt;&lt;p&gt;This presents a challenge. How can we use private information (i.e.
secrets) in these files without leaking said secrets?&lt;/p&gt;&lt;p&gt;There are three solutions I can think of.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create files containing secrets manually and avoid using Guix. Set
permissions to limit readability.&lt;/li&gt;&lt;li&gt;Use a service to store secrets in the store encrypted, then decrypt
them on boot and place them in the desired location.&lt;/li&gt;&lt;li&gt;Store the secrets encrypted, then run scripts to retrieve them on an
as-needed basis.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;In this post I will be discussing how I implement the 3rd solution.
If 2 sounds interested, check out &lt;a href=&quot;https://github.com/fishinthecalculator/sops-guix&quot;&gt;sops-guix&lt;/a&gt;.&lt;/p&gt;&lt;h1&gt;Runtime Decryption and Retrieval&lt;/h1&gt;&lt;h2&gt;The Not State of the Art Solution&lt;/h2&gt;&lt;p&gt;I store my secrets encrypted in a separate, private Git repository
using &lt;a href=&quot;https://www.passwordstore.org/&quot;&gt;password-store&lt;/a&gt;. For this strategy to work, the service or program being configured
needs to support running a script to retrieve a secret.&lt;/p&gt;&lt;p&gt;Consider my git configuration. I regularly use the &lt;code&gt;$ git send-email&lt;/code&gt; command. While I could manually decrypt my password and paste that
when prompted, I would rather git do that automatically. Fortunately,
git supports running an arbitrary command to retrieve the password.
This is done by setting &lt;code&gt;core.askPass&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;I could construct my git config like this:&lt;/p&gt;&lt;div class=&quot;code-block&quot;&gt;&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;syntax-comment&quot;&gt;;; Git does not actually support arguments for askPass.
&lt;/span&gt;&lt;span class=&quot;syntax-comment&quot;&gt;;; For brevity I am going to pretend it does.
&lt;/span&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-special&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;rsent-git-config-file&lt;/span&gt;
  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;plain-file&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;git-config&amp;quot;&lt;/span&gt;
                   &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;
[core]
        askPass = pass ls Passwords/MyEmailAccount&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;# Contents of .config/git/config
[core]
        askPass = pass ls Passwords/MyEmailAccount&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While it works in this example, this solution is not as general
purpose as it could be. This solution:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Requires an environment where the PATH environment variable is already
set.&lt;/li&gt;&lt;li&gt;Makes it challenging to switch off of password-store in the future.&lt;/li&gt;&lt;li&gt;Creates an unlinked dependency in my code because I have to install
password-store elsewhere.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Some of these issues can be mitigated while still invoking pass
directly but I believe there's a better way.&lt;/p&gt;&lt;h2&gt;A More Guixy Way&lt;/h2&gt;&lt;p&gt;Instead of invoking pass directly in my git config file, I go
another route. In essence, I write a function that takes a &amp;quot;key&amp;quot; for
what I want to retrieve. This function returns a script that, when
invoked, decrypts the secret associated with said key and returns its
value.&lt;/p&gt;&lt;div class=&quot;code-block&quot;&gt;&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-special&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;rsent-get-smtp-password-script&lt;/span&gt;
  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;get-secret-program-file&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;git-getpass&amp;quot;&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;Passwords/MyEmailAccount&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-special&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;rsent-git-config-file&lt;/span&gt;
  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;mixed-text-file&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;git-config&amp;quot;&lt;/span&gt;
                   &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;
[core]
        askPass = &amp;quot;&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;rsent-get-smtp-password-script&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;
&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;# Contents of .config/git/config
[core]
        askPass = /gnu/store/...-git-getpass&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;get-secret-program-file&lt;/code&gt; in turn looks something like this:&lt;/p&gt;&lt;div class=&quot;code-block&quot;&gt;&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-special&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;get-secret-program-file&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;Returns a file-like object capable of retrieving the secret for @var{key}.&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;program-file&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;#~&lt;/span&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;display&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;#$&lt;/span&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;get-secret&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can probably guess, most of the magic happens in
another function called &lt;code&gt;get-secret&lt;/code&gt;. This function is quite a bit &lt;a href=&quot;https://git.sr.ht/~freakingpenguin/rsent/tree/f37511d6e343bb42518ebf36ad382181d6e661d0/item/rsent/utils/secrets.scm#L96&quot;&gt;more complicated&lt;/a&gt; than I want to dive into, but I'll share an abbreviated
version. (Until recently this function was short, but then &lt;a href=&quot;https://git.sr.ht/~freakingpenguin/rsent/commit/3f9f4f3b9c7b44b916e59218659c2fa4cae73cd0&quot;&gt;GnuPG pinentry fun&lt;/a&gt; happened.)&lt;/p&gt;&lt;div class=&quot;code-block&quot;&gt;&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-special&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;get-secret&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;Returns a @code{gexp} that returns the associated secret for @var{key}.
Key should be in the form of a password-store path.&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;syntax-symbol&quot;&gt;#~&lt;/span&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-special&quot;&gt;begin&lt;/span&gt;
      &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;use-modules&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;ice-9&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
                   &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;ice-9&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;popen&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
                   &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;ice-9&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;rdelim&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;current-error-port&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;~!Fetching secret for ~a... &amp;quot;&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;#$key&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-special&quot;&gt;let*&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;open-input-pipe&lt;/span&gt;
                    &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;string-append&lt;/span&gt;
                     &lt;span class=&quot;syntax-symbol&quot;&gt;#$&lt;/span&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;file-append&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;password-store&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;/bin/pass&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
                     &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot; ls &amp;quot;&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;#$key&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
             &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;read-line&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;close-pipe&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;eof-object?&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;Failed to retrieve secret for&amp;quot;&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;#$key&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;current-error-port&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;~!done~%&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;syntax-symbol&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I want to emphasize how flexible this solution is. Any arbitrary
Scheme code can be placed in &lt;code&gt;get-secret&lt;/code&gt;, invoking any combination of external programs. If in the future I
want to switch to another secret management system, I only need to
rework one function and everything that works with secrets will use
the new system.&lt;/p&gt;&lt;h1&gt;Conclusion&lt;/h1&gt;&lt;p&gt;Guix does not have an official integrated method for secrets
management. This leads to users creating ad-hoc solutions. One such
method is creating executable scripts that can decrypt and retrieve
secrets, then embedding those scripts in the appropriate program or
service.&lt;/p&gt;&lt;p&gt;Here are the positives to this approach:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Secrets are always encrypted on disk.&lt;/li&gt;&lt;li&gt;Works for both Guix System and Guix Home.&lt;/li&gt;&lt;li&gt;It is easy to migrate between different methods for storing secrets.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;However, there are limitations:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The services and/or programs being configured need to support
executing programs to retrieve secrets.&lt;/li&gt;&lt;li&gt;User interaction is generally recommended to protect against
unauthorized access.&lt;/li&gt;&lt;li&gt;The creator is responsible for ensuring the script is secure,
especially if it is designed to operate non-interactivity&lt;/li&gt;&lt;li&gt;Debugging requires a thorough understanding of the context the
script is running in. For example, system services have a barer environment than
home services.&lt;/li&gt;&lt;/ol&gt;</content></entry><entry><title>Guix Tricks: Soft-pinning channels in a channels.scm file.</title><id>https://freakingpenguin.com/blog/guix-tricks-soft-pinning-channels-in-a-channelsscm-file.html</id><author><name>Richard Sent</name><email>richard@freakingpenguin.com</email></author><updated>2024-05-26T12:00:00Z</updated><link href="https://freakingpenguin.com/blog/guix-tricks-soft-pinning-channels-in-a-channelsscm-file.html" rel="alternate" /><content type="html">&lt;h1&gt;Problem&lt;/h1&gt;&lt;p&gt;Guix is a very powerful tool to integrate into a project's toolchain.
This isn't just for build systems. It can also apply to tools like
interactive interpreters and debuggers. By creating a project-wide &lt;code&gt;channels.scm&lt;/code&gt; file, you can prepend tool scripts with both &lt;code&gt;guix time-machine&lt;/code&gt; and &lt;code&gt;guix shell&lt;/code&gt; invocations. This allows for easy integration of additional Guix
channels and ensures that every developer has development tools
available to them that aren't reliant on the outside environment.&lt;/p&gt;&lt;p&gt;You can see an example of me using this &lt;a href=&quot;https://git.sr.ht/~freakingpenguin/blog/tree/53b1d18aea0fe7353d9cfdc2a5b8e7f393cf06a3/item/.dir-locals.el&quot;&gt;here&lt;/a&gt; to invoke a Guile REPL with the Guix and rsent-freakingpenguin
channels. This includes a dependency of rsent-freakingpenguin:
guile-blocks. I also use it in the &lt;a href=&quot;https://git.sr.ht/~freakingpenguin/blog/tree/53b1d18aea0fe7353d9cfdc2a5b8e7f393cf06a3/item/bin/serve&quot;&gt;./bin/serve&lt;/a&gt; script to build and locally deploy the website for testing.&lt;/p&gt;&lt;p&gt;However, one challenge of &lt;code&gt;guix time-machine&lt;/code&gt; is that unless you are willing to pin channels to specific commits,
it will ALWAYS try to pull the latest version of that channel. Because
channels are always changing (particularly the Guix channel) this can
introduce significant delays when, say, invoking a Guile REPL after a
new Guix commit was pushed.&lt;/p&gt;&lt;p&gt;As mentioned, one solution is to pin channels to specific version or
branches. However, this has several drawbacks:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The commit needs to be updated regularly to ensure
compatibility with newer tools.&lt;/li&gt;&lt;li&gt;When first run, developers still need to pull an entirely different
commit to what they're running.&lt;/li&gt;&lt;li&gt;It implies that the developer tools require that specific commit to
function even if that is not the case.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;My proposed solution to this is a concept I'll call &amp;quot;soft-pinning&amp;quot;,
which means &amp;quot;try to use whatever commit of this channel that is
currently present on the user's profile&amp;quot;. This will let us mix and
match channels into different categories. One category is kept up to
date at all times while the other is only updated as the user updates
their system.&lt;/p&gt;&lt;h1&gt;Solution&lt;/h1&gt;&lt;p&gt;To implement this, we can take advantage of the fact that &lt;code&gt;channels.scm&lt;/code&gt; is Guile Scheme code and use the &lt;code&gt;current-channels&lt;/code&gt; procedure in the &lt;code&gt;(guix describe)&lt;/code&gt; module. Here's the code:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;syntax-comment&quot;&gt;;; Use this file with time-machine -C to get an up to date copy of
&lt;/span&gt;&lt;span class=&quot;syntax-comment&quot;&gt;;; this channel and any dependencies.
&lt;/span&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;use-modules&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;srfi&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;srfi-1&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
             &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;guix&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;channels&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
             &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;guix&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
             &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;ice-9&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;control&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-special&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;soft-pin-channel&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;input-channel&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;Given @var{channel}, return a new version of channel that uses the
current profile's commit of said channel. If the channel is not present,
commit is #f.&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-special&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;desired-channel?&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;equal?&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;channel-name&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;input-channel&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;channel-name&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-special&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;profile-commit&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;input-channel&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;call/ec&lt;/span&gt;
     &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-special&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-special&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-special&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;desired-channel?&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;channel-commit&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;current-channels&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;syntax-symbol&quot;&gt;#f&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;channel&lt;/span&gt;
   &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;inherit&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;input-channel&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;commit&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;profile-commit&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;input-channel&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;syntax-comment&quot;&gt;;; Note that if you try running this in a container (say, the
&lt;/span&gt;&lt;span class=&quot;syntax-comment&quot;&gt;;; project's interactive Guile), the commit picked up will match the
&lt;/span&gt;&lt;span class=&quot;syntax-comment&quot;&gt;;; Guix package in the container's profile. It will not match the
&lt;/span&gt;&lt;span class=&quot;syntax-comment&quot;&gt;;; user's current profile channel commit.
&lt;/span&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;cons*&lt;/span&gt;
 &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;channel&lt;/span&gt;
  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;'freakingpenguin&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;string-append&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;file://&amp;quot;&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;getcwd&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;list&lt;/span&gt;
  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;soft-pin-channel&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;%default-guix-channel&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, invoking the Guile REPL is instantaneous as long as the
freakingpenguin channel does not change. I no longer have a minute
delay or more every time a new Guix commit is pushed, while still not
having to pin Guix at a specific commit.&lt;/p&gt;</content></entry><entry><title>Guix Tricks: Self-contained shebang scripts.</title><id>https://freakingpenguin.com/blog/guix-tricks-self-contained-shebang-scripts.html</id><author><name>Richard Sent</name><email>richard@freakingpenguin.com</email></author><updated>2024-05-11T12:00:00Z</updated><link href="https://freakingpenguin.com/blog/guix-tricks-self-contained-shebang-scripts.html" rel="alternate" /><content type="html">&lt;h1&gt;Intro&lt;/h1&gt;&lt;p&gt;I like scripts. You like scripts. We all like scripts. But, I don't
like that when I give a script to someone else, they need to install
X, Y, and Z packages for that script to work. I don't like that my
scripts may behave differently depending on the user's environment. I
don't like needing to install a bunch of packages in a CI environment
before running my&lt;code&gt;./bin/*&lt;/code&gt;scripts. If ONLY someone could help me!&lt;/p&gt;&lt;p&gt;Consider the &lt;a href=&quot;https://git.sr.ht/~freakingpenguin/blog/tree/master/item/bin/readme-html&quot;&gt;following script&lt;/a&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;#!/usr/bin/env&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;-S&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;emacs&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;-x&lt;/span&gt;
&lt;span class=&quot;syntax-comment&quot;&gt;;; -*- mode: emacs-lisp; -*-
&lt;/span&gt;&lt;span class=&quot;syntax-comment&quot;&gt;;; https://orgmode.org/manual/Bare-HTML.html
&lt;/span&gt;&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;org-html-head&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;
      &lt;span class=&quot;syntax-symbol&quot;&gt;org-html-head-extra&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;
      &lt;span class=&quot;syntax-symbol&quot;&gt;org-html-head-include-default-style&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;nil&lt;/span&gt;
      &lt;span class=&quot;syntax-symbol&quot;&gt;org-html-head-include-scripts&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;nil&lt;/span&gt;
      &lt;span class=&quot;syntax-symbol&quot;&gt;org-html-postamble&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;nil&lt;/span&gt;
      &lt;span class=&quot;syntax-symbol&quot;&gt;org-html-preamble&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;nil&lt;/span&gt;
      &lt;span class=&quot;syntax-symbol&quot;&gt;org-html-use-infojs&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;nil&lt;/span&gt;
      &lt;span class=&quot;syntax-symbol&quot;&gt;org-html-mathjax-template&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;defvar&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;build-status-image&lt;/span&gt;
  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;#+ATTR_HTML: :alt builds.sr.ht status
[[https://builds.sr.ht/~freakingpenguin/&amp;quot;&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;getenv&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;repo&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;]&amp;quot;&lt;/span&gt;
&lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;[https://builds.sr.ht/~freakingpenguin/&amp;quot;&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;getenv&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;repo&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-string&quot;&gt;&amp;quot;.svg]]
&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;with-current-buffer&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;find-file-noselect&lt;/span&gt; &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;nth&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;insert&lt;/span&gt; &lt;span class=&quot;syntax-symbol&quot;&gt;build-status-image&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;syntax-open&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-symbol&quot;&gt;org-html-export-to-html&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-close&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is a script that takes an org-mode file and generates an HTML
file. A perfect task for a CI job. Ideally, we want this script to
contain all the information required to run it, including the packages
it requires. Keeping this information together in one file makes it
easier to track and simpler for anyone to run the script.&lt;/p&gt;&lt;p&gt;To both install Emacs AND run the script in one go, all we have to do
is replace the shebang with this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;#!/usr/bin/env -S guix shell emacs emacs-htmlize -- emacs -x&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I like this because it lets me avoid needing to learn complicated
tools like Docker for something that should be a simple task. A
concept like &amp;quot;run command X with software Y in a containerized
environment&amp;quot; can be expressed using basic tooling (shebangs) everyone
is already familiar with. I may know how Docker works and how to write
Dockerfiles, but without regular, consistent use it's easy to forget
their syntax and need to relearn it yet again. Shell scripts are
universal. 💕&lt;/p&gt;&lt;h1&gt;Another example&lt;/h1&gt;&lt;p&gt;You can go one step further and run the script in a containerized
environment to ensure that you captured all possible dependencies and
that the behavior is consistent regardless of the user's environment.&lt;/p&gt;&lt;p&gt;For particularly version-sensitive scripts, you can combine this
with guix time-machine. Then, all dependencies required to run the
script can be near-effortlessly frozen and guaranteed to run
identically between invocations.&lt;/p&gt;&lt;p&gt;Using guix shell in shebangs makes running arbitrary projects with
Emacs's&lt;code&gt;compile-command&lt;/code&gt; a breeze, since it boils down the complicated task of environment
setup all the way down to just a running a script. Like, for example,
with how I build this site. In&lt;code&gt;./bin/serve&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;#!/usr/bin/env -S guix shell ruby ruby-colorize -- ruby
# -*- mode: ruby; -*-
require 'optparse'
require 'colorize'
require 'socket'

bind = &amp;quot;localhost&amp;quot;
quiet = &amp;quot;&amp;quot;
no_substitutes = &amp;quot;&amp;quot;
options = {}
OptionParser.new do |opts|
  opts.on(&amp;quot;-p&amp;quot;, &amp;quot;--public&amp;quot;, &amp;quot;Bind to 0.0.0.0 instead of localhost&amp;quot;) do
    bind = &amp;quot;0.0.0.0&amp;quot;
  end

  opts.on(&amp;quot;-q&amp;quot;, &amp;quot;--quiet&amp;quot;, &amp;quot;Minimize output.&amp;quot;) do
    quiet = &amp;quot;-q&amp;quot;
  end

  opts.on(&amp;quot;-s&amp;quot; &amp;quot;--no-substitutes&amp;quot;, &amp;quot;Do not use substitutes&amp;quot;) do
    # Helpful if there are network issues
    no_substitutes = &amp;quot;--no-substitutes&amp;quot;
  end
end.parse!

puts &amp;quot;Building site&amp;quot;.red
puts site_dir = `guix time-machine -C channels.scm -- \
                      build #{no_substitutes} #{quiet} -f guix.scm`
                  .chomp + &amp;quot;/site&amp;quot;
raise &amp;quot;Build Failure&amp;quot; unless $?.success?

ip_address = Socket.ip_address_list.detect{|intf| intf.ipv4_private?}.ip_address.to_s
puts &amp;quot;Serving site publicly on #{ip_address}&amp;quot;.red unless bind == &amp;quot;localhost&amp;quot;
system(&amp;quot;guix time-machine -C channels.scm -- shell #{no_substitutes} \
              -CN --no-cwd --expose=#{site_dir}=/srv/html python --   \
              python3 -m http.server -d /srv/html --bind #{bind}&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now I can add &lt;code&gt;(compile-command .
 &amp;quot;./bin/serve -p&amp;quot;)&lt;/code&gt; to &lt;code&gt;.dir-locals.el&lt;/code&gt;, without
 needing to worry about &lt;code&gt;direnv&lt;/code&gt;, syncing separate scripts and
 Dockerfiles, or anything else.&lt;/p&gt;</content></entry></feed>