Skip to content

Instantly share code, notes, and snippets.

@marcellodesales
Forked from jplew/README.md
Last active May 25, 2024 19:31
Show Gist options
  • Save marcellodesales/4beda0a503c5ff38b1fe4e66997b5e4d to your computer and use it in GitHub Desktop.
Save marcellodesales/4beda0a503c5ff38b1fe4e66997b5e4d to your computer and use it in GitHub Desktop.
How to Setup SSH and GPG keys with Gitlab

Set up Keybase.io, GPG & Git to sign commits on Github, Gitlab

This is a step-by-step guide on how to create a GPG key on keybase.io, adding it to a local GPG setup and use it with Git and Gitlab.

Requirements

  • MacOS: Use homebrew
  • Linux: Use apt-get, apk, etc
  • Windows: Get a better life, replace the entire OS with MacOS or Linux

Use the appropriate command for your OS...

  1. Install GPG CLI:
$ brew install gpg
  1. Install Keybase:

Install from the website https://prerelease.keybase.io/

  1. You should now have both the keycloak CLI and the Keybase desktop app (/Applications/Keybase). Open the Keybase app, create an account and sign in.

Add your public SSH key to Gitlab:

  1. Generate new SSH keys:
$ ssh-keygen -o -t rsa -b 4096 -C "email@host.type"
  1. Copy your public SSH key to your clipboard:
cat ~/.ssh/id_rsa.pub | pbcopy
  1. Add the key to repos
  1. Test that this worked by cloning a repo using the git:// protocol

This should succeed if you are a member of the repo and that you can clone the repo read/write permissions.

Create a new GPG key using the Keybase CLI

  1. Generate a new PGP key and write it to your local secret keychain:
$ keybase pgp gen --multi

# Enter your real name, which will be publicly visible in your new key: Patrick Stadler
# Enter a public email address for your key: patrick.stadler@gmail.com
# Enter another email address (or <enter> when done):
# Push an encrypted copy of your new secret key to the Keybase.io server? [Y/n] Y
# ▶ INFO PGP User ID: Patrick Stadler <patrick.stadler@gmail.com> [primary]
# ▶ INFO Generating primary key (4096 bits)
# ▶ INFO Generating encryption subkey (4096 bits)
# ▶ INFO Generated new PGP key:
# ▶ INFO   user: Patrick Stadler <patrick.stadler@gmail.com>
# ▶ INFO   4096-bit RSA key, ID CB86A866E870EE00, created 2016-04-06
# ▶ INFO Exported new key to the local GPG keychain

You will be prompted to set a passphrase. Create a strong, 31-character password using your Keychain Access app (see reference image above).

Enter it twice to confirm. Since you will likely need it again, store this password somewhere secure, like as a Secure Note in Keychain Access, or in a password manager like LastPass.

Export and Import from Keybase

Keybase Commands: https://book.keybase.io/docs/cli#basics Explanation: https://davidwinter.dev/managing-gpg-with-keybase/ NOTE: Make sure to set export GPG_TTY=$(tty) before starting

$ cat ~/.zshenv
export GPG_TTY=$(tty)

if [ -f $HOME/.cargo/env ]; then
  . "$HOME/.cargo/env"
fi
  • Login and verify user (Note that if the desktop version is available you are all logged in)
$ keybase login
$ keybase whoami
marcellodesales
$ keybase status
Username:      marcellodesales
Logged in:     yes

Device:
    name:      Mac Device - Work **** M1 MAC
    ID:        92b8a65*****01318
    status:    active
...
...
  • Look for the key required
$ keybase pgp list
Keybase Key ID:  0101cd33c5162998afc99ae6ec76f1bea8709b1046dc036a23ce356a4e47947588ae0a
PGP Fingerprint: 3c7a2b7ba9e2c86d5f0bfc26dc4e536ee3a84b25
PGP Identities:
   Marcello DeSales <marcello.desales@gmail.com>
   Marcello DeSales <marcello@super.cash>
  • Inspect the public key
    • That can be used to past the keys at $GITHUB_HOST/settings/keys GPG keys section.
