<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>rail</title>
      <link>https://flufftech.net</link>
      <description>enby fox on the internet :3</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://flufftech.net/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Fri, 24 Jul 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Configuring Token2 security key for PGP and SSH</title>
          <pubDate>Fri, 24 Jul 2026 00:00:00 +0000</pubDate>
          <author>rail</author>
          <link>https://flufftech.net/posts/configuring-security-key-for-pgp-and-ssh/</link>
          <guid>https://flufftech.net/posts/configuring-security-key-for-pgp-and-ssh/</guid>
          <description xml:base="https://flufftech.net/posts/configuring-security-key-for-pgp-and-ssh/">&lt;div class=&quot;side-note&quot;&gt;
    &lt;strong&gt;Note:&lt;&#x2F;strong&gt;&amp;nbsp;I’m not an expert in cryptography and while I believe this blog post shouldn’t contain any gross factual errors, I do not give you any warranty that described procedure is secure. Please do your own research for real-life security-sensitive situations.
&lt;&#x2F;div&gt;
&lt;p&gt;A while ago I bought myself a pair of Token2 FIDO2 security keys – honestly I can recommend them, they’re great. They offer most of the same functionality as YubiKeys while being less than half the price, it’s really awesome.&lt;&#x2F;p&gt;
&lt;p&gt;For all that time though I’ve been massively underutilizing my keys, only setting them up for hardware-based 2FA and occasional passwordless login on those few services that allow it.&lt;&#x2F;p&gt;
&lt;p&gt;Not a long time ago I set up my own email server and started using new email addresses for different services, which prompted me to reconsider my use of PGP keys. This is where the idea was born to look at the on-device cryptography features of my Token2 keys.&lt;&#x2F;p&gt;
&lt;p&gt;Take a fair warning, this is going to be a long, comprehensive read about a lot of things you can do with PGP on your hardware token ^^&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;the-basics&quot;&gt;The Basics&lt;&#x2F;h2&gt;
&lt;p&gt;Version of the key that I own (Token2 PIN+ Release3.3 USB-C) supports several OpenPGP algorithms including RSA4096 and ed25519, various embedded cryptographic algorithms and ecdsa-sk &amp;amp; ed25519-sk SSH keys. Support may vary depending on exact hardware revision and product type but in general, it should be more or less the same with any modern Token2 key.&lt;&#x2F;p&gt;
&lt;p&gt;In practical terms it basically means that in addition to FIDO2 functionalities, that Token2 key is also a PGP smart-card which is a solid basis for achieving two goals:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Storing my PGP keys on the token itself&lt;&#x2F;strong&gt; so I can always have them with me on the go regardless of what device I use; and&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Using my token for SSH pubkey authentication&lt;&#x2F;strong&gt; for waow ultra secure remote access to very important things. I’ll be honest here, this is pretty overkill and in most situations this kind of setup will probably just end up getting in the way but I did it anyway just to have fun with it.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;using-gpg-to-configure-your-keys&quot;&gt;Using GPG to configure your keys&lt;&#x2F;h2&gt;
&lt;p&gt;The tool I used for setting up everything on my token was the &lt;code&gt;gpg&lt;&#x2F;code&gt; binary from GnuPG suite and the embedded command-line interface.&lt;&#x2F;p&gt;
&lt;p&gt;Technically most of the same things can be done with standard OpenPGP and CCID combo but that approach felt more wonky and less out-of-the-box for me so I decided not to use it.&lt;&#x2F;p&gt;
&lt;p&gt;Usage of GPG for setting up a smartcard doesn’t seem to be documented very well and there are some things that are just left for you to figure out so I will try to do my best of describing the process here.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s begin with reading some information about our token with the following command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ gpg --card-status&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Reader ...........: 0000:0000:X:0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Application ID ...: 00000000000000000000000000000000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Application type .: OpenPGP&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Version ..........: 3.4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Manufacturer .....: Token2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Serial number ....: 00000000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Name of cardholder: [not set]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Language prefs ...: [not set]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Salutation .......:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;URL of public key : [not set]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Login data .......: [not set]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Signature PIN ....: forced&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Key attributes ...: rsa2048 rsa2048 rsa2048&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Max. PIN lengths .: 127 127 127&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;PIN retry counter : 3 0 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Signature counter : 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;KDF setting ......: off&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;UIF setting ......: Sign=off Decrypt=off Auth=off&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Signature key ....: [none]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Encryption key....: [none]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Authentication key: [none]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;General key info..: [none]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;step-1-change-pin&quot;&gt;Step 1: Change PIN&lt;&#x2F;h3&gt;
&lt;p&gt;PGP smartcards use PINs (user and admin PIN) for confirming operations such as creation of keys, changes of data or signing stuff. These PINs are &lt;strong&gt;different&lt;&#x2F;strong&gt; from the FIDO2 part of your token and you set them up separately. It’s good to start by changing them.&lt;&#x2F;p&gt;
&lt;p&gt;Factory defaults:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;User PIN: &lt;code&gt;123456&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Admin PIN: &lt;code&gt;12345678&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To do that, we need to enter GPG command-line interface on the token:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ gpg --card-edit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And unlock admin-space commands:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&#x2F;card&amp;gt; admin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then proceed to the PIN management menu:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&#x2F;card&amp;gt; passwd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg: OpenPGP card no. 00000000000000000000000000000000 detected&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;1 - change PIN&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2 - unblock PIN&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3 - change Admin PIN&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;4 - set the Reset Code&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Q - quit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Your selection?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Pick option &lt;code&gt;3&lt;&#x2F;code&gt;, which will prompt you to change admin PIN. Then use option &lt;code&gt;1&lt;&#x2F;code&gt; to change user PIN, and &lt;code&gt;Q&lt;&#x2F;code&gt; to exit PIN change menu.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-2-configuring-cryptography&quot;&gt;Step 2: Configuring cryptography&lt;&#x2F;h3&gt;
&lt;p&gt;PGP on the token will generate 3 keys: primary &lt;strong&gt;S&lt;&#x2F;strong&gt;igning and &lt;strong&gt;C&lt;&#x2F;strong&gt;ertifying key &lt;code&gt;[SC]&lt;&#x2F;code&gt;, &lt;strong&gt;A&lt;&#x2F;strong&gt;uthentication sub-key &lt;code&gt;[A]&lt;&#x2F;code&gt; and &lt;strong&gt;E&lt;&#x2F;strong&gt;ncryption sub-key &lt;code&gt;[E]&lt;&#x2F;code&gt;. Default algorithm for all of these keys is &lt;code&gt;rsa2048&lt;&#x2F;code&gt;, which is a safe bet for compatibility with everything, but it’s definitely not the best modern option.&lt;&#x2F;p&gt;
&lt;p&gt;Command used to create keys will &lt;strong&gt;not&lt;&#x2F;strong&gt; prompt you to select algorithm, you need to select those first using the following command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&#x2F;card&amp;gt; key-attr&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which will then prompt you to select algorithm and elliptic curve for each of the keys to be generated.&lt;&#x2F;p&gt;
&lt;p&gt;For a widely supported modern setup, pick &lt;code&gt;ECC&lt;&#x2F;code&gt; and &lt;code&gt;Curve 25519&lt;&#x2F;code&gt; in every case.&lt;&#x2F;p&gt;
&lt;p&gt;Once that’s done you can run the following command to verify that changes have been made to your token’s configuration:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&#x2F;card&amp;gt; list&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;. . .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Key attributes ...: ed25519 cv25519 ed25519&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;. . .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;step-3-generating-keys&quot;&gt;Step 3: Generating keys&lt;&#x2F;h3&gt;
&lt;p&gt;With that out of the way, we can actually generate our set of keys!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&#x2F;card&amp;gt; generate&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;div class=&quot;side-note&quot;&gt;
    &lt;strong&gt;Note:&lt;&#x2F;strong&gt;&amp;nbsp;This section is &lt;em&gt;&lt;strong&gt;important&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; as it will determine your ability to create a clone of your token’s PGP keys. &lt;strong style=&quot;color:orange&quot;&gt;Please read carefully!&lt;&#x2F;strong&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;You will be asked if you want to make an off-card backup of the encryption key.&lt;&#x2F;p&gt;
