JFrog CLI: the single binary that fronts the whole JFrog platform
JFrog CLI is a client that provides a simple interface that automates access to the JFrog products.
At a glance
- What is it?
- Written in Go and shipped as the jf command, JFrog CLI is the client that automates access to JFrog products across a dozen package ecosystems, and its weekly releases and pinned dependency set tell you how tightly it tracks the platform it serves.
- Who is it for?
- JFrog CLI is the right tool for a team that already runs JFrog Artifactory and wants the same repository operations from a shell, a Makefile and a CI job, and for a platform engineer who needs upload, download and promotion behaviour to be identical on a laptop and on a build agent.
- Can I use it commercially?
- Yes. Apache-2.0 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 last received commits 2 days ago.
- 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
What the client is for and who reaches for it
The project describes itself in one line: JFrog CLI is a client that provides a simple interface that automates access to the JFrog products. That word client is doing real work. This is not a server, and it is not a repository implementation. It is a command line front end for a platform that already exists, and its value is in turning a set of HTTP APIs into commands that a person can type and a CI script can call.
The audience is narrow and obvious once the platform is named. Engineers who already keep their artifacts in JFrog Artifactory, and who need the same operation from a terminal, from a Docker build step and from a Jenkins pipeline, without writing a script that speaks REST with a signed request. The repository layout shows the same audience in the file names, since the top level carries a separate test file for nearly every package type the platform handles.
The binary is called jf, which the Makefile confirms when it describes building jf with different OpenAPI spec bundles. A single name across every product is convenient for a pipeline, because the same executable is used regardless of whether the payload is a jar, a tarball, a container image or a Python wheel.
One CLI, one test file per package ecosystem
The top level of the repository is essentially a catalogue of ecosystems. There are test files named apk_test.go, apt_test.go, cargo_test.go, choco_test.go, conan_test.go, helm_test.go, nix_test.go, huggingface_test.go, pip_test.go, nuget_test.go, nuget_native_test.go, dotnet_native_test.go, go_test.go, npm_test.go, maven_test.go, gradle_test.go, docker_test.go and more, alongside tests for the platform surfaces themselves such as artifactory_test.go, distribution_test.go and access_test.go.
The continuous integration configuration mirrors that list one for one. The README shows separate workflow files for Artifactory, Distribution and Access, and then per ecosystem workflows named mavenTests.yml, gradleTests.yml, npmTests.yml, dockerTests.yml, podmanTests.yml, nugetTests.yml, pythonTests.yml, goTests.yml and scriptTests.yml, plus analysis.yml for code analysis and pluginsTests.yml for plugins. A further workflow is labelled Transfer To Cloud.
That structure says something about the difficulty of the project. A client that only moved files would need one test suite. A client that has to resolve, cache, authenticate against and upload through a different native mechanism for each package manager needs one per ecosystem, and the cost of that work grows with every format the platform supports. The native variants, nuget_native and dotnet_native, show the same point in a different direction, where an ecosystem specific path exists next to the generic one.
For an adopter, the practical reading is that ecosystem support is the maturity signal here, not the presence of a command. A team using Helm and PyPI is covered by workflows that run on every change.
The go.mod reveals a client that tracks its own platform closely
The Go module file is more informative than the README, which is largely a wall of badges. The module is declared as github.com/jfrog/jfrog-cli and it requires a recent Go toolchain.
module github.com/jfrog/jfrog-cli
go 1.26.9The interesting part is the require list, which is dominated by sibling modules from the same organisation: jfrog-cli-core, jfrog-client-go, jfrog-cli-security, jfrog-cli-evidence, jfrog-cli-platform-services, jfrog-cli-artifactory, jfrog-cli-application, build-info-go, gofrog and archiver. Alongside them sit general purpose libraries such as urfave/cli for the command line surface, viper for configuration, testify for assertions and testcontainers-go for integration tests.
Those versions are not release versions. Several are pseudo versions that point at a commit, for example jfrog-cli-core/v2 at v2.60.1-0.20261016082348 and jfrog-client-go at v1.55.1-0.20261014133608, and a release dated 2026-10-20 containing a dependency built on 2026-10-16 tells you the release was cut from a moving main branch rather than from a frozen tag.
The replace block is the most revealing part, because it records the exceptions where the client deliberately refuses to move forward.
github.com/CycloneDX/cyclonedx-go => github.com/CycloneDX/cyclonedx-go v0.10.0
// Should not be updated to 0.2.6 due to a bug (https://github.com/jfrog/jfrog-cli-core/pull/372)
github.com/c-bata/go-prompt => github.com/c-bata/go-prompt v0.2.5The CycloneDX entry is held at 0.10.0 because a default specification version change from 1.6 to 1.7 would not yet be supported downstream, and the two other pins are held back because of a known bug. These are dependency decisions with a documented reason attached, which is a good sign for a security tool, since the client resolves to a directory of artifacts.
The Makefile distinguishes a stub binary from a full one
The build file is where a surprising design decision shows up, and it affects what you should ship.
@echo " stub - Build jf with the OSS-default stub OpenAPI spec bundle"
@echo " full - Build jf with the full OpenAPI spec bundle (requires docs/api-spec/full/ to be populated)"There are two ways to build the same command. The stub build uses an open source default OpenAPI specification bundle, while the full build uses a larger specification bundle that has to be populated into docs/api-spec/full/ before it will compile. In other words, the complete set of API operations is not shipped in the open source tree, and the default build intentionally covers a smaller surface. For a team on a self hosted Artifactory that is probably enough, and for a team using the whole commercial platform the extra endpoints are what they would want.
The rest of the Makefile is dependency maintenance. A help target lists update-all, which updates the JFrog dependencies to their latest main branches, and a set of per module targets such as update-core, update-client-go and update-security. The update commands deliberately bypass the configured proxy.
@GOPROXY=direct go get github.com/jfrog/build-info-go@mainThat is a sensible detail for a tool that lives inside corporate networks, because the tool is often used to configure the proxy it would otherwise fetch through, and the file also carries a clean and a test target alongside the build target.
Installing the client and making a first call
The README as published points to hosted documentation for the operational details, at the docs.jfrog.com integration page for JFrog CLI, and the repository homepage links to the product documentation on the vendor site. The README itself does not print an install command, so the install step is documented rather than reproduced here.
What the repository does make clear is the shape of a first use. The config directory at the top level holds the configuration machinery, and the docs and documentation directories, together with the guides directory, hold the operational guidance that a new user should read before touching a production instance. The completion directory suggests the command surface is intended to be used interactively, and the packagealias directory is another sign of a command hierarchy with grouped subcommands rather than a flat list of flags.
The sequence a new user should follow is therefore short. Install the client from the vendor documentation, point the config command at an Artifactory instance and give that instance a server ID, and only then run the first transfer. Getting the server ID right at configuration time matters more than it looks, because the configuration file is what lets a CI job and a laptop talk to the same instance with different credentials, and the config directory is where that mapping lives.
One warning belongs here. The CLI is a client for JFrog services, so there is nothing to point it at until such a service exists, and the first meaningful test needs a real repository with real permissions, not a local file.
Where the CLI is the wrong choice
There are situations where this tool is simply the wrong shape of answer.
The first is a project with no JFrog platform. The client automates access to the JFrog products, so without one of those products it has nothing to talk to. A team that needs somewhere to put a jar or a container image and does not want to run a vendor specific platform will find that the CLI assumes a whole world of configuration, server IDs and repository types that they do not have.
The second is the open source build surface. If you build the stub binary because docs/api-spec/full/ is not populated in your environment, some operations against the full commercial platform are not there. That is a deliberate trade of capability for a build that works without internal artefacts, and it is the one decision in this repository most likely to surprise a new contributor.
The third is a one off task. Uploading a single file to a repository that another tool already manages well does not justify learning a client with a large command surface, its own configuration model and a per ecosystem set of behaviours.
There is a fourth consideration, and it is not a functional one. The dependency pinning shows a client that deliberately waits for downstream support before moving forward, which is careful work, but it also means the tool tracks one vendor's release cadence closely. A build that pins a CLI version inherits that cadence, including the times when a release holds a dependency back.
Apache-2.0 licensing and a weekly release rhythm
The licence is Apache 2.0, which permits commercial use and modification with attribution and preservation of the licence and notice, and includes an express patent grant. The repository carries the LICENSE file and a THIRD_PARTY_NOTICES file, the latter being expected for a Go project that vendors a long list of dependencies. This is not legal advice, and a company should still run its own review, but the licence is a permissive one that is easy to accept in a commercial pipeline.
The release cadence is fast and regular. Version 2.126.0 was published on 2026-10-06, 2.127.0 on 2026-10-13 and 2.128.0 on 2026-10-20, three releases in three consecutive weeks, and the last push to the default branch was on 2026-10-20. The repository is not archived. A weekly minor release means a pipeline that pins a version is making a decision every week, and a pipeline that tracks the latest is accepting new behaviour every week.
The practical cost of that pace is upgrade work rather than licence work. The RELEASE.md file and the Jenkinsfile-dev-build at the top of the tree show a project with a defined release process, and the per ecosystem test workflows mean a change in one format can be landed without breaking another. Your own upgrade check should be narrower than that, and should focus on the commands your pipeline actually calls.
How it differs from calling the repository API yourself
The alternative is a shell script that talks to Artifactory over HTTP with a signed request, using curl and jq. That approach is short, has no dependencies beyond a shell, and is easy to read in a pull request.
The difference is what the client already knows. The jfrog client ships the request signing, the retry and progress behaviour, the per ecosystem resolution rules that determine which URL a Maven or npm client expects a file at, and the build information handling that the build-info-go dependency exists for. A hand written script has to reproduce the parts it needs and accept the drift on the parts it does not, and the parts that matter most for a repository are the addressing rules, because an artifact uploaded to the wrong path is still readable to a human and still broken to a build.
The second difference is configuration. The client keeps server configuration in files managed by its own config command, with named server instances, so the same script can be run by a developer and by a CI job against the same instance. A script with curl carries that mapping in environment variables, which is fine for one pipeline and painful for many.
The trade is dependency weight. The Go module pulls in a client library, a security module, an evidence module, an archiver and a container API library, so the tool is a real program rather than a thin wrapper, and you inherit its upgrade cycle. For a platform team that runs dozens of pipelines, that weight is a small price for one consistent client, and for a single upload it is not worth it.
Editorial conclusion
JFrog CLI is the right tool for a team that already runs JFrog Artifactory and wants the same repository operations from a shell, a Makefile and a CI job, and for a platform engineer who needs upload, download and promotion behaviour to be identical on a laptop and on a build agent. It is the wrong tool for a team with no JFrog deployment, for a one off upload, or for a project that wants a neutral artifact store, because the client is bound to the vendor's own services and configuration model. Before adopting, check the config command's environment and server ID handling for your CI provider, and read the stub versus full OpenAPI build targets in the Makefile to decide which binary your environment should ship.
Frequently asked questions
What does JFrog CLI automate?
The project describes itself as a client that provides a simple interface that automates access to the JFrog products. It is distributed as the jf command and covers repository operations across package ecosystems, with the repository carrying separate test files and CI workflows for Maven, npm, Docker, Go, NuGet, Python, Helm, Conan, Cargo and others.
What licence is JFrog CLI released under?
It is released under the Apache 2.0 licence, with the LICENSE file at the root of the repository and a THIRD_PARTY_NOTICES file alongside it. The README badge in the repository also states License-Apache_2.0.
How often are new versions of JFrog CLI published?
The recent releases show v2.126.0 on 2026-10-06, v2.127.0 on 2026-10-13 and v2.128.0 on 2026-10-20, which is a weekly minor release cadence, and the last push to the default branch was on 2026-10-20.
What is the difference between the stub and full builds of jf?
The Makefile describes the stub target as building jf with the open source default stub OpenAPI spec bundle, and the full target as building jf with the full OpenAPI spec bundle, which requires docs/api-spec/full/ to be populated. The default build therefore covers a smaller API surface than the full one.
Which programming language and dependencies does JFrog CLI use?
It is written in Go, and the go.mod file declares the module as github.com/jfrog/jfrog-cli with a Go 1.26.9 toolchain. The dependency list is dominated by JFrog modules such as jfrog-cli-core, jfrog-client-go and jfrog-cli-security, alongside urfave/cli, viper, testify and testcontainers-go.
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/jfrog-jfrog-cli)Was this useful?