$ keybase pgp export -q 3c7a2b7ba9e2c86d5f0bfc26dc4e536ee3a84b25
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: https://keybase.io/download
Version: Keybase Go 5.9.0 (darwin)

xsFNBGHYp1cBEACZ7CcPxd7cqKzgn3eYgSra1HUOqRbVM9H8fNNIl5COat/NxZb5
r3WJNHWEyyHkR2CuZuxxp7+QglmSA/iLWOGwXvbhuLMrDgObUlREHAiqs3eBaS/u
...
...
  • Export the public
$ keybase pgp export -q 3c7a2b7ba9e2c86d5f0bfc26dc4e536ee3a84b25 | gpg --import
gpg: key DC4E536EE3A84B25: public key "Marcello DeSales <marcello.desales@gmail.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1

NOTE: You must have the env GPG_TTY=$(tty) so that the prompt shows up in the terminal to import the private key Sometimes the prompt will not show if this is not set

$ keybase pgp export -q 3c7a2b7ba9e2c86d5f0bfc26dc4e536ee3a84b25 --secret | gpg --allow-secret-key-import --import
gpg: key DC4E536EE3A84B25: "Marcello DeSales <marcello.desales@gmail.com>" not changed
gpg: key DC4E536EE3A84B25: secret key imported
gpg: Total number processed: 1
gpg:              unchanged: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1

Associate Email to GPG Key

List secrets

$ gpg --list-secret-key --keyid-form LONG
/Users/marcellodesales/.gnupg/pubring.kbx
-----------------------------------------
sec   rsa4096/DC*****25 2022-01-07 [SC] [expires: 2038-01-03]
      3C7A2B7B***********6EE3A84B25
uid                 [ unknown] Marcello DeSales <marcello.desales@gmail.com>
uid                 [ unknown] Marcello DeSales <marcello@super.cash>
ssb   rsa4096/E73E*******FF 2022-01-07 [E] [expires: 2038-01-03]

Get Public GPG Signature

  • keybase
keybase pgp export -q DC4E******25 | pbcopy
  • gpg
$ gpg --armor --export DC*****B25
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQINBGHYp1cBEACZ7CcPxd7cqKzgn3eYgSra1HUOqRbVM9H8fNNIl5COat/NxZb5
r3WJNHWEyyHkR2CuZuxxp7+QglmSA/iLWOGwXvbhuLMrDgObUlREHAiqs3eBaS/u
...
...
9trpzNMK2HHhLExRbsVf8ZSg5h3s5Pq+mvnimBvfNuHhqgKfIFERgH/YDl4LhHkB
vBXF3x3sBpWVJxutordywK4=
=k2Ne
-----END PGP PUBLIC KEY BLOCK-----

Set up Git to sign all commits

  1. Obtain your signing key via the GPG CLI:
$ gpg --list-secret-keys --keyid-format LONG

/Users/jplew/.gnupg/pubring.kbx
-------------------------------
sec   rsa4096/C8AB98F11Y123456 2018-06-02 [SC] [expires: 2034-05-29]
      B21DBAB6AA037F5641504A8CC2DB56E29C562080
uid                 [ unknown] JP Lew <jp@cto.ai>
ssb   rsa4096/ZZ1Z1234556FAPPO 2018-06-02 [E] [expires: 2034-05-29]

Your signingkey is the 16-character string on the sec line, following rsa4096/.

gpg --list-secret-keys --keyid-format LONG | grep sec  | awk '{ print $2 }' | awk -F"/" '{ print $2 }'
DC4*****25
  1. Add your signing key and user info to your global Git config file. To do this this, you can either:
$ git config --global user.name "Marcello DeSales"
$ git config --global user.email myemail@mydomain.com
$ git config --global user.signingkey DC4*****25
$ git config --global commit.gpgsign true

Update Config ~/.gitconfig

REF: https://stackoverflow.com/questions/4220416/can-i-specify-multiple-users-for-myself-in-gitconfig/43654115#43654115

The final product should look like this:

  • Default config is with open-source email address
  • Work email address stays in separate config
    • cat ~/.gitconfig
    • It can also have its own section for the signing