&lt;p&gt;For maximum security it’s best to select &lt;strong&gt;N&lt;&#x2F;strong&gt;, because the default action &lt;strong&gt;Y&lt;&#x2F;strong&gt; will save the key to your computer and then copy it to the token, defeating the purpose of storing keys in isolation on dedicated hardware.&lt;&#x2F;p&gt;
&lt;p&gt;But it also means that once the keys are generated they &lt;em&gt;&lt;strong&gt;cannot be exported&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; so no backup copy can be made. I describe an alternative procedure for creating an exact clone of two tokens &lt;a href=&quot;https:&#x2F;&#x2F;flufftech.net&#x2F;posts&#x2F;configuring-security-key-for-pgp-and-ssh&#x2F;#alternative-key-generation-for-token-backup&quot;&gt;in another section&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;After that you will be prompted to authenticate with PIN and select standard options for your key such as expiration time and your identity. You will be able to change your UID or add more than one later on.&lt;&#x2F;p&gt;
&lt;p&gt;After picking all the options and accepting them, it’s done. Your Token2 security key now holds a set of PGP keys that you can use for all the standard things that these keys are used for, such as encrypting your emails, creating encrypted backup archives, signing git commits and many more. PGP is a beautifully standard solution in the computer world.&lt;&#x2F;p&gt;
&lt;p&gt;You can run the &lt;code&gt;list&lt;&#x2F;code&gt; command once more to verify that your keys have been generated correctly:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&#x2F;card&amp;gt; list&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;. . .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Signature key ....: A3D6 FF6F 7A89 3043 763F  5168 3AEB 1058 B661 2722&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      created ....: 2026-07-23 18:32:29&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Encryption key....: CB88 B08E CD20 8F68 EB05  5938 1F94 F58E 4B03 9FF8&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      created ....: 2026-07-23 18:32:29&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Authentication key: 4CD3 8306 5841 664A BE68  685A 3F40 606D 8586 A4F0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      created ....: 2026-07-23 18:32:29&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;General key info..:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pub  ed25519&#x2F;3AEB1058B6612722 2026-07-23 test &amp;lt;test@example.com&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sec&amp;gt;  ed25519&#x2F;3AEB1058B6612722  created: 2026-07-23  expires: never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                card-no: 0000 00000000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ssb&amp;gt;  ed25519&#x2F;3F40606D8586A4F0  created: 2026-07-23  expires: never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                card-no: 0000 00000000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ssb&amp;gt;  cv25519&#x2F;1F94F58E4B039FF8  created: 2026-07-23  expires: never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                card-no: 0000 00000000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And the following command outside the GPG environment on your computer to verify that private key stubs have been correctly loaded into your local keyring:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ gpg --list-secret-keys&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;. . .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sec&amp;gt;  ed25519 2026-07-23 [SC]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      A3D6FF6F7A893043763F51683AEB1058B6612722&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Card serial no. = 0000 00000000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;uid           [ultimate] test &amp;lt;test@example.com&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ssb&amp;gt;  ed25519 2026-07-23 [A]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ssb&amp;gt;  cv25519 2026-07-23 [E]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note the &lt;code&gt;&amp;gt;&lt;&#x2F;code&gt; symbol next to keys. It indicates that the keys is stored on a token, not on your computer.&lt;&#x2F;p&gt;
&lt;p&gt;Changes to that key can be made the standard GPG way of using &lt;code&gt;gpg --edit-key KEY_FINGERPRINT&lt;&#x2F;code&gt; command. That can be useful to add extra identities with different email addresses for example.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-4-import-keys-on-other-computers&quot;&gt;Step 4: Import keys on other computers&lt;&#x2F;h3&gt;
&lt;p&gt;One thing that I noticed basically none of the guides on the internet is talking about is loading keys from your token into the local keyring so you can actually use them.&lt;&#x2F;p&gt;
&lt;p&gt;During initial key s setup public keys are imported and private key stubs are added to the keyring, but if you want to use the same already configured token on a different device or if you changed your operating system, you need to set a few more things to be able to do that easily.&lt;&#x2F;p&gt;
&lt;p&gt;GPG’s built-in card environment includes &lt;code&gt;fetch&lt;&#x2F;code&gt; command which does exactly that, but for it to work properly you need to configure a URL on the internet from which your public keys will be imported.&lt;&#x2F;p&gt;
&lt;p&gt;I genuinely don’t understand why this can’t be simply done locally since all the keys are literally there, but that’s the way it’s done 🤷 If I’m actually wrong here and there’s a better way, please let me know.&lt;&#x2F;p&gt;
&lt;p&gt;But alas, for this step you need to export your public key and put it online (which is fine! public keys are meant for sharing!). You can use a well-established public PGP key server such as &lt;a class=&quot;external&quot; rel=&quot;noopener nofollow external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;keys.openpgp.org&#x2F;&quot;&gt;keys.openpgp.org&lt;&#x2F;a&gt; for example, but I just &lt;a href=&quot;&#x2F;pgpkey.asc&quot;&gt;uploaded mine to my website&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here’s a command to export your public key in a plaintext readable format:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ gpg --export --armor KEY_FINGERPRINT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Output should look like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-----BEGIN PGP PUBLIC KEY BLOCK-----&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;xjMEamJjmBYJKwYBBAHaRw8BAQdAqTghyjm+5ajp71X4Z7wwfbVoD+dQllE5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;uIh+y&#x2F;gq3XHNF3Rlc3QgPHRlc3RAZXhhbXBsZS5jb20+wowEEBYKAD4Fgmpi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Y5gECwkHCAmQyhE0EsNnUPADFQgKBBYAAgECGQECmwMCHgEWIQRDMznItGnQ&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;7ciFoFPKETQSw2dQ8AAA&#x2F;zUA&#x2F;3tx7GNTsdR1VDAvu&#x2F;+H6T4z05Iyx2Z1+vlF&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;w71qFAUAP9y01ZJc0GT5PlpXcYUNaKnw9Ppviok3mu5GgLaigrIDM44BGpi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Y5gSCisGAQQBl1UBBQEBB0BWTiDR&#x2F;eDmQzpalzQ2CiygNpPxDRtgHmsznHy&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;SCurJQMBCAfCeAQYFgoAKgWCamJjmAmQyhE0EsNnUPACmwwWIQRDMznItGnQ&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;7ciFoFPKETQSw2dQ8AAAvgwBAMNi62VPYmYmcpGmzvK0kdFQKJXyjCbKl&#x2F;Dd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;6du6d1BXAQDazFUtOA1Zn8lK5YHZjoOL7xQZsyUIEl12WwQUxXd&#x2F;Ag==&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;=WUWn&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-----END PGP PUBLIC KEY BLOCK-----&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can either paste it into an online key server or save it as a file with &lt;code&gt;.asc&lt;&#x2F;code&gt; extension and host somewhere.&lt;&#x2F;p&gt;
&lt;p&gt;Once you do that, you can go back to the GPG environment on your key and set up key fetching:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&#x2F;card&amp;gt; url&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;URL to retrieve public key:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You need to enter a full URL from which your public key can be obtained, for me it’s going to be &lt;code&gt;https:&#x2F;&#x2F;flufftech.net&#x2F;pgpkey.asc&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;And it’s done! You can set up some other extra settings that are mostly unimportant (such as &lt;code&gt;name&lt;&#x2F;code&gt; or &lt;code&gt;salutation&lt;&#x2F;code&gt; which are mostly used with an actual card-format smartcards) or type &lt;code&gt;quit&lt;&#x2F;code&gt; to exit the GPG shell, since you’re set up now.&lt;&#x2F;p&gt;
&lt;p&gt;Now when you connect your token to a new computer, you can import your keys into the local keyring in seconds by using this procedure:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ gpg --card-edit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&#x2F;card&amp;gt; fetch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And your keys are ready to use.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-5-setting-up-ssh-key&quot;&gt;Step 5: Setting up SSH key&lt;&#x2F;h3&gt;
&lt;p&gt;An extra feature of this setup is that it you use your PGP key as your SSH key.&lt;&#x2F;p&gt;
&lt;div class=&quot;side-note&quot;&gt;
    &lt;strong&gt;Note:&lt;&#x2F;strong&gt;&amp;nbsp;This is different from generating &lt;code&gt;ed25519-sk&lt;&#x2F;code&gt; or &lt;code&gt;ecdsa-sk&lt;&#x2F;code&gt; type keys with &lt;code&gt;ssh-keygen&lt;&#x2F;code&gt;, as that will generate a special type of dedicated SSH private key that uses FIDO functionality of your token to access. If you don’t want to use PGP features of the token or you have a FIDO-only U2F device without PGP support, that might be a great alternative for you!
&lt;&#x2F;div&gt;
&lt;p&gt;But since I have all my keys on the token, we’re going to use them together with &lt;code&gt;gpg-agent&lt;&#x2F;code&gt; to use them for SSH.&lt;&#x2F;p&gt;
&lt;div class=&quot;side-note&quot;&gt;
    &lt;strong&gt;Note:&lt;&#x2F;strong&gt;&amp;nbsp;The following section assumes you don’t already use &lt;code&gt;gpg-agent&lt;&#x2F;code&gt; as your SSH agent. If you do, I’m sure you can figure it out.
