Git and libgit2

Git LFS SSH Authentication

Published 2025-01-06. Last modified 2025-01-18.
Time to read: 5 minutes.

This page is part of the git collection.

I have published 8 articles about the Git large file system (LFS). They are meant to be read in order.

  1. Git Large File System Overview
  2. Git LFS Scripts
  3. Git LFS Client Installation
  4. Git LFS Server URLs
  5. Git-ls-files, Wildmatch Patterns and Permutation Scripts
  6. Git LFS Tracking, Migration and Un-Migration
  7. Git LFS Client Configuration & Commands
  8. Git LFS SSH Authentication
  9. Working With Git LFS
  10. Evaluation Procedure For Git LFS Servers
  11. Git LFS server tests:
    1. Null Git LFS Server

6 articles are still in process.

Instructions for typing along are given for Ubuntu and WSL/Ubuntu. If you have a Mac, most of this information should be helpful.

This Article Probably Contains Errors

This article is incomplete and may contain errors. It has been published to allow collaboration with fact-checkers. Not all of these scenarios might make sense to attempt to review. Do not rely on this information yet.

Although I built the software as shown below, I have not needed it for testing yet. I will have more to say later.

Two Approaches

There are two possible approaches for using Git LFS over SSH.

  1. The older form uses a program called git-lfs-authenticate, which provides authentication for an HTTP server, and then the data is uploaded over HTTP or HTTPS. This would be an unusual setup for a local server, and is suboptimal for a remote server.
  2. The Git LFS client v3.0, released 3 years ago on 2021-09-24, supports the Git LFS SSH protocol via the git-lfs-transfer program. Git-lfs-transfer has two implementations: the original Rust implementation that does not implement locking, and a Go implementation that does implement locking.

    If Git LFS detects an SSH remote, it will run the git-lfs-authenticate command. This allows supporting Git servers to give the Git LFS client alternative authentication, so the user does not have to set up a Git credential helper.

The git-lfs-transfer command is generally the preferred Git LFS authentication option for the SSH protocol. Here are a few words about each of the two implementations:

  1. Scutiger published the original version of git-lfs-transfer on 2019-03-09. Within that repository, the scutiger-lfs subdirectory provides Git LFS-specific utilities. This repository is written in Rust, has been forked 7 times, starred 25 times and is watched by 6 GitHub users.
  2. Charmbracelet ported the Rust code written by scutiger to the Go language on 2022-12-09 and has since been forked 6 times. This project has been starred 62 times and is watched by 5 GitHub users.

Both versions are easy to install. An examination of the source code showed me that the Go version implements locking, while the Rust version does not. Neither version has good documentation, but the Go version has slightly worse (less) documentation than the Rust version.

Charmbracelet’s Go version has more than 250% more stars, even though it is only half as old. Although I think this is the version to use, the rest of this section shows you how to work with both versions.

Building git-lfs-transfer

Git-lfs-transfer runs on the Git LFS server, so it should be built on that machine. In the next two sections, I show how to build both the Go and the Rust versions. You can build both if you are so inclined. After they are built, you must choose one implementation.

Building Rust Version

You will need Rust 1.63 or later, Cargo, GNU Make, and Git. If you additionally have zlib, the 8-bit version of libpcre2, and libgit2 available, Scutiger will dynamically link against them. This is highly recommended for security reasons.
 – From Scutiger README

The build process is very easy:

Shell
mslinn@gojira ~ $ cargo install \
--git https://github.com/bk2204/scutiger.git \
scutiger-lfs
Updating git repository `https://github.com/bk2204/scutiger.git`
Installing scutiger-lfs v0.3.0 (https://github.com/bk2204/scutiger.git#614a4f01)
Updating crates.io index
Locking 98 packages to latest compatible versions
Adding bytes v0.4.12 (available: v1.9.0)
Adding clap v2.34.0 (available: v4.5.23)
Adding digest v0.9.0 (available: v0.10.7)
Adding git2 v0.16.1 (available: v0.19.0)
Adding sha2 v0.9.9 (available: v0.10.8)
Downloaded autocfg v1.4.0
Downloaded icu_provider_macros v1.5.0
Downloaded cpufeatures v0.2.16
Downloaded zerofrom v0.1.5
Downloaded write16 v1.0.0
Downloaded errno v0.3.10
Downloaded fastrand v2.3.0
Downloaded displaydoc v0.2.5
Downloaded idna_adapter v1.2.0
Downloaded zerovec-derive v0.10.3
Downloaded utf8_iter v1.0.4
Downloaded synstructure v0.13.1
Downloaded yoke-derive v0.7.5
Downloaded version_check v0.9.5
Downloaded zerofrom-derive v0.1.5
Downloaded writeable v0.5.5
Downloaded pkg-config v0.3.31
Downloaded jobserver v0.1.32
Downloaded tinystr v0.7.6
Downloaded yoke v0.7.5
Downloaded quote v1.0.38
Downloaded once_cell v1.20.2
Downloaded litemap v0.7.4
Downloaded icu_normalizer v1.5.0
Downloaded unicode-ident v1.0.14
Downloaded tempfile v3.14.0
Downloaded icu_provider v1.5.0
Downloaded bitflags v2.6.0
Downloaded url v2.5.4
Downloaded serde v1.0.217
Downloaded icu_collections v1.5.0
Downloaded idna v1.0.3
Downloaded zerovec v0.10.4
Downloaded icu_properties v1.5.1
Downloaded icu_normalizer_data v1.5.0
Downloaded num-traits v0.2.19
Downloaded icu_properties_data v1.5.0
Downloaded vcpkg v0.2.15
Downloaded unicode-width v0.1.14
Downloaded syn v2.0.93
Downloaded chrono v0.4.39
Downloaded icu_locid_transform_data v1.5.0
Downloaded proc-macro2 v1.0.92
Downloaded log v0.4.22
Downloaded icu_locid v1.5.0
Downloaded rustix v0.38.42
Downloaded cc v1.2.6
Downloaded icu_locid_transform v1.5.0
Downloaded iana-time-zone v0.1.61
Downloaded stable_deref_trait v1.2.0
Downloaded utf16_iter v1.0.5
Downloaded libc v0.2.169
Downloaded libz-sys v1.1.20
Downloaded linux-raw-sys v0.4.14
Downloaded 54 crates (6.7 MB) in 0.28s (largest was `linux-raw-sys` at 1.8 MB)
Compiling proc-macro2 v1.0.92
Compiling unicode-ident v1.0.14
Compiling libc v0.2.169
Compiling stable_deref_trait v1.2.0
Compiling writeable v0.5.5
Compiling litemap v0.7.4
Compiling shlex v1.3.0
Compiling pkg-config v0.3.31
Compiling icu_locid_transform_data v1.5.0
Compiling icu_properties_data v1.5.0
Compiling vcpkg v0.2.15
Compiling version_check v0.9.5
Compiling typenum v1.17.0
Compiling smallvec v1.13.2
Compiling write16 v1.0.0
Compiling utf16_iter v1.0.5
Compiling utf8_iter v1.0.4
Compiling icu_normalizer_data v1.5.0
Compiling autocfg v1.4.0
Compiling percent-encoding v2.3.1
Compiling bitflags v1.3.2
Compiling rustix v0.38.42
Compiling bitflags v2.6.0
Compiling log v0.4.22
Compiling unicode-width v0.1.14
Compiling cfg-if v1.0.0
Compiling linux-raw-sys v0.4.14
Compiling byteorder v1.5.0
Compiling fastrand v2.3.0
Compiling cpufeatures v0.2.16
Compiling opaque-debug v0.3.1
Compiling form_urlencoded v1.2.1
Compiling iana-time-zone v0.1.61
Compiling once_cell v1.20.2
Compiling hex v0.4.3
Compiling generic-array v0.14.7
Compiling textwrap v0.11.0
Compiling num-traits v0.2.19
Compiling clap v2.34.0
Compiling quote v1.0.38
Compiling syn v2.0.93
Compiling chrono v0.4.39
Compiling jobserver v0.1.32
Compiling iovec v0.1.4
Compiling passwd v0.0.1
Compiling bytes v0.4.12
Compiling cc v1.2.6
Compiling block-buffer v0.9.0
Compiling digest v0.9.0
Compiling sha2 v0.9.9
Compiling tempfile v3.14.0
Compiling libz-sys v1.1.20
Compiling libgit2-sys v0.14.2+1.5.1
Compiling synstructure v0.13.1
Compiling zerofrom-derive v0.1.5
Compiling yoke-derive v0.7.5
Compiling zerovec-derive v0.10.3
Compiling displaydoc v0.2.5
Compiling icu_provider_macros v1.5.0
Compiling zerofrom v0.1.5
Compiling yoke v0.7.5
Compiling zerovec v0.10.4
Compiling tinystr v0.7.6
Compiling icu_collections v1.5.0
Compiling icu_locid v1.5.0
Compiling icu_provider v1.5.0
Compiling icu_locid_transform v1.5.0
Compiling icu_properties v1.5.1
Compiling icu_normalizer v1.5.0
Compiling idna_adapter v1.2.0
Compiling idna v1.0.3
Compiling url v2.5.4
Compiling git2 v0.16.1
Compiling scutiger-core v0.3.0 (/home/mslinn/.cargo/git/checkouts/scutiger-b9c2aae4f2fdd878/614a4f0/scutiger-core)
Compiling scutiger-lfs v0.3.0 (/home/mslinn/.cargo/git/checkouts/scutiger-b9c2aae4f2fdd878/614a4f0/scutiger-lfs)
Finished `release` profile [optimized] target(s) in 8.55s
Installing /home/mslinn/.cargo/bin/git-lfs-transfer
Installed package `scutiger-lfs v0.3.0 (https://github.com/bk2204/scutiger.git#614a4f01)` (executable `git-lfs-transfer`) 

The build process stores the built code in ~/.cargo/bin/:

Shell
mslinn@gojira scutiger $ ls ~/.cargo/bin
git-lfs-transfer* 

The help information is incomplete:

Shell
mslinn@gojira scutiger $ git-lfs-transfer -h
git-lfs-transfer
Implement the remote side of a Git LFS SSH transfer
USAGE: git-lfs-transfer <path> <operation>
FLAGS: -h, --help Prints help information -V, --version Prints version information
ARGS: <path> <operation>

Building Go Version

Shell
mslinn@gojira ~ $ go install \
github.com/charmbracelet/git-lfs-transfer@latest
go: downloading github.com/charmbracelet/git-lfs-transfer v0.1.0
go: downloading github.com/git-lfs/git-lfs/v3 v3.3.0
go: downloading github.com/git-lfs/pktline v0.0.0-20210330133718-06e9096e2825
go: downloading golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1
go: downloading github.com/rubyist/tracerx v0.0.0-20170927163412-787959303086
go: downloading github.com/git-lfs/wildmatch/v2 v2.0.1
go: downloading github.com/git-lfs/gitobj/v2 v2.1.1
go: downloading github.com/leonelquinteros/gotext v1.5.0
go: downloading github.com/pkg/errors v0.0.0-20170505043639-c605e284fe17 

The go install command places executables in ~/go/bin/

Shell
mslinn@gojira ~ $ ls ~/go/bin
git-lfs-transfer* 

The help information for the Go version is even less helpful than the Rust version’s help information:

Shell
mslinn@gojira ~ $ ~/go/bin/git-lfs-transfer
Git LFS SSH transfer agent

Usage:
git-lfs-transfer PATH OPERATION

expected 2 arguments, got 0 

Installing git-lfs-transfer

The git-lfs-transfer must be installed in the PATH of the Git LFS server. You can discover the value of the PATH as follows. This incantation can be run from any machine, be it client or server:

Shell
mslinn@bear ~ $ ssh mslinn@gojira 'echo $PATH'
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin 

I prefer to use a symbolic link from /usr/local/bin/, so if git-lfs-transfer is ever updated, the link will continue to work.

Installing Rust Version

Shell
mslinn@gojira scutiger $ sudo ln -s \
"$HOME/go/bin/git-lfs-transfer" \
/usr/local/bin/git-lfs-transfer
mslinn@gojira scutiger $ which git-lfs-transfer /usr/local/bin/git-lfs-transfer
mslinn@gojira scutiger $ ssh mslinn@gojira which git-lfs-transfer /usr/local/bin/git-lfs-transfer

Installing Go Version

Shell
mslinn@gojira scutiger $ sudo ln -s \
"$HOME/.cargo/bin/git-lfs-transfer" \
/usr/local/bin/git-lfs-transfer
mslinn@gojira scutiger $ which git-lfs-transfer /usr/local/bin/git-lfs-transfer
mslinn@gojira scutiger $ ssh mslinn@gojira which git-lfs-transfer /usr/local/bin/git-lfs-transfer

Testing git-lfs-transfer

This section is incomplete and probably has errors. It has been published to allow collaboration with fact-checkers. Do not rely on this information yet.

Once installed, the git-lfs-transfer program is automatically run on the server as required. Users do not need to know it exists. The rest of this section is only provided for those who might be curious about further details.

When the SSH protocol is used, Git clients with the Git LFS extension run the following command to authenticate:

Shell
mslinn@gojira scutiger $ ssh [{user}@]{server} \
git-lfs-transfer {path} {operation}

The user, server, and path properties are taken from the SSH remote, stored in the user’s copy of the repository in .git/config. The {operation} can either be download or upload. If a Git client is used, the SSH command can be tweaked with the GIT_SSH or GIT_SSH_COMMAND environment variables.

Let's try it now. This will give us confidence that git-lfs-transfer is properly set up, so Git LFS will be able to work with it without problems. The following incantation can be invoked from any server, be it client or server. I provided the fully qualified path to the bare Git repository on the server (/mnt/_/work/git/eval_bare_repo). The Git client with the Git LFS extension must also provide this path.

Shell
mslinn@bear ~ $ ssh mslinn@gojira git-lfs-transfer \
/mnt/_/work/git/eval_bare_repo download
000eversion=1
000clocking
0000 ^C

I am stuck here. Hopefully one of the reviewers will be able to provide some insight.

Using git-lfs-transfer

This section is incomplete and probably has errors. It has been published to allow collaboration with fact-checkers. Do not rely on this information yet.

I typed the following from the top-level directory within a Git repository:

Shell
mslinn@bear eval_bare_repo $ git config -f .lfsconfig lfs.url \
ssh://mslinn@gojira/mnt/_/work/git/eval_bare_repo

Then .lfsconfig will have at least the following:

.lfsconfig
[lfs]
url = ssh://mslinn@gojira/mnt/_/work/git/eval_bare_repo

Which means the Git command-line client will issue a command something like the following when fetching an LFS file:

Shell
$ ssh mslinn@gojira git-lfs-transfer /mnt/_/work/git/eval_bare_repo download

When I type the above incantation, the response is:

Shell
000eversion=1
000clocking
0000

And then the program appears to pause, awaiting input.

I am stuck here. Hopefully one of the reviewers will be able to provide some insight.

I have published 8 articles about the Git large file system (LFS). They are meant to be read in order.

  1. Git Large File System Overview
  2. Git LFS Scripts
  3. Git LFS Client Installation
  4. Git LFS Server URLs
  5. Git-ls-files, Wildmatch Patterns and Permutation Scripts
  6. Git LFS Tracking, Migration and Un-Migration
  7. Git LFS Client Configuration & Commands
  8. Git LFS SSH Authentication
  9. Working With Git LFS
  10. Evaluation Procedure For Git LFS Servers
  11. Git LFS server tests:
    1. Null Git LFS Server

6 articles are still in process.

Instructions for typing along are given for Ubuntu and WSL/Ubuntu. If you have a Mac, most of this information should be helpful.

* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.