[user]
    name = Marcello DeSales
    email = marcello.desales@gmail.com
    signingkey = DC4E******B25

[commit]
    gpgsign = true

[tag]
    gpgsign = true

[includeIf "gitdir:~/dev/gitlab.com/supercash/"]
    path = ~/.gitconfig-super.cash

[color]
    ui = true

NOTE: YOU MUST SPECIFY THE includeIf ending with /

  • Config for work address
    • cat ~/.gitconfig-super.cash
[user]
    email = marcello@super.cash

In order to verify, just cd to the dir of your projects

☁️  aws-cli@2.2.32 🔖 aws-iam-authenticator@0.5.3
☸️  kubectl@1.22.4 📛 kustomize@v4.3.0 🎡 helm@3.7.0 👽 argocd@2.2.0 ✈️  glooctl@1.9.0  🐙 docker-compose@1.29.2
👤 AWS_PS1_PROFILE 🗂️   🌎 sa-east-1
🏗  1.21.2-eks-0389ca3 🔐 arn:aws:eks:sa-east-1:806101772216:cluster/eks-ppd-prd-super-cash 🍱 default
~/dev/github.com/marcellodesales/Systems-Design on  master! 📅 01-07-2022 ⌚14:18:19
$ git config user.email
marcello.desales@gmail.com

☁️  aws-cli@2.2.32 🔖 aws-iam-authenticator@0.5.3
☸️  kubectl@1.22.4 📛 kustomize@v4.3.0 🎡 helm@3.7.0 👽 argocd@2.2.0 ✈️  glooctl@1.9.0 🐳 docker@20.10.11 🐙 docker-compose@1.29.2
👤 AWS_PS1_PROFILE 🗂️   🌎 sa-east-1
🏗  1.21.2-eks-0389ca3 🔐 arn:aws:eks:sa-east-1:806101772216:cluster/eks-ppd-prd-super-cash 🍱 default
~/dev/gitlab.com/supercash/apps-web/maceio-shopping-tickets-web on  develop! 📅 01-07-2022 ⌚14:17:43
$ git config user.email
marcello@super.cash

Add your public GPG key to Git Servers

REF: https://docs.gitlab.com/ee/user/project/repository/gpg_signed_commits

  1. Go to the key settings of your repo server
  1. Copy your public key to your clipboard by running:
$ keybase pgp export -q C8A*****456 | pbcopy

Make sure you use your actual signing key.

  1. Paste your key and save.

  2. Test that this worked by signing a git commit and submitting a merge request.

$ cd myrepo
$ git touch newfile.txt
$ git commit -m "make a GPG signed commit"
$ git --no-pager show --show-signature
commit 0d9c2366a58c985b3ce31c32ad092ecf0a0ba378 (HEAD -> feature/pipeline-with-deployment-cloudfront)
gpg: Signature made Fri Jan  7 13:16:19 2022 PST

The commit should show the signed information with gpg: on it...

  1. If you are allowed to create a merge request, it worked.

Trust the Key

Problem

  • commands like git log --show-signature will show the problem
$ git --no-pager show --show-signature
commit 0d9c2366a58c985b3ce31c32ad092ecf0a0ba378 (HEAD -> feature/pipeline-with-deployment-cloudfront)
gpg: Signature made Fri Jan  7 13:16:19 2022 PST
gpg:                using RSA key 3C7A****B25
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Author: Marcello DeSales <marcello.desales@gmail.com>
Date:   Fri Jan 7 13:16:19 2022 -0800

    :memo: README: update title to be shorter

Solution

REF: https://serverfault.com/questions/569911/how-to-verify-an-imported-gpg-key/569923#569923

  • List the key ID
$ gpg --list-secret-keys --keyid-format LONG | grep sec  | awk '{ print $2 }' | awk -F"/" '{ print $2 }'
DC4*****B25
  • Edit the key
$ gpg --edit-key DC4****B25
gpg (GnuPG) 2.3.4; Copyright (C) 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  rsa4096/DC4****25
     created: 2022-01-07  expires: 2038-01-03  usage: SC
     trust: unknown       validity: unknown
ssb  rsa4096/E73******7FF
     created: 2022-01-07  expires: 2038-01-03  usage: E
[ unknown] (1). Marcello DeSales <marcello.desales@gmail.com>
[ unknown] (2)  Marcello DeSales <marcello@super.cash>
  • On the prompt, type trust
gpg> trust
sec  rsa4096/DC4*****25
     created: 2022-01-07  expires: 2038-01-03  usage: SC
     trust: unknown       validity: unknown
ssb  rsa4096/E73EBAAEC18957FF
     created: 2022-01-07  expires: 2038-01-03  usage: E
[ unknown] (1). Marcello DeSales <marcello.desales@gmail.com>
[ unknown] (2)  Marcello DeSales <marcello@super.cash>

Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)

  1 = I don't know or won't say
  2 = I do NOT trust
  3 = I trust marginally
  4 = I trust fully
  5 = I trust ultimately
  m = back to the main menu

Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y

sec  rsa4096/DC4****25
     created: 2022-01-07  expires: 2038-01-03  usage: SC
     trust: ultimate      validity: unknown
ssb  rsa4096/E73****7FF
     created: 2022-01-07  expires: 2038-01-03  usage: E
[ unknown] (1). Marcello DeSales <marcello.desales@gmail.com>
[ unknown] (2)  Marcello DeSales <marcello@super.cash>
Please note that the shown key validity is not necessarily correct
unless you restart the program.

gpg> q

Verify signed commits

  • Set the commit prompt to ask for the passthrase of signature once it finishes the commit