&lt;&#x2F;div&gt;
&lt;p&gt;To do this, you need to edit your &lt;code&gt;~&#x2F;.gnupg&#x2F;gpg-agent.conf&lt;&#x2F;code&gt; file and add the following config options to it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;enable-ssh-support&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;write-env-file&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;enable-ssh-support&lt;&#x2F;code&gt; is pretty self-explanatory, it enables support for SSH keys in &lt;code&gt;gpg-agent&lt;&#x2F;code&gt; – it will automatically load SSH key from your PGP keys.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;write-env-file&lt;&#x2F;code&gt; is used to share agent information with other processes and sessions as a file so they can always access it.&lt;&#x2F;p&gt;
&lt;p&gt;Another option I really recommend you set up is &lt;code&gt;pinentry-program&lt;&#x2F;code&gt;, which will be used to help you with PIN&#x2F;password prompts. On GNOME the best choice is &lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;pinentry-gnome3&lt;&#x2F;code&gt;, if you run KDE use &lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;pinentry-qt&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you use GNOME, there is a decent chance that GNOME Keyring will interfere with this so it’s best to disable the SSH agent feature of it. That’s usually done by stopping and disabling &lt;code&gt;gcr-ssh-agent.socket&lt;&#x2F;code&gt; and &lt;code&gt;gcr-ssh-agent.service&lt;&#x2F;code&gt; systemd units with &lt;code&gt;systemctl&lt;&#x2F;code&gt; command.&lt;&#x2F;p&gt;
&lt;p&gt;The last step is to reload &lt;code&gt;gpg-agent&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ gpg-connect-agent reloadagent &#x2F;bye&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And set &lt;code&gt;SSH_AUTH_SOCK&lt;&#x2F;code&gt; env variable for your session.&lt;&#x2F;p&gt;
&lt;p&gt;I use &lt;code&gt;fish&lt;&#x2F;code&gt; shell so I added the following 2 lines into my &lt;code&gt;~&#x2F;.config&#x2F;fish&#x2F;config.fish&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;fish&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #0DB9D7;&quot;&gt;set&lt;&#x2F;span&gt;&lt;span&gt; -gx SSH_AGENT_PID &lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #0DB9D7;&quot;&gt;set&lt;&#x2F;span&gt;&lt;span&gt; -gx SSH_AUTH_SOCK &lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;$XDG_RUNTIME_DIR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt;&#x2F;gnupg&#x2F;S.gpg-agent.ssh&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you use &lt;code&gt;bash&lt;&#x2F;code&gt;, add this into your &lt;code&gt;~&#x2F;.bashrc&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #0DB9D7;&quot;&gt;unset&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; SSH_AGENT_PID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; [ &amp;quot;${&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;gnupg_SSH_AUTH_SOCK_by&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;:-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;}&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt; -ne&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F7768E;&quot;&gt; $$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; ];&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9D7CD8;font-style: italic;&quot;&gt;  export&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; SSH_AUTH_SOCK&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;=&amp;quot;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;gpgconf&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E0AF68;&quot;&gt; --list-dirs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; agent-ssh-socket&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;)&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt;fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Make sure to reload your session for this to take effect.&lt;&#x2F;p&gt;
&lt;p&gt;Assuming everything worked out the way it should, this should be enough to automatically load SSH key from your token whenever you plug it in.&lt;&#x2F;p&gt;
&lt;p&gt;You can verify that by running this command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ ssh-add -L&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFYX3suL5yb0099HU5jIz+ttx8Q8VMFRKtzLsrZu0ikX cardno:0000_00000000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If the output you see is similar to the above, with standard SSH key and a comment including your token’s serial number, it’s all good. If there’s nothing, it means something is wrong with the &lt;code&gt;gpg-agent&lt;&#x2F;code&gt; configuration to appending your SSH key into &lt;code&gt;~&#x2F;.gnupg&#x2F;sshcontrol&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;
&lt;p&gt;You can fix that manually by finding the keygrip of your &lt;code&gt;[A]&lt;&#x2F;code&gt; subkey from the token:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ gpg --list-keys --with-keygrip&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;. . .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sub   ed25519 2026-07-23 [A]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Keygrip = F635631A0BD254172DA068AED25E8C0B74BB40D5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then add the keygrip value (here it’s &lt;code&gt;F635631A0BD254172DA068AED25E8C0B74BB40D5&lt;&#x2F;code&gt;) into &lt;code&gt;~&#x2F;.gnupg&#x2F;sshcontrol&lt;&#x2F;code&gt; manually and reload the agent. That should do the trick.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;alternative-key-generation-for-token-backup&quot;&gt;Alternative key generation for token backup&lt;&#x2F;h2&gt;
&lt;p&gt;As I mentioned before, generating keys &lt;em&gt;on&lt;&#x2F;em&gt; the token is safer, but has its downsides in the form or losing the ability to create backup of private keys. Oddly enough, GPG doesn’t let you know about this limitation.&lt;&#x2F;p&gt;
&lt;p&gt;Attempting to export your private key stored on a token with a command like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; gpg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E0AF68;&quot;&gt; --export-private-keys -a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; KEY_FINGERPRINT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; private.key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Will result in an export of a private key &lt;em&gt;stub&lt;&#x2F;em&gt; which is effectively garbage. It really should result in an error instead but it doesn’t and this behavior can be really confusing.&lt;&#x2F;p&gt;
&lt;p&gt;A solution to this problem can be generating a set of keys locally on your computer and then copying them over to your token. In terms of security this is worse, as private keys aren’t isolated entirely inside the token, but this will let us have two tokens with the exact same set of keys for backup.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-1-temporary-keyring&quot;&gt;Step 1: Temporary keyring&lt;&#x2F;h3&gt;
&lt;p&gt;For this process we will create a temporary GnuPG home directory in our computer’s RAM so none of the sensitive data makes it to storage.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s start by verifying that local &lt;code&gt;&#x2F;tmp&lt;&#x2F;code&gt; directory is &lt;em&gt;actually&lt;&#x2F;em&gt; on physical hardware RAM. It usually is but now always.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; df&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E0AF68;&quot;&gt; -h&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #0DB9D7;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; . .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;tmpfs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt;            12G   21M   12G   1% &#x2F;tmp&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you see a line like this including &lt;code&gt;tmpfs&lt;&#x2F;code&gt; next to your mounted &lt;code&gt;&#x2F;tmp&lt;&#x2F;code&gt; directory, you’re good to go. If it’s anything else, stop right here and get yourself a &lt;code&gt;tmpfs&lt;&#x2F;code&gt; working directory for this purpose.&lt;&#x2F;p&gt;
&lt;p&gt;So now we can switch GnuPG to a temporary working space:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; export GNUPGHOME=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;&amp;quot;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;mktemp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E0AF68;&quot;&gt; -d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;)&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And verify it succeeded by running:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; gpg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E0AF68;&quot;&gt; --list-secret-keys&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It should be empty&lt;&#x2F;p&gt;
&lt;div class=&quot;side-note&quot;&gt;
    &lt;strong&gt;Note:&lt;&#x2F;strong&gt;&amp;nbsp;Remember that this &lt;code&gt;$GNUPGHOME&lt;&#x2F;code&gt; override is specific to your currently open shell, avoid doing any of those operations in a different shell session from now on.
