pkg/sftp is the SFTP client and server Go programs actually ship
SFTP support for the go.crypto/ssh package
At a glance
- What is it?
- The pkg/sftp package implements the SFTP subsystem for Go, with a client whose operations mirror the os package and a server that can serve files from a local filesystem, plus a separate request server with its own interface. The project is in the middle of a 2.0 rewrite and keeps an explicit roadmap that admits the duplication in the client API.
- Who is it for?
- Adopt pkg/sftp when a Go program needs to move files over SSH and you would rather not shell out to the sftp binary, and you can live with an API modelled on os rather than on a richer client. Do not adopt the 2.0 line until the prereleases settle, since v2.0.0 is still in alpha while the maintained line is 1.13.
- Can I use it commercially?
- Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A client and a server in one package
The sftp package provides support for file system operations on remote ssh servers using the SFTP subsystem. It also implements an SFTP server for serving files from the filesystem.
Both halves in one package is unusual and is the first thing to understand about the project. Most SFTP libraries are clients only, because a client is what applications need. A server is what you need when you are building a service that exposes a filesystem over SSH, and having it here means you can write a test server and a production client against the same implementation.
The API shape follows that of the standard library. The basic operation of the package mirrors the facilities of the os package, so a reader who knows os.Open and os.Stat has a good idea of what to expect, and the documentation points at pkg.go.dev for the examples.
Directory traversal has its own interface, the Walker, and the readme says it is heavily inspired by Keith Rarick's fs package, which is the well known Go filesystem walking library.
The implementation is Go, the licence is BSD-2-Clause, the module path is github.com/pkg/sftp, and the last push was on 2026-10-18, which makes it one of the more actively maintained SFTP libraries in the ecosystem.
The roadmap admits the client API is repetitive
The roadmap section is short and unusually frank.
There is way too much duplication in the Client methods. If there was an unmarshal(interface{}) method this would reduce a heap of the duplication.
That is a maintainer describing a known problem in their own API, which is a good sign about the project's honesty and a useful warning about the API you are adopting. The client has a method for every operation, and each one has to do the same work of sending a request, waiting for a reply and decoding the result, so the code is large and the surface is wide.
The proposed fix is worth understanding as a design direction rather than a promise. A single unmarshal method that takes an interface would mean one place that does the request and response handling, with each operation becoming a small wrapper that describes what it wants. That is the direction most Go clients in this space have gone, and it is the direction this one says it wants to go.
So the trade for a user today is breadth against consistency. There is a method for the operation you need, and there is repetition underneath every call you make. Whether that matters depends on how much of the API you use, and a program that opens a file and reads it is not going to notice.
A 2.0 in alpha and a 1.x that is still maintained
The release history has two lines running at once, and the naming of the 2.0 prereleases is worth reading.
The recent releases are v1.13.11, described as a maintenance release, from 2026-07-12, v2.0.0-alpha2 from 2026-05-04, and v2.0.0-alpha from 2026-01-22, whose title is A Proper Prerelease.
That last title is a small joke with a point to it. A proper prerelease, in semver terms, is one where the version sorts before the final release, so v2.0.0-alpha is less than v2.0.0 and any tool that asks for v2.0.0 will not be given the alpha by accident.
So the picture is a maintained 1.13 line for people who want stability and a 2.0 line in alpha for people who want to test the rewrite. The rewrite is the 2.0 one, and the roadmap entry about the client duplication is the kind of change that a major version exists to accommodate.
The practical advice is the usual one, and it is worth being explicit about because the version numbers are close together. If you are starting a new project, take the 2.0 alphas if you can tolerate churn in the API, and pin them. If you are putting this into something that has to work, stay on the 1.13 line and take the maintenance releases.
The request server is a second thing entirely
The repository contains two server implementations, and they are different in kind.
There is server.go, which is the SFTP server for serving files from the filesystem, and there is request-server.go with a set of request interfaces alongside it, and a server_standalone/ directory. The examples directory has a go-sftp-server example and a request-server example, one for each.
The request server is the part that looks like a middleware. Instead of exposing a directory, it exposes a set of operations that the implementation provides, and the client calls those operations. That is what you want when the thing you are serving is not a filesystem but an abstraction over one, and it is why the package carries the word request in half a dozen file names.
There is a request-attrs.go for the attributes passed with a request, request-errors.go for the error type, request-typing.go and request-interfaces.go for the interface definitions, and request-example.go showing how to write one. The readme file request-readme.md sits alongside them.
So the package offers three shapes rather than two. A client for talking to a remote server, a plain filesystem server, and a request server that serves a custom interface over the same protocol.
The test matrix is driven by build tags, not by environment
The Makefile is the clearest document of how this library is tested, and the design is unusual enough to be worth reading closely.
The integration target runs the suite six times, in combinations.
go test -integration -v ./...
go test -testserver -v ./...
go test -integration -testserver -v ./...And then the same three combinations with the allocator flag added, so every feature is exercised both with the pooled allocator and without it.
The flags are build tags, not environment variables, which means the tests that need an integration environment, that need a test server, or that need the alternative allocator are compiled into separate binaries and selected at compile time. The alternative allocator is a real thing in this codebase, with allocator.go and allocator_test.go at the top level, and a test that only runs against the default one would leave the other one untested.
The race variants do the same six combinations with -race added, and the benchmark target runs with -run=NONE so only the benchmarks execute, with a count variable and a pattern variable to select which ones.
The memory profile target is a nice touch. It runs the same benchmarks and adds a memprofile output, then pipes it through go tool pprof to produce an SVG of the allocations.
For a protocol library, this is the right shape of testing, because the interesting bugs are in the interaction between the request handling and the buffer management, and the combinations are where those live.
Platform support shows up as file suffixes
The top level listing is long, and a pattern runs through it.
There are errno_plan9.go and errno_posix.go for the error numbers, ls_plan9.go, ls_unix.go and ls_stub.go for the directory listing formats, request_plan9.go, request_unix.go and request_windows.go for the platform parts of the request handling, server_plan9.go and server_posix.go for the server, and attrs_stubs.go alongside attrs_unix.go. There is a server_nowindows_test.go, which is a test file that only exists on the platforms where the server works.
That is a lot of files for a protocol implementation, and it is the cost of a package that runs everywhere without cgo. The listing formats in particular differ between systems, and the Plan 9 stubs are there so the package still compiles there even if the support is minimal.
The rest of the tree is the protocol itself. There is conn.go for the connection, packet.go, packet-manager.go and packet-typing.go for the wire format, request.go and request-errors.go for the request layer, client.go and server.go for the two ends, pool.go for connection pooling, match.go for the path matching, ls_formatting.go for the human readable listing, and debug.go, fuzz.go and release.go.
The dependency list is four modules, with kr/fs as the reference for the Walker interface, stretchr/testify for the assertions, and the x/crypto and x/sys modules. The module targets Go 1.25.0.
Contributing, and a policy of zero open issues
The contributing section is short, and the second half of it is the part worth quoting.
Before proposing a large change, first please discuss your change by raising an issue. For API and code bugs, please include a small, self contained code example to reproduce the issue, and for pull requests, remember test coverage.
Then there is the policy. We try to handle issues and pull requests with a 0 open philosophy. That means we will try to address the submission as soon as possible and will work toward a resolution. If progress can no longer be made, for example an unreproducible bug, or stops, for example an unresponsive submitter, we will close the bug.
A zero open policy is unusual and it is worth understanding what it means before you rely on it. It is not a promise that every issue is fixed, it is a promise that nothing is left open indefinitely. An issue that is waiting for information from the reporter will be closed rather than left open, and the closing is how the maintainers say they have stopped waiting.
For a user filing a bug, the practical implication is to send a complete report the first time, because a report that needs a follow up question may be closed before you answer. The repository also has a SECURITY.md for anything that should not go in the public tracker, and a CONTRIBUTORS file and a licence file at the top.
Editorial conclusion
Adopt pkg/sftp when a Go program needs to move files over SSH and you would rather not shell out to the sftp binary, and you can live with an API modelled on os rather than on a richer client. Do not adopt the 2.0 line until the prereleases settle, since v2.0.0 is still in alpha while the maintained line is 1.13. Verify first which version you depend on, since the 1.x and 2.x APIs differ, and that you understand the request server is a separate construct from the filesystem server, because the examples directory ships both.
Frequently asked questions
What does the pkg/sftp package provide?
It provides support for file system operations on remote ssh servers using the SFTP subsystem, and it also implements an SFTP server for serving files from the filesystem. The basic operation of the package mirrors the facilities of the os package.
Which version of pkg/sftp should I use?
The 1.13 line is the maintained one, with v1.13.11 as a maintenance release in July 2026, while 2.0.0 is still in alpha. The 2.0 line is the rewrite, so start there only if you can tolerate API churn, and pin the version either way.
What is the request server in pkg/sftp?
It is a second kind of server alongside the filesystem one, where the implementation provides a set of operations through the request interfaces rather than exposing a directory. The examples directory ships a go-sftp-server example and a request-server example, one for each.
How is pkg/sftp tested?
With build tag selected combinations. The Makefile runs the suite with the integration, testserver and allocator flags in every combination, and again with the race detector, plus benchmark targets that can emit a memory profile through go tool pprof.
What is the known weakness of the pkg/sftp client API?
The roadmap says there is way too much duplication in the Client methods, and that an unmarshal(interface{}) method would reduce a heap of that duplication. It is a known problem the project intends to address in the 2.0 line.
How do directory traversal and dependencies work in pkg/sftp?
The Walker interface for directory traversal is heavily inspired by Keith Rarick's fs package. The module depends on kr/fs, stretchr/testify, golang.org/x/crypto and golang.org/x/sys, and targets Go 1.25.0.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pkg-sftp)Was this useful?