export GPG_TTY=$(tty)`
  • Make a new commit... It will ask for the passphrase of the key
	       ┌───────────────────────────────────────────────────────────────┐
	       │ Please enter the passphrase to unlock the OpenPGP secret key: │
	       │ "Marcello DeSales <marcello.desales@gmail.com>"               │
	       │ 4096-bit RSA key, ID DC*****25,                               │
	       │ created 2022-01-07.                                           │
	       │                                                               │
	       │                                                               │
	       │ Passphrase: _________________________________________________ │
	       │                                                               │
	       │         <OK>                                   <Cancel>       │
	       └───────────────────────────────────────────────────────────────┘
  • Upon entering the passphrase, the commit works... Now you can verify it is signed
# https://stackoverflow.com/questions/3357280/print-commit-message-of-a-given-commit-in-git/54886099#54886099
$ git --no-pager show --show-signature -s --format=%s
gpg: Signature made Sun Jul  3 19:08:06 2022 PDT
gpg:                using RSA key 33F17EFF7BD0A66A8C533A41A0CDC02D297930BF
gpg: Good signature from "Marcello DeSales <marcello.desales@viasat.com>" [ultimate]
gpg:                 aka "Marcello DeSales <marcello.desalesjr@viasat.com>" [ultimate]
:tada: :memo: Add readme initial for the project

$ git --no-pager show --show-signature
commit 0d9c2366a58c985b3ce31c32ad092ecf0a0ba378 (HEAD -> feature/pipeline-with-deployment-cloudfront)
gpg: Signature made Fri Jan  7 13:16:19 2022 PST
gpg:                using RSA key 3C7A2B7BA9*******84B25
gpg: Good signature from "Marcello DeSales <marcello.desales@gmail.com>" [ultimate]
gpg:                 aka "Marcello DeSales <marcello@super.cash>" [ultimate]
Author: Marcello DeSales <marcello.desales@gmail.com>
Date:   Fri Jan 7 13:16:19 2022 -0800

    :memo: README: update title to be shorter

diff --git a/README.md b/README.md
index 28748c1..652b6be 100644
--- a/README.md
+++ b/README.md

Optional: Manage your GPG keys using GPG Suite

Install the GPG Suite, available from gpgtools.org, or from brew by running:

$ brew install --cask gpg-suite

Once installed, open Spotlight and search for "GPGPreferences", or open system preferences and select "GPGPreferences"

Select the Default Key if it is not already selected, and ensure "Store in OS X Keychain" is checked (see reference image above):

The gpg-agent.conf is different from Method 1:

Set up the agent:

$ $EDITOR ~/.gnupg/gpg-agent.conf
# GPG Suite should pre-populate with something similar to the following:
default-cache-ttl 600
max-cache-ttl 7200

Keybase Verifications

Keybase Encrypt/Decrypt, Sign/Verify

Backup and Transfer GPG keys - Export/Import GPG Keys

Steps described at https://serverfault.com/questions/86048/how-to-backup-gpg/1040984#1040984

When installing the public/private keys in a new computer. Make sure to back them up and store them in a secure location.

  • List the existing keys before transferring to another computer
  • Export all what needs:
    • public key
    • private key
    • sub private keys
    • ownership
  • Verify the keys

Here's the current list of keys

$ gpg -k
/Users/marcellodesales/.gnupg/pubring.kbx
-----------------------------------------
pub   rsa4096 2022-01-07 [SC] [expires: 2038-01-03]
      3C7A2B7BA9E2C86D5F0BFC26DC4E536EE3A84B25
uid           [ultimate] Marcello DeSales <marcello.desales@gmail.com>
uid           [ultimate] Marcello DeSales <marcello@super.cash>
sub   rsa4096 2022-01-07 [E] [expires: 2038-01-03]

Export the Public Key

  • Choose any of the identifications of the key such as the name or email
$ gpg --export --armor marcello.desales@gmail.com  > marcello.desales@gmail.com.public.asc
$ cat ~/marcello.desales@gmail.com.pub.asc | head -5 && echo "...\n...\n" && cat ~/marcello.desales@gmail.com.pub.asc | tail -5
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQINBGHYp1cBEACZ7CcPxd7cqKzgn3eYgSra1HUOqRbVM9H8fNNIl5COat/NxZb5
r3WJNHWEyyHkR2CuZuxxp7+QglmSA/iLWOGwXvbhuLMrDgObUlREHAiqs3eBaS/u
ZMLnX8zMlfi0YtAuiQTkayDNCYNTev138E+JGo58eCUEMUe+USTzmz8j9sHojuCD
...
...

fJREULTai3kRHgHSNLrd8I04beMeGYmJwOcXe3fIO4hJt7PP6X0F3Q4B+i/zaP+4
9trpzNMK2HHhLExRbsVf8ZSg5h3s5Pq+mvnimBvfNuHhqgKfIFERgH/YDl4LhHkB
vBXF3x3sBpWVJxutordywK4=
=k2Ne
-----END PGP PUBLIC KEY BLOCK-----

Export the private key

  • Choose any of the identifications of the key such as the name or email
$ gpg --export-secret-keys --armor marcello.desales@gmail.com > marcello.desales@gmail.com.priv.asc
$ cat ~/marcello.desales@gmail.com.priv.asc | head -5 && echo "...\n...\n" && cat ~/marcello.desales@gmail.com.priv.asc | tail -5

-----BEGIN PGP PRIVATE KEY BLOCK-----

lQdGBGHYp1cBEACZ7CcPxd7cqKzgn3eYgSra1HUOqRbVM9H8fNNIl5COat/NxZb5
r3WJNHWEyyHkR2CuZuxxp7+QglmSA/iLWOGwXvbhuLMrDgObUlREHAiqs3eBaS/u
ZMLnX8zMlfi0YtAuiQTkayDNCYNTev138E+JGo58eCUEMUe+USTzmz8j9sHojuCD
...
...

3fCNOG3jHhmJicDnF3t3yDuISbezz+l9Bd0OAfov82j/uPba6czTCthx4SxMUW7F
X/GUoOYd7OT6vpr54pgb3zbh4aoCnyBREYB/2A5eC4R5AbwVxd8d7AaVlScbraK3
csCu
=d+0l
-----END PGP PRIVATE KEY BLOCK-----

Export the sub private keys

$ gpg --export-secret-subkeys --armor  marcello.desales@gmail.com > marcello.desales@gmail.com.sub_priv.asc
$ cat ~/marcello.desales@gmail.com.sub_priv.asc | head -5 && echo "...\n...\n" && cat ~/marcello.desales@gmail.com.sub_priv.asc | tail -5
-----BEGIN PGP PRIVATE KEY BLOCK-----

lQIVBGHYp1cBEACZ7CcPxd7cqKzgn3eYgSra1HUOqRbVM9H8fNNIl5COat/NxZb5
r3WJNHWEyyHkR2CuZuxxp7+QglmSA/iLWOGwXvbhuLMrDgObUlREHAiqs3eBaS/u
ZMLnX8zMlfi0YtAuiQTkayDNCYNTev138E+JGo58eCUEMUe+USTzmz8j9sHojuCD
...
...

MnyURFC02ot5ER4B0jS63fCNOG3jHhmJicDnF3t3yDuISbezz+l9Bd0OAfov82j/
uPba6czTCthx4SxMUW7FX/GUoOYd7OT6vpr54pgb3zbh4aoCnyBREYB/2A5eC4R5
AbwVxd8d7AaVlScbraK3csCu
=4eYm
-----END PGP PRIVATE KEY BLOCK-----

Export the ownertrust settings

$ gpg --export-ownertrust > ownertrust.txt
$ cat ownertrust.txt
# List of assigned trustvalues, created Fri May 24 18:52:42 2024 PDT
# (Use "gpg --import-ownertrust" to restore them)
3C7A2B7BA9E2C86D5F0BFC26DC4E536EE3A84B25:6:

Import GPG Keys

  • Start from the public key
$ gpg --import marcello.desales@gmail.com.pub.asc
gpg: directory '/Users/marcellodesales/.gnupg' created
gpg: /Users/marcellodesales/.gnupg/trustdb.gpg: trustdb created
gpg: key DC4E536EE3A84B25: public key "Marcello DeSales <marcello.desales@gmail.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1

ATTENTION: If you try to add the private key, you will get errors.

  • Add the ownership trust first, then, add the private key... Skip this step
~ ⌚ 18:54:47
$ gpg --import marcello.desales@gmail.com.priv.asc
gpg: key DC4E536EE3A84B25: "Marcello DeSales <marcello.desales@gmail.com>" not changed
gpg: key DC4E536EE3A84B25/DC4E536EE3A84B25: error sending to agent: Bad passphrase
gpg: error building skey array: Bad passphrase
gpg: error reading 'marcello.desales@gmail.com.priv.asc': Bad passphrase
gpg: import from 'marcello.desales@gmail.com.priv.asc' failed: Bad passphrase
gpg: Total number processed: 0
gpg:              unchanged: 1
gpg:       secret keys read: 1
  • Add the ownertrust
$ gpg --import-ownertrust ownertrust.txt
gpg: inserting ownertrust of 6
  • Then, add the sub private keys first
$ gpg --import marcello.desales@gmail.com.sub_priv.asc
gpg: key DC4E536EE3A84B25: "Marcello DeSales <marcello.desales@gmail.com>" not changed
gpg: To migrate 'secring.gpg', with each smartcard, run: gpg --card-status
gpg: key DC4E536EE3A84B25: secret key imported
gpg: Total number processed: 1
gpg:              unchanged: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1
  • Attempting to add the private keys
$ gpg --import marcello.desales@gmail.com.priv.asc
gpg: key DC4E536EE3A84B25: "Marcello DeSales <marcello.desales@gmail.com>" not changed
gpg: key DC4E536EE3A84B25: secret key imported
gpg: Total number processed: 1
gpg:              unchanged: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1
gpg:  secret keys unchanged: 1
  • Show the keys
$ gpg --list-secret-keys --keyid-format LONG
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2038-01-03
[keyboxd]
---------
sec   rsa4096/DC4E536EE3A84B25 2022-01-07 [SC] [expires: 2038-01-03]
      3C7A2B7BA9E2C86D5F0BFC26DC4E536EE3A84B25
uid                 [ultimate] Marcello DeSales <marcello.desales@gmail.com>
uid                 [ultimate] Marcello DeSales <marcello@super.cash>
ssb   rsa4096/E73EBAAEC18957FF 2022-01-07 [E] [expires: 2038-01-03]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment