Sunday, March 3, 2013

Signing and Promoting your Clojure libraries on Clojars

Phil Hagelberg, the creator and primary maintainer of Leiningen, has been advocating that Clojurians sign their Clojure libraries for the releases repository in Clojars. By itself, this isn't sufficient to provide security to avoid malicious code from causing havoc with public code repositories, but it is a necessary first step. Phil has talked about his ideas on how to get to a more complete model of security in a couple of places:

/* ---[ Signing your Clojure libraries ]--- */

My first experience deploying a signed jar to Clojars was a little rocky, so I'm providing this how-to report to help others (including future me).

I have only done this on (Xubuntu) Linux, but I imagine it will work fairly similarly on Macs. Not sure about Windows, as I seem to have constant trouble getting Clojure and Windows to play nicely together. I have used GPG on Windows that comes with mysysgit, so that will probably work with these instructions as well, but I haven't tried it.

/* ---[ STEP 1: Generate GPG Keys ]--- */

Clojars security is based on PGP keys, so you need to a have a PGP public/private keyset. GnuPG (GPG) is the generally recommended tool for that.

If you already have GPG installed and can't remember if you've already created a keyset, try this first:

gpg --list-keys

If you see your name and email in the list, then you have. If not, generate them with:

gpg --gen-key

Accepting the defaults you are prompted with is fine. See this article for details on this step. When completed this will create your public key ring and secret/private key ring:

$ ls ~/.gnupg
pubring.gpg  pubring.gpg~  random_seed  secring.gpg  trustdb.gpg

/* ---[ STEP 2: Publish your public GPG key to a keyserver ]--- */

By publishing your public key, others can download it and verify that your signed library is in fact signed by you.

To publish your key you will need to get its ID.

$ gpg --list-keys
pub   2048R/5414B325 2012-11-12
uid                  Michael Peterson <>

The 8 characters after the '/' on the "pub" row of your key is your key's ID. Now publish it:

$ gpg --send-key 5414B325

If you don't specify a key server it will choose the GnuPG keyserver. If you want to target a specify keyserver use the --keyserver option as shown here.

/* ---[ STEP 3: Add your GPG key to your Clojars account ]--- */

When you sign up for Clojars there is a section in your Profile to add two keys: 1) an SSH public key and a PGP public key. The SSH key is for secure transport of the library from your system to the Clojars repo via scp. It is not related to signing your jars.

Your library will be signed with your PGP private key that resides only on your system. That signature indicates that the owner of the private key (the one paired with the public key you just published) signed this code artifact. It allows someone else to know who signed it and whether the code artifact has been changed since it was signed and deployed.

By having your PGP public key on Clojars, you allow Clojars to verify that one of its members signed the artifact. This check happens when you promote your release to the release repo (more on that below).

Note: Clojars is not a keyserver, so putting it there will not allow others to verify the signature. That is why in step 2 we published it to a public keyserver.

To add your public key to Clojars you create an "ASCII-armored" version of the binary public key, which you generate with:

gpg --armor --export code

Once you have it, what exactly do you paste into the Clojars text box? The BEGIN and END delimiter lines and everything in between, like so:

Version: GnuPG v1.4.11 (GNU/Linux)


/* ---[ STEP 4: Prepare your project and its metadata ]--- */

With Clojars you can publish SNAPSHOTS or releases. The latter can be "promoted" if you meet all the criteria in your project.clj, which are:

  • you cannot have the word SNAPSHOT in your version
  • you should have your license filled in
  • you need to have the :scm section filled in
    • you can either do this manually, as in the example below
    • or lein in theory can automatically do this for you if you are using GitHub and its remote "ID" is origin (though I've had issues even in that case)

Here is an example project.clj:

 (defproject thornydev/go-lightly "0.4.0"
   :description "Clojure library to facilitate CSP concurrent programming based on Go concurrency constructs"
   :url ""
   :license {:name "Eclipse Public License"
             :url ""}
   :profiles {:dev {:dependencies [[criterium "0.3.1"]]}}
   :dependencies [[org.clojure/clojure "1.5.0"]]
   :scm {:name "git"
         :url ""})

/* ---[ STEP 5: Commit your code ]--- */

Make sure you have committed all your changes into Git (or Hg, SVN or whatever SCM you are using). Tag the release if you are so inclined and (optional) push it to GitHub or your remote or central hosting server.

/* ---[ STEP 6: Deploy to Clojars ]--- */

From the top of your project directory, enter:

lein deploy clojars

In my case, my gpg-agent prompted me twice for my GPG passphrase and then the deploy happened.

When you do this lein will create a pom and a jar and upload those to Clojars. That pom.xml should include SCM information that looks like this:


The tag there should be the SHA1 of the last commit (in the case of Git). Note: Don't confuse it with a "tag" that you create with "git tag".

If the deploy was successful, your jar should be signed and (possibly) ready for Promotion.

/* ---[ STEP 7: Check whether your jar was signed ]--- */

Create a new lein project and make your deployed library one of its dependencies. Then in that new project run:

$ lein deps :verify
:signed [criterium "0.3.1"]
:unsigned [enlive "1.0.1"]
:signed [org.clojure/tools.macro "0.1.1"]
:signed [org.clojure/clojure "1.5.0"]
:bad-signature [thornydev/go-lightly "0.4.0"]

You see that some are signed and some are not. Obviously, you want yours to say :signed. If it has unsigned then you are probably either using Lein 1 or you didn't generate your GPG keys. If it has :bad-signature then something got corrupted on the Clojars server. In my case above, I promoted and tried to redeploy, which uncovered a bug in lein/clojars that caused some files to get overwritten when they shouldn't. This issue should be fixed soon. If you do have that problem, delete your local copy from your ~/.m2 directory and contact someone on the #leiningen IRC channel.

/* ---[ Optional STEP 8: Promote to release status ]--- */

If you are eligible to promote to release status, you will see a "Promote" button on your Clojars page. If you are not, you may be missing SCM information, which is what happened to me recently.

Note that once you promote you can no longer deploy to that version again, so make sure you're ready to make it immutable. After that, you can only add new versions.