&lt;&#x2F;div&gt;
&lt;h3 id=&quot;step-2-local-key-generation&quot;&gt;Step 2: Local key generation&lt;&#x2F;h3&gt;
&lt;p&gt;Now let’s create our keys:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; gpg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E0AF68;&quot;&gt; --full-generate-key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Follow the usual options for key generation, with the default &lt;code&gt;ECC (sign and encrypt)&lt;&#x2F;code&gt; &amp;amp; &lt;code&gt;Curve 25519&lt;&#x2F;code&gt; combo being a good option. GPG will be very unhappy with you when you decide to make a key without a password, but for this purpose it’s actually okay to do it this way since key on the token will be protected by PIN for all sensitive operations.&lt;&#x2F;p&gt;
&lt;p&gt;The default key generation process creates only &lt;code&gt;[SC]&lt;&#x2F;code&gt; primary key and &lt;code&gt;[E]&lt;&#x2F;code&gt; encryption subkey. We have to create an additional &lt;code&gt;[A]&lt;&#x2F;code&gt; authentication subkey manually:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; gpg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E0AF68;&quot;&gt; --expert --edit-key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; KEY_FINGERPRINT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once in expert edit mode, let’s add a subkey:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&amp;gt; addkey&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Please select what kind of key you want:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   (3) DSA (sign only)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   (4) RSA (sign only)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   (5) Elgamal (encrypt only)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   (6) RSA (encrypt only)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   (7) DSA (set your own capabilities)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   (8) RSA (set your own capabilities)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (10) ECC (sign only)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (11) ECC (set your own capabilities)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (12) ECC (encrypt only)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (13) Existing key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (14) Existing key from card&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Your selection?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Select &lt;code&gt;11&lt;&#x2F;code&gt;. The default will be a signing &lt;code&gt;[S]&lt;&#x2F;code&gt; subkey.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Possible actions for this ECC key: Sign Authenticate&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Current allowed actions: Sign&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   (S) Toggle the sign capability&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   (A) Toggle the authenticate capability&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   (Q) Finished&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Your selection?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;First select &lt;code&gt;A&lt;&#x2F;code&gt; to allow authenticate, then &lt;code&gt;S&lt;&#x2F;code&gt; to disable sign feature. Then &lt;code&gt;Q&lt;&#x2F;code&gt; to save.&lt;&#x2F;p&gt;
&lt;p&gt;Select the default &lt;code&gt;Curve 25519&lt;&#x2F;code&gt; curve, your preferred expiration and skip setting a password again.&lt;&#x2F;p&gt;
&lt;p&gt;Output should look like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sec  ed25519&#x2F;8BC55E989F30E234&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     created: 2026-07-23  expires: never       usage: SC&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     trust: ultimate      validity: ultimate&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ssb  cv25519&#x2F;CA1E4DF2A5758FEB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     created: 2026-07-23  expires: never       usage: E&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ssb  ed25519&#x2F;DF08FED94A66B8FB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     created: 2026-07-23  expires: never       usage: A&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[ultimate] (1). test &amp;lt;test@example.com&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;step-3-moving-keys-to-the-token&quot;&gt;Step 3: Moving keys to the token&lt;&#x2F;h3&gt;
&lt;p&gt;Now we can begin copying our keys to both tokens.&lt;&#x2F;p&gt;
&lt;p&gt;A few notes:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;keytocard&lt;&#x2F;code&gt; command can only move one key or subkey at a time, so for each token you need to use it 3 times.&lt;&#x2F;li&gt;
&lt;li&gt;Be careful to &lt;em&gt;&lt;strong&gt;not&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; use &lt;code&gt;save&lt;&#x2F;code&gt; command before all keys are moved to both tokens, as it will remove the locally stored key after &lt;code&gt;keytocard&lt;&#x2F;code&gt; command it used.&lt;&#x2F;li&gt;
&lt;li&gt;After adding keys to the first token, swapping tokens will confuse GPG and it will not be able to do anything. Use &lt;code&gt;quit&lt;&#x2F;code&gt; command and re-enter the &lt;code&gt;--edit-key&lt;&#x2F;code&gt; environment.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Moving primary key:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&amp;gt; keytocard&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Really move the primary key? (y&#x2F;N) y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Please select where to store the key:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   (1) Signature key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   (3) Authentication key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Your selection?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Select &lt;code&gt;y&lt;&#x2F;code&gt; and then &lt;code&gt;1&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To move the &lt;code&gt;[E]&lt;&#x2F;code&gt; subkey first copy it’s fingerprint from the list, then select it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&amp;gt; key E_SUBKEY_FINGERPRINT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&amp;gt; keytocard&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Please select where to store the key:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   (2) Encryption key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Your selection?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Select &lt;code&gt;2&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now to move the &lt;code&gt;[A]&lt;&#x2F;code&gt; subkey you first need to un-select the previous key, select &lt;code&gt;[A]&lt;&#x2F;code&gt; key and only then you can proceed:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&amp;gt; key E_SUBKEY_FINGERPRINT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&amp;gt; key A_SUBKEY_FINGERPRINT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gpg&amp;gt; keytocard&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Please select where to store the key:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   (3) Authentication key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Your selection?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Select &lt;code&gt;3&lt;&#x2F;code&gt; and type &lt;code&gt;quit&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Verify that all keys have been moved:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ gpg --card-status&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;. . .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Signature key ....: E6F9 C957 0C88 42FA 0BE7  8FE5 8BC5 5E98 9F30 E234&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      created ....: 2026-07-23 20:53:48&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Encryption key....: 30C7 7655 B800 09A2 7C18  7BBB CA1E 4DF2 A575 8FEB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      created ....: 2026-07-23 20:53:48&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Authentication key: A187 B940 5D19 F956 0D6F  3C81 DF08 FED9 4A66 B8FB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      created ....: 2026-07-23 21:03:39&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;General key info..: pub  ed25519&#x2F;8BC55E989F30E234 2026-07-23 test &amp;lt;test@example.com&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sec   ed25519&#x2F;8BC55E989F30E234  created: 2026-07-23  expires: never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ssb   cv25519&#x2F;CA1E4DF2A5758FEB  created: 2026-07-23  expires: never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ssb   ed25519&#x2F;DF08FED94A66B8FB  created: 2026-07-23  expires: never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If the output looks something like this, congrats, the move has worked!&lt;&#x2F;p&gt;
&lt;p&gt;You can now swap the tokens, re-enter &lt;code&gt;--edit-key&lt;&#x2F;code&gt; environment and repeat the exact same procedure.&lt;&#x2F;p&gt;
&lt;div class=&quot;side-note&quot;&gt;
    &lt;strong&gt;Note:&lt;&#x2F;strong&gt;&amp;nbsp;It’s best to export public key for the &lt;code&gt;fetch&lt;&#x2F;code&gt; functionality on the token before you delete local copy of all keys because otherwise &lt;code&gt;--full-generate-key&lt;&#x2F;code&gt; can import private key stubs from token but only one at a time. Using &lt;code&gt;fetch&lt;&#x2F;code&gt; grabs them all.
&lt;&#x2F;div&gt;
&lt;p&gt;Once you’re done doing that, you can actually quit edit environment using &lt;code&gt;save&lt;&#x2F;code&gt;, which will remove local copy of all affected keys.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wow-that-was-pretty-long&quot;&gt;Wow that was pretty long&lt;&#x2F;h2&gt;
&lt;p&gt;If you reached this far wow I’m proud of you, you’re such a massive nerd, you deserve a medal 🏅&lt;&#x2F;p&gt;
&lt;p&gt;I have a few words to end this post with, mostly that it was a really interesting journey that tought me some cool things and overall made me more comfortable with handling PGP keys. It was definitely worth it and I sincerely recommend you play with it yourself.&lt;&#x2F;p&gt;
&lt;p&gt;A bonus piece of information I have for you is that this entire project was much funnier for me considering that the entire time I’ve been setting up cryptography stuff on my collar :3&lt;&#x2F;p&gt;

    


    


    


&lt;div class=&quot;media-wrapper &quot;&gt;

    &lt;figure class=&quot;media image&quot;&gt;
        &lt;a href=&quot;&amp;#x2F;posts&amp;#x2F;configuring-security-key-for-pgp-and-ssh&amp;#x2F;pgp-and-ssh-enabled-collar.jpg&quot;&gt;
            
    
    
    
        
    
    
        
        

        
            
        

        
            
        
    
    &lt;picture&gt;
        &lt;source srcset=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;pgp-and-ssh-enabled-collar.e20abc020e2990d0.webp, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;pgp-and-ssh-enabled-collar.1cf2b5b6dbed45ec.webp 2x, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;pgp-and-ssh-enabled-collar.1719c9681223d813.webp 3x&quot; type=&quot;image&#x2F;webp&quot;&gt;
        &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;pgp-and-ssh-enabled-collar.1ddf6d90265356da.jpg&quot; width=&quot;675&quot; height=&quot;508&quot; alt=&quot;jpg file named &amp;#x2F;posts&amp;#x2F;configuring-security-key-for-pgp-and-ssh&amp;#x2F;pgp-and-ssh-enabled-collar.jpg&quot; &gt;
    &lt;&#x2F;picture&gt;

        &lt;&#x2F;a&gt;
        
        &lt;figcaption class=&quot;media-caption&quot;&gt;
            
                &lt;div class=&quot;caption&quot;&gt;This cool accessory is like 1000x cooler now :3&lt;&#x2F;div&gt;
            
            
        &lt;&#x2F;figcaption&gt;
        
    &lt;&#x2F;figure&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Now I literally can SSH into my servers with my collar and it’s genuinely perfect.&lt;&#x2F;p&gt;
&lt;p&gt;If you have any questions or want to share your thoughts, feel free to send me an email or reach me on the Fediverse ^^&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Scanning a (literal) trashcan roll of photographic film</title>
          <pubDate>Sat, 13 Jun 2026 00:00:00 +0000</pubDate>
          <author>rail</author>
          <link>https://flufftech.net/posts/scanning-trashcan-film-negative/</link>
          <guid>https://flufftech.net/posts/scanning-trashcan-film-negative/</guid>
          <description xml:base="https://flufftech.net/posts/scanning-trashcan-film-negative/">&lt;p&gt;Today I’m going to tell you a short story of a negative I found near a trashcan a while ago.&lt;&#x2F;p&gt;
&lt;p&gt;Yes, seriously. I still don’t know how this happened and why would anyone throw film away but here we are.&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;the-negative-itself&quot;&gt;The negative itself&lt;&#x2F;h2&gt;
&lt;p&gt;Let’s start with the main character here – the negative. After I found it and did a quick field inspection, it turned out to definitely contain images and be somewhat damaged. It later turned out the damage was mostly local and not too bad.&lt;&#x2F;p&gt;

    


    



&lt;div class=&quot;media-wrapper &quot;&gt;

    &lt;figure class=&quot;media image&quot;&gt;
        &lt;a href=&quot;&amp;#x2F;posts&amp;#x2F;scanning-trashcan-film-negative&amp;#x2F;roll.jpg&quot;&gt;
            
    
    
    
    
        
        

        
            
        

        
            
        
    
    &lt;picture&gt;
        &lt;source srcset=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;roll.7a0bcc237e25174c.webp, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;roll.1ba9ce09b333a00f.webp 2x, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;roll.e58ba839d2e93a05.webp 3x&quot; type=&quot;image&#x2F;webp&quot;&gt;
        &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;roll.6f29edb80a6f1da5.jpg&quot; width=&quot;350&quot; height=&quot;433&quot; alt=&quot;rail holding a film negative in their hand&quot; &gt;
    &lt;&#x2F;picture&gt;

        &lt;&#x2F;a&gt;
        
        &lt;figcaption class=&quot;media-caption&quot;&gt;
            
                &lt;div class=&quot;caption&quot;&gt;Negative as it was found&lt;&#x2F;div&gt;
            
            
        &lt;&#x2F;figcaption&gt;
        
    &lt;&#x2F;figure&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;The obvious next step was to investigate it further, clean it and attempt to retrieve images from it.&lt;&#x2F;p&gt;
&lt;p&gt;It turns out the film used was Konica VX200, a color film that was manufactured until 2007 as far as I know. This fact was the first hint that the negative is old but let’s not get ahead of ourselves.&lt;&#x2F;p&gt;
&lt;p&gt;Concentrated isopropyl alcohol did just fine cleaning the negative, but that was the end of obvious solutions. My only option to get images out of it in home environment was DSLR scanning, a procedure that is effectively taking a photo of the frame on the negative with a digital camera and processing it digitally from there.&lt;&#x2F;p&gt;
&lt;p&gt;Properly executed DSLR scanning is about the cheaper and most accessible way of scanning film at home (dedicated drum scanners for photographic film tend to be very expensive), but it still requires some hardware. Mostly a good quality backlight and a bracket to hold the film in place.&lt;&#x2F;p&gt;
&lt;p&gt;Sadly, I don’t have any of those things, so I had to improvise with what I had on hand.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-stupid-method&quot;&gt;The (stupid) method&lt;&#x2F;h2&gt;
&lt;p&gt;Due to a lack of either a backlight or a bracket, I decided to go at it redneck style and use whatever I had, knowing full well the tools will be suboptimal, and the results won’t be nearly as good as they could’ve been with proper equipment. But given the way I got the negative to begin with and the potential to have fun, sure, let’s go.&lt;&#x2F;p&gt;
&lt;p&gt;My trusty Pixel 7 phone set to max brightness with HDR took the role of backlight – a solution that works just fine except for one tiny issue. You can see the LED matrix in every image, making it basically impossible to get all the detail from the image. Effective resolution goes down a lot.&lt;&#x2F;p&gt;
&lt;p&gt;And instead of a bracket, I grabbed two hex keys and put them at the edges of the frame 👍️&lt;&#x2F;p&gt;

    


    



&lt;div class=&quot;media-wrapper &quot;&gt;

    &lt;figure class=&quot;media image&quot;&gt;
        &lt;a href=&quot;&amp;#x2F;posts&amp;#x2F;scanning-trashcan-film-negative&amp;#x2F;scanning-setup.jpeg&quot;&gt;
            
    
    
    
    
        
        

        
            
        

        
            
        
    
    &lt;picture&gt;
        &lt;source srcset=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;scanning-setup.5e66536b8741e92f.webp, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;scanning-setup.65dcb2934db735e8.webp 2x, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;scanning-setup.ebc4913135d74105.webp 3x&quot; type=&quot;image&#x2F;webp&quot;&gt;
        &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;scanning-setup.8af28bcc794121c2.jpg&quot; width=&quot;610&quot; height=&quot;460&quot; alt=&quot;Pixel phone with white background, roll of film stretched across it and held in place with two large hex keys&quot; &gt;
    &lt;&#x2F;picture&gt;

        &lt;&#x2F;a&gt;
        
        &lt;figcaption class=&quot;media-caption&quot;&gt;
            
                &lt;div class=&quot;caption&quot;&gt;The scanning setup I used was a little bit suboptimal, let’s say&lt;&#x2F;div&gt;
            
            
        &lt;&#x2F;figcaption&gt;
        
    &lt;&#x2F;figure&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;With that setup ready, it’s time to grab a camera and get to “scanning”!&lt;&#x2F;p&gt;

    


    



&lt;div class=&quot;media-wrapper &quot;&gt;

    &lt;figure class=&quot;media image&quot;&gt;
        &lt;a href=&quot;&amp;#x2F;posts&amp;#x2F;scanning-trashcan-film-negative&amp;#x2F;scanning.jpg&quot;&gt;
            
    
    
    
    
        
        

        
            
        

        
    
    &lt;picture&gt;
        &lt;source srcset=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;scanning.5cbf24a8fec2f377.webp, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;scanning.caf414c6b2975290.webp 2x&quot; type=&quot;image&#x2F;webp&quot;&gt;
        &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;scanning.40f72c074e657d6b.jpg&quot; width=&quot;540&quot; height=&quot;526&quot; alt=&quot;rail taking a photo of a frame on the negative with their camera&quot; &gt;
    &lt;&#x2F;picture&gt;

        &lt;&#x2F;a&gt;
        
        &lt;figcaption class=&quot;media-caption&quot;&gt;
            
                &lt;div class=&quot;caption&quot;&gt;A very professional work environment&lt;&#x2F;div&gt;
            
            
        &lt;&#x2F;figcaption&gt;
        
    &lt;&#x2F;figure&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;I used my Nikon D3200 with a Sigma 18-35mm f&#x2F;1.8 AF lens. It’s not a micro lens, another less-than-ideal thing in this setup, but it was able to focus just close enough to make it work with some fair bit of cropping later in editing.&lt;&#x2F;p&gt;

    


    



&lt;div class=&quot;media-wrapper &quot;&gt;

    &lt;figure class=&quot;media image&quot;&gt;
        &lt;a href=&quot;&amp;#x2F;posts&amp;#x2F;scanning-trashcan-film-negative&amp;#x2F;lightroom-raw.png&quot;&gt;
            
    
    
    
    
        
        

        
            
        

        
    
    &lt;picture&gt;
        &lt;source srcset=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;lightroom-raw.0c0bcac1d737483d.webp, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;lightroom-raw.0b5c2ed1dbbf0a14.webp 2x&quot; type=&quot;image&#x2F;webp&quot;&gt;
        &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;lightroom-raw.25bcf331221dda99.jpg&quot; width=&quot;820&quot; height=&quot;447&quot; alt=&quot;Screenshot of Adobe Lightroom 2015 with a cropped image of a frame on a negative&quot; &gt;
    &lt;&#x2F;picture&gt;

        &lt;&#x2F;a&gt;
        
        &lt;figcaption class=&quot;media-caption&quot;&gt;
            
                &lt;div class=&quot;caption&quot;&gt;Negative image opened in Lightroom – cropped and perspective adjusted&lt;&#x2F;div&gt;
            
            
        &lt;&#x2F;figcaption&gt;
        
    &lt;&#x2F;figure&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Once all frames were individually photographed, I got to work. My RAW editing software of choice is Adobe Lightroom, 2015 version ran through Wine on Linux. That’s a whole other topic I should probably write about, but alas.&lt;&#x2F;p&gt;
&lt;p&gt;It was only during this project when I realized working with the RGB levels curve is very annoying and buggy in this setup, so I decided to split the editing procedure into two steps:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Lightroom: Crop, perspective correction and other RAW-level small fixes to the photos&lt;&#x2F;li&gt;
&lt;li&gt;GIMP: Inverting and color-correcting exported JPEG files&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;

    

&lt;div class=&quot;media-wrapper &quot;&gt;
    &lt;figure class=&quot;media video &quot;&gt;
        &lt;video
            controls
            preload=&quot;metadata&quot;
            poster=&quot;https:&#x2F;&#x2F;flufftech.net&#x2F;posts&#x2F;scanning-trashcan-film-negative&#x2F;gimp-invert-thumb.webp&quot;
            width=&quot;870&quot;
            aria-label=&quot;Process of inverting a negative in GIMP&quot;
            
        &gt;
            &lt;source src=&quot;&amp;#x2F;posts&amp;#x2F;scanning-trashcan-film-negative&amp;#x2F;gimp-invert.webm&quot; type=&quot;video&#x2F;webm&quot;&gt;
            
            &lt;source src=&quot;&amp;#x2F;posts&amp;#x2F;scanning-trashcan-film-negative&amp;#x2F;gimp-invert.mp4&quot; type=&quot;video&#x2F;mp4&quot;&gt;
            
            Your browser does not support the video tag. Download the video &lt;a href=&quot;&amp;#x2F;posts&amp;#x2F;scanning-trashcan-film-negative&amp;#x2F;gimp-invert.webm&quot;&gt;here&lt;&#x2F;a&gt;.
        &lt;&#x2F;video&gt;
        
        &lt;figcaption class=&quot;media-caption&quot;&gt;
            
                &lt;div class=&quot;caption&quot;&gt;It turns out GIMP is actually really good for inverting negatives&lt;&#x2F;div&gt;
            
            
        &lt;&#x2F;figcaption&gt;
        
    &lt;&#x2F;figure&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;The second step turned out to be actually surprisingly simple and quick. As it happens, GIMP is very competent at doing exactly this task without any complicated procedures. Inverting color channels is as simple as clicking one menu option, and the buil-in automatic color equalization tool was plenty good enough for the purpose of this project.&lt;&#x2F;p&gt;
&lt;p&gt;The end results were definitely much better than I expected when starting this project!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-result-and-findings&quot;&gt;The result and findings&lt;&#x2F;h2&gt;
&lt;p&gt;Now I want to take a moment to talk about the final results and findings about the content of the roll once I was able to take a proper look at it.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s start by addressing something that some of you may think – no, there are no nudes on that film. There is nothing spicy or inappropriate. Actually, looking at the photos, it feels like someone’s early adventures with photography.&lt;&#x2F;p&gt;
&lt;p&gt;Images include photos people, house pets, architecture and (I think) some documentary of a trip, school trip it looks like.&lt;&#x2F;p&gt;
&lt;p&gt;An interesting part of this is also that judging by the way people are clothed, the design of interiors and a &lt;em&gt;very&lt;&#x2F;em&gt; old-school bus in one of the photos, I think these pictures were taken around 1990s or early 2000s – old stuff.&lt;&#x2F;p&gt;
&lt;p&gt;I was able to recover 33 frames out of the roll. The rest was either underexposed to a point of being unrecoverable or just blank.&lt;&#x2F;p&gt;
&lt;p&gt;Actually, the way there are blank spaces in the middle of the film makes me think the camera used might’ve been one of the Zenit Soviet-made SLRs. The one I own (Zenit TTL) can sometimes get “stuck” in a way that allows film to be advanced without shots being taken. I had that exact issue before.&lt;&#x2F;p&gt;
&lt;p&gt;Either way, I found the experience of working on this negative quite fascinating.&lt;&#x2F;p&gt;
&lt;p&gt;There is a feeling of uncovering a mystery, and ultimately working with photos that were taken by a stranger, capturing moments they spent with family or friends is oddly intimate, like entering that person’s life and observing it with a degree of insight that wouldn’t be possible any other way.&lt;&#x2F;p&gt;
&lt;p&gt;That ultimately is the reason why I won’t share most of what I found, but I am going to include a few photos just to give you a taste of what I discovered.&lt;&#x2F;p&gt;
&lt;div class=&quot;simple-gallery&quot;&gt;

    


    



&lt;div class=&quot;media-wrapper &quot;&gt;

    &lt;figure class=&quot;media image&quot;&gt;
        &lt;a href=&quot;&amp;#x2F;posts&amp;#x2F;scanning-trashcan-film-negative&amp;#x2F;photo1.jpg&quot;&gt;
            
    
    
    
    
        
        

        
            
        

        
            
        
    
    &lt;picture&gt;
        &lt;source srcset=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo1.f0ac5ee58949ff9c.webp, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo1.8b8210c2397757fc.webp 2x, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo1.c2f3621cedf19a0a.webp 3x&quot; type=&quot;image&#x2F;webp&quot;&gt;
        &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo1.264d0f6f2b601d83.jpg&quot; width=&quot;350&quot; height=&quot;312&quot; alt=&quot;Cat on a windowsill&quot; &gt;
    &lt;&#x2F;picture&gt;

        &lt;&#x2F;a&gt;
        
        &lt;figcaption class=&quot;media-caption&quot;&gt;
            
                &lt;div class=&quot;caption&quot;&gt;A cat photo, ofc :3&lt;&#x2F;div&gt;
            
            
        &lt;&#x2F;figcaption&gt;
        
    &lt;&#x2F;figure&gt;
&lt;&#x2F;div&gt;


    


    



&lt;div class=&quot;media-wrapper &quot;&gt;

    &lt;figure class=&quot;media image&quot;&gt;
        &lt;a href=&quot;&amp;#x2F;posts&amp;#x2F;scanning-trashcan-film-negative&amp;#x2F;photo2.jpg&quot;&gt;
            
    
    
    
    
        
        

        
            
        

        
            
        
    
    &lt;picture&gt;
        &lt;source srcset=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo2.8acfe45b42bfbf30.webp, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo2.a8b6acd4b47f61fa.webp 2x, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo2.a153319fdd6e3ebb.webp 3x&quot; type=&quot;image&#x2F;webp&quot;&gt;
        &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo2.ed95d3326d3eb1e0.jpg&quot; width=&quot;350&quot; height=&quot;308&quot; alt=&quot;A game of football with intense lens flare from the sun&quot; &gt;
    &lt;&#x2F;picture&gt;

        &lt;&#x2F;a&gt;
        
    &lt;&#x2F;figure&gt;
&lt;&#x2F;div&gt;


    


    



&lt;div class=&quot;media-wrapper &quot;&gt;

    &lt;figure class=&quot;media image&quot;&gt;
        &lt;a href=&quot;&amp;#x2F;posts&amp;#x2F;scanning-trashcan-film-negative&amp;#x2F;photo3.jpg&quot;&gt;
            
    
    
    
    
        
        

        
            
        

        
            
        
    
    &lt;picture&gt;
        &lt;source srcset=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo3.50861db22e4e9584.webp, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo3.ad892d550b74e0a4.webp 2x, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo3.9a522df76f42c96e.webp 3x&quot; type=&quot;image&#x2F;webp&quot;&gt;
        &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo3.5be4e07325032f04.jpg&quot; width=&quot;350&quot; height=&quot;304&quot; alt=&quot;Person with long curly hair leaning towards a swan in a lake&quot; &gt;
    &lt;&#x2F;picture&gt;

        &lt;&#x2F;a&gt;
        
        &lt;figcaption class=&quot;media-caption&quot;&gt;
            
                &lt;div class=&quot;caption&quot;&gt;One of the prettiest images of the roll&lt;&#x2F;div&gt;
            
            
        &lt;&#x2F;figcaption&gt;
        
    &lt;&#x2F;figure&gt;
&lt;&#x2F;div&gt;


    


    



&lt;div class=&quot;media-wrapper &quot;&gt;

    &lt;figure class=&quot;media image&quot;&gt;
        &lt;a href=&quot;&amp;#x2F;posts&amp;#x2F;scanning-trashcan-film-negative&amp;#x2F;photo4.jpg&quot;&gt;
            
    
    
    
    
        
        

        
            
        

        
            
        
    
    &lt;picture&gt;
        &lt;source srcset=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo4.86509fc63153be26.webp, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo4.8c68cf715c9f3fda.webp 2x, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo4.671beeac9e633416.webp 3x&quot; type=&quot;image&#x2F;webp&quot;&gt;
        &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo4.d9bfb4b6d18064bd.jpg&quot; width=&quot;350&quot; height=&quot;307&quot; alt=&quot;A lake during a sunset&quot; &gt;
    &lt;&#x2F;picture&gt;

        &lt;&#x2F;a&gt;
        
        &lt;figcaption class=&quot;media-caption&quot;&gt;
            
                &lt;div class=&quot;caption&quot;&gt;Vibes…&lt;&#x2F;div&gt;
            
            
        &lt;&#x2F;figcaption&gt;
        
    &lt;&#x2F;figure&gt;
&lt;&#x2F;div&gt;


    


    



&lt;div class=&quot;media-wrapper &quot;&gt;

    &lt;figure class=&quot;media image&quot;&gt;
        &lt;a href=&quot;&amp;#x2F;posts&amp;#x2F;scanning-trashcan-film-negative&amp;#x2F;photo5.jpg&quot;&gt;
            
    
    
    
    
        
        

        
            
        

        
            
        
    
    &lt;picture&gt;
        &lt;source srcset=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo5.a96de3374063d7ab.webp, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo5.96beaeffcc7be102.webp 2x, https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo5.8f602c2d70e997a8.webp 3x&quot; type=&quot;image&#x2F;webp&quot;&gt;
        &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;flufftech.net&amp;#x2F;processed_images&amp;#x2F;photo5.62e9f2dc54f1a258.jpg&quot; width=&quot;350&quot; height=&quot;313&quot; alt=&quot;A group of kids posing for a small group photo in front of a bus&quot; &gt;
    &lt;&#x2F;picture&gt;

        &lt;&#x2F;a&gt;
        
    &lt;&#x2F;figure&gt;
&lt;&#x2F;div&gt;

&lt;&#x2F;div&gt;
&lt;p&gt;And one last thing that I got out of this adventure is an even stronger desire to get the hardware and do proper DSLR scanning of my own film in the future, heh.&lt;&#x2F;p&gt;
&lt;p&gt;Cheers!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>This website is now using Zola</title>
          <pubDate>Sun, 11 May 2025 00:00:00 +0000</pubDate>
          <author>rail</author>
          <link>https://flufftech.net/posts/this-website-now-uses-zola/</link>
          <guid>https://flufftech.net/posts/this-website-now-uses-zola/</guid>
          <description xml:base="https://flufftech.net/posts/this-website-now-uses-zola/">&lt;p&gt;After writing the &lt;a href=&quot;..&#x2F;signal-desktop-natively-on-wayland&#x2F;&quot;&gt;last blog post&lt;&#x2F;a&gt; it became obvious to me that migrating to a proper static site generator with templates and all the fancy stuff is necessary, so that’s what I’ve been working on for the past few days.&lt;&#x2F;p&gt;
&lt;p&gt;Historically this website has always been just raw HTML with a bunch of CSS thrown at it. That had it’s benefits like me being able to just tinker with everything and maintain a sense of absolute minimalism (which to be honest was completely fine back when it used to be just a front page), but as the site grew a bit, it became a massive chore and a pain in the ass every time I wanted to change or add anything.&lt;&#x2F;p&gt;
&lt;p&gt;So anyways, my choice was mostly limited to &lt;a class=&quot;external&quot; rel=&quot;noopener nofollow external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gohugo.io&quot;&gt;Hugo&lt;&#x2F;a&gt; (written in Go) and &lt;a class=&quot;external&quot; rel=&quot;noopener nofollow external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&quot;&gt;Zola&lt;&#x2F;a&gt; (written in Rust). Ultimately I decided to give Zola a go and stuck with that choice.&lt;&#x2F;p&gt;
&lt;p&gt;I still decided to not use any of the publicly available templates and just re-created the whole layout of my original page with some changes, because why not.&lt;&#x2F;p&gt;
&lt;p&gt;In this post I will yap a little bit about my experience with this transition, what improvements happened, what it means for this blog and which things annoyed me in the process. Enjoy.&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;changes&quot;&gt;Changes&lt;&#x2F;h2&gt;
&lt;p&gt;For the starters, while migrating to Zola i decided to make some changes to the website. Some content got dropped. It may come back at some point in a slightly different form but my photography showcase and a somewhat already outdated “my hardware” pages are gone. This is not an accident.&lt;&#x2F;p&gt;
&lt;p&gt;Also, the &lt;a href=&quot;&#x2F;about&#x2F;&quot;&gt;about me&lt;&#x2F;a&gt; page has been refreshed. Go give a look if you want.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, the theme while mostly unmodified (not without trying, I wanted to give the color scheme a refresh but hit a creative wall) has some more-or-less minor changes too. Honestly I like it better now :3&lt;&#x2F;p&gt;
&lt;h2 id=&quot;benefits-for-me-and-maybe-for-you&quot;&gt;Benefits for me and (maybe) for you&lt;&#x2F;h2&gt;
&lt;p&gt;So, why do I even bothered so much?&lt;&#x2F;p&gt;
&lt;p&gt;Well, mainly because I wanted to write more blogs for a while and one of the things stopping me (besides ADHD) has been the painstaking process of manually working with HTML to post anything and then very carefully verifying that everything works.&lt;&#x2F;p&gt;
&lt;p&gt;In a sense, every blog post means writing a website from the scratch 🥴&lt;&#x2F;p&gt;
&lt;p&gt;Now I can work with a &lt;a class=&quot;external&quot; rel=&quot;noopener nofollow external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Markdown&quot;&gt;regular markdown&lt;&#x2F;a&gt; to write content which is way faster and less annoying, so you can (maybe! pls don’t hold me liable here!) expect more posts coming in the foreseeable future.&lt;&#x2F;p&gt;
&lt;p&gt;In addition to that the &lt;a href=&quot;&#x2F;atom.xml&quot;&gt;Atom&#x2F;RSS feed&lt;&#x2F;a&gt; is now automatically generated, so the chance of me forgetting to update it when I publish new content drastically drops ^^&lt;&#x2F;p&gt;
&lt;p&gt;Also finally I decided to publish the source code of this site publicly now that it’s a bit less of a hot mess – it’s &lt;a class=&quot;external&quot; rel=&quot;noopener nofollow external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;git.vulpinecitrus.info&#x2F;rail&#x2F;flufftech-net&quot;&gt;available here&lt;&#x2F;a&gt; on the &lt;a class=&quot;external&quot; rel=&quot;noopener nofollow external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;forgejo.org&#x2F;&quot;&gt;Forgejo&lt;&#x2F;a&gt; instance hosted by my &lt;a class=&quot;external&quot; rel=&quot;noopener nofollow external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;vulpinecitrus.info&#x2F;&quot;&gt;partner&lt;&#x2F;a&gt; :3&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-the-switch-was-a-little-annoying&quot;&gt;Why the switch was a little annoying&lt;&#x2F;h2&gt;
&lt;p&gt;Some things with getting into static site generators with their own templating engines are a bit annoying. Honestly what made me struggle a lot here especially at the very beginning was the documentation.&lt;&#x2F;p&gt;
&lt;p&gt;I’m mostly mentioning it here at all to remind those of you reading this about something I find very important while writing good docs.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Please, for the love of gods, make sure you don’t write it for yourself, actually&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Zola’s documentation in many aspects strikes me as a text written by the developers for themselves, without even realizing that. It makes a lot of assumptions about the knowledge of the reader that for many (most?) of them won’t be true.&lt;&#x2F;p&gt;
&lt;p&gt;It’s very hard to find basic information about the syntax of templating language and how different elements of the site interact with each other, and those bits that mention it are very technical in a way that might be difficult to understand for many people.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;So yeah to end this too-long-for-its-own-good blog, I had fun. And now my site is better and easier to deploy new content. I will probably make some updates to it and improve it further soon, but for now I’m satisfied enough to show it to the world.&lt;&#x2F;p&gt;
&lt;p&gt;Cya!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Running Signal desktop app natively on Wayland</title>
          <pubDate>Sun, 04 May 2025 00:00:00 +0000</pubDate>
          <author>rail</author>
          <link>https://flufftech.net/posts/signal-desktop-natively-on-wayland/</link>
          <guid>https://flufftech.net/posts/signal-desktop-natively-on-wayland/</guid>
          <description xml:base="https://flufftech.net/posts/signal-desktop-natively-on-wayland/">&lt;p&gt;Signal, the private messaging app, ships an Electron-based desktop application available for Windows, Mac and Linux computers.&lt;&#x2F;p&gt;
&lt;p&gt;The Linux version by default does not support Wayland natively and will run through XWayland in a Wayland environment despite generally accepted system-wide configuration that should cause a different behavior.&lt;&#x2F;p&gt;
&lt;p&gt;That is not without a reason. Signal desktop for Linux ships its own packaged Electron build which does not respect &lt;code&gt;$HOME&#x2F;.config&#x2F;electron-flags.conf&lt;&#x2F;code&gt; configuration nor has any custom &lt;code&gt;-flags.conf&lt;&#x2F;code&gt; file to pull configuration from. I can speculate this behavior is due to (understandable) security concerns that would come from using external system-wide Electron and its configurations.&lt;&#x2F;p&gt;
&lt;p&gt;In this post I will summarize my journey into configuring Signal to work natively on Wayland and issues I encountered along the way.&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;making-signal-not-use-xwayland&quot;&gt;Making Signal not use XWayland&lt;&#x2F;h2&gt;
&lt;div class=&quot;side-note&quot;&gt;
    &lt;strong&gt;Note:&lt;&#x2F;strong&gt;&amp;nbsp;At the beginning of writing this blog, I was not aware of Signal actually respecting an environment variable and wanted to suggest a different, more complicated solution. The things you get to know ^^
&lt;&#x2F;div&gt;
&lt;p&gt;Setting &lt;code&gt;ELECTRON_OZONE_PLATFORM_HINT&lt;&#x2F;code&gt; environment variable to &lt;code&gt;&quot;auto&quot;&lt;&#x2F;code&gt; will make Signal start without using XWayland.&lt;&#x2F;p&gt;
&lt;p&gt;This behavior didn’t use to be the case in the past, and since Signal doesn’t respect Electron flags set in a config file, the only option of making it run on Wayland was adding command-line flags directly into the &lt;code&gt;.desktop&lt;&#x2F;code&gt; file. That obviously had its implications for example for window managers such as Sway, because they don’t use those files to start applications.&lt;&#x2F;p&gt;
&lt;p&gt;Usually the easiest way to set up an environmental variable for your system is by creating a file in &lt;code&gt;~&#x2F;.config&#x2F;environment.d&#x2F;&lt;&#x2F;code&gt; folder. I will assume you never set it up before, so here is what you need to do:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; mkdir ~&#x2F;.config&#x2F;environment.d&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; echo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt;ELECTRON_OZONE_PLATFORM_HINT=&amp;quot;auto&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;&amp;#39; &amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; ~&#x2F;.config&#x2F;environment.d&#x2F;variables.conf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;div class=&quot;side-note&quot;&gt;
    &lt;strong&gt;Note:&lt;&#x2F;strong&gt;&amp;nbsp;This solution only works for distributions using systemd. If you don’t use one of those, I’m sure you know best how to set up env variables on your system!
&lt;&#x2F;div&gt;
&lt;p&gt;After variables from the configuration file are loaded, Signal (and other Electron applications) should start without using XWayland. You can verify that by running &lt;code&gt;xlsclients&lt;&#x2F;code&gt; (&lt;code&gt;signal-desktop&lt;&#x2F;code&gt; should &lt;strong&gt;not&lt;&#x2F;strong&gt; appear there).&lt;&#x2F;p&gt;
&lt;p&gt;However, this does not mean there are no issues…&lt;&#x2F;p&gt;
&lt;h2 id=&quot;problems-ahead&quot;&gt;Problems ahead&lt;&#x2F;h2&gt;
&lt;p&gt;There are &lt;em&gt;some&lt;&#x2F;em&gt; problems&lt;a class=&quot;external&quot; rel=&quot;noopener nofollow external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;signalapp&#x2F;Signal-Desktop&#x2F;issues?q=state%3Aopen%20label%3A%22Wayland%22&quot;&gt;&lt;sup&gt;[1]&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt; with running Signal natively on Wayland. I will only cover some of them here, consult GitHub if you encounter different ones.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;problem-1-starting-minimized&quot;&gt;Problem 1: Starting minimized&lt;&#x2F;h3&gt;
&lt;div class=&quot;side-note&quot;&gt;
    &lt;strong&gt;Note:&lt;&#x2F;strong&gt;&amp;nbsp;Check if GitHub issue &lt;a class=&quot;external&quot; rel=&quot;noopener nofollow external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;signalapp&#x2F;Signal-Desktop&#x2F;issues&#x2F;6368&quot;&gt;#6368&lt;&#x2F;a&gt; hasn’t been fixed at the time of reading. Maybe you don’t need to do anything!
&lt;&#x2F;div&gt;
&lt;p&gt;Trying to run Signal, you may notice it seems to not work initially, or only starts in system tray, not launching the window and you need to start the application twice or more to actually get it running. This might not be a desired behavior and does not normally happen under X11.&lt;&#x2F;p&gt;
&lt;p&gt;This problem can be approached in a few different ways.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;solution-1-json-config&quot;&gt;Solution 1: JSON config&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;strong&gt;The first solution&lt;&#x2F;strong&gt; is changing the &lt;code&gt;maximized&lt;&#x2F;code&gt; key within &lt;code&gt;~&#x2F;.config&#x2F;Signal&#x2F;ephemeral.json&lt;&#x2F;code&gt; file from &lt;code&gt;false&lt;&#x2F;code&gt; to &lt;code&gt;true&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;But this does not fully solve the issue, as contents of &lt;code&gt;~&#x2F;.config&#x2F;Signal&#x2F;ephemeral.json&lt;&#x2F;code&gt; are overwritten by Signal on every startup. To solve this, you may need to create a script that will be run every time the app is closed or before it is started, setting the window maximization key to &lt;code&gt;true&lt;&#x2F;code&gt; every time.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;solution-2-desktop-file&quot;&gt;Solution 2: &lt;code&gt;.desktop&lt;&#x2F;code&gt; file&lt;&#x2F;h4&gt;
&lt;div class=&quot;side-note&quot;&gt;
    &lt;strong&gt;Note:&lt;&#x2F;strong&gt;&amp;nbsp;If you use a window manager, ignore this section entirely and skip to the &lt;a href=&quot;#solution-3-patched-build&quot;&gt;section below&lt;&#x2F;a&gt; instead.
&lt;&#x2F;div&gt;
&lt;p&gt;Another solution is less elegant and involves modifying the &lt;code&gt;.desktop&lt;&#x2F;code&gt; file used to run the application in a desktop environment. That file controls entries in the application list!&lt;&#x2F;p&gt;
&lt;p&gt;First, you need to copy a system-wide &lt;code&gt;.desktop&lt;&#x2F;code&gt; file and make a local copy of it. Then open it using a text editor:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; cp &#x2F;usr&#x2F;share&#x2F;applications&#x2F;signal-desktop.desktop ~&#x2F;.local&#x2F;share&#x2F;applications&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; nano ~&#x2F;.local&#x2F;share&#x2F;applications&#x2F;signal-desktop.desktop&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once you have the file opened, find a line starting with &lt;code&gt;Exec=&lt;&#x2F;code&gt;. It should be somewhere in the upper half of the file. &lt;strong&gt;Replace it&lt;&#x2F;strong&gt; with:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;Exec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt;signal-desktop&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; sleep&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF9E64;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; signal-desktop&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After saving, you need to update the database of &lt;code&gt;.desktop&lt;&#x2F;code&gt; files:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; update-desktop-database ~&#x2F;.local&#x2F;share&#x2F;applications&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That should result in the Signal application being called twice, once to start it minimized and once more to make it show up.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;solution-3-patched-build&quot;&gt;Solution 3: Patched build&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;strong&gt;Third way&lt;&#x2F;strong&gt; of fixing this is, if available on your distribution, installing &lt;a class=&quot;external&quot; rel=&quot;noopener nofollow external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;aur.archlinux.org&#x2F;packages&#x2F;signal-desktop-fix-sway&quot;&gt;&lt;code&gt;signal-desktop-fix-sway&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; build of Signal. It does fix this issue. Said package is available for example on Arch Linux through AUR.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;problem-2-visual-glitches&quot;&gt;Problem 2: Visual glitches&lt;&#x2F;h3&gt;
&lt;p&gt;Running Signal on Wayland not through XWayland may also cause a bunch of different visual glitches such as visual stuttering, glitchy shadows around the window or blurriness (e.g. with UI fractional scaling enabled). Unfortunately I do not know a way of fixing those. We probably need to wait for the developers to fix this :&#x2F;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;If you have any questions, feel free to reach out to me on the fediverse – you can find me there under &lt;a class=&quot;external&quot; rel=&quot;noopener nofollow external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;social.flufftech.net&#x2F;@rail&quot;&gt;&lt;code&gt;@rail@flufftech.net&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
    </channel>
</rss>
