diff --git a/.gitignore b/.gitignore
index 5d276348528b6f5f649fe26d5953d72ee3ff6095..2e8401727ef4ac983c0d7861f7ea82d0497cb18f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,8 +2,8 @@ config.json
 cashierd.conf
 tmp
 
-cashier
-cashierd
+/cashier
+/cashierd
 
 signing_key
 http.log
diff --git a/.travis.yml b/.travis.yml
index 34b5a8f5d4427350538197fc149f0538bb7debd8..fecbd3737056084ae61e5395cef41faacd226e98 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,13 +6,12 @@ env:
   - MYSQL_TEST="true"
 
 go:
-  - 1.7.4
-  - 1.8rc2
-  - tip
+  - 1.7.5
+  - 1.8rc3
 
 matrix:
   allow_failures:
-    - go: tip
+    - go: 1.8rc2
 
 before_install:
   - go get -v github.com/golang/lint/golint
@@ -25,8 +24,4 @@ before_script:
 
 sudo: false
 script:
-  - go install -v ./cmd/cashier ./cmd/cashierd
-  - go list ./... |grep -v vendor/ |xargs go test
-  - gofmt -d $(find -type f -name '*.go' -not -path './vendor/*')
-  - go list ./... |grep -v vendor/ |xargs go vet
-  - go list ./... |grep -v vendor/ |xargs -L1 golint -set_exit_status
+  - ./run_tests.sh
diff --git a/README.md b/README.md
index 5ba9cfc63e41a8622537d7969b95ac8bf1498e46..9581761829eccf5dae313d510cb720681e9d31a7 100644
--- a/README.md
+++ b/README.md
@@ -144,7 +144,7 @@ server {
 }
 ```
 
-Prior to using MySQL or SQLite you need to create the database and tables using [one of the provided files](db).  
+Prior to using MySQL or SQLite you need to create the database and tables using [the provided seed file](db/seed.sql).  
 e.g. `mysql < db/seed.sql`.  
 Obviously you should setup a role user for running in prodution.
 
@@ -214,7 +214,14 @@ For the server you need the following:
 
 ## Using cashier
 Once the server is up and running you'll need to configure your client.  
-The client is configured using either a [HCL](https://github.com/hashicorp/hcl) configuration file - [example](example-client.conf) - or command-line flags.  
+The client is configured using either a [HCL](https://github.com/hashicorp/hcl) configuration file - [example](example-client.conf) - or command-line flags.
+
+- `--ca`          CA server (default "http://localhost:10000").
+- `--config`      Path to config file (default "~/.cashier.conf").
+- `--key_size`    Key size. Ignored for ed25519 keys (default 2048).
+- `--key_type`    Type of private key to generate - rsa, ecdsa or ed25519 (default "rsa").
+- `--public_file_prefix` Prefix for filename for public key and cert (optional, no default). The public key is put in a file with `.pub` appended to it; the public cert file in a file with `-cert.pub` appended to it.
+- `--validity`    Key validity (default 24h).
 
 Running the `cashier` cli tool will open a browser window at the configured CA address.
 The CA will redirect to the auth provider for authorisation, and redirect back to the CA where the access token will printed.  
@@ -222,9 +229,17 @@ Copy the access token. In the terminal where you ran the `cashier` cli paste the
 The client will then generate a new ssh key-pair and send the public part to the server (along with the access token).  
 Once signed the client will install the key and signed certificate in your ssh agent. When the certificate expires it will be removed automatically from the agent.
 
+If you set `public_file_prefix` then the public key and public cert will be written to the files that start with `public_file_prefix` and end with `.pub` and `-cert.pub` respectively.
+
+In your `ssh_config` you can load these for a given host with the `IdentityFile` and `CertificateFile`. However prior to OpenSSH version 7.2p1 the latter option didn't exist.
+In that case you could specify `~/.ssh/some-identity` as your `IdentityFile` and OpenSSH would look in `~/.ssh/some-identity.pub` and `~/.ssh/some-identity-cert.pub`.
+
+Starting with 7.2p1 the two options exist in the `ssh_config` and you'll need to use the full paths to them.
+Note that like these `ssh_config` options, the `public_file_prefix` supports tilde expansion.
+
 ## Configuring SSH
-The ssh client needs no special configuration, just a running ssh-agent.  
-The ssh server needs to trust the public part of the CA signing key. Add something like the following to your sshd_config:
+The ssh client needs no special configuration, just a running `ssh-agent`.  
+The ssh server needs to trust the public part of the CA signing key. Add something like the following to your `sshd_config`:  
 ```
 TrustedUserCAKeys /etc/ssh/ca.pub
 ```
diff --git a/client/client.go b/client/client.go
index 382c53dcb4a6f86b4158becca4756a96f342ed27..e1fb98c635a5af674fee81b65ea00b846e33e89e 100644
--- a/client/client.go
+++ b/client/client.go
@@ -3,8 +3,10 @@ package client
 import (
 	"bytes"
 	"crypto/tls"
+	"encoding/base64"
 	"encoding/json"
 	"fmt"
+	"io/ioutil"
 	"net/http"
 	"net/url"
 	"path"
@@ -16,6 +18,22 @@ import (
 	"golang.org/x/crypto/ssh/agent"
 )
 
+// SavePublicFiles installs the public part of the cert and key.
+func SavePublicFiles(prefix string, cert *ssh.Certificate, pub ssh.PublicKey) error {
+	if prefix == "" {
+		return nil
+	}
+	pubTxt := ssh.MarshalAuthorizedKey(pub)
+	certPubTxt := []byte(cert.Type() + " " + base64.StdEncoding.EncodeToString(cert.Marshal()))
+
+	if err := ioutil.WriteFile(prefix+".pub", pubTxt, 0644); err != nil {
+		return err
+	}
+	err := ioutil.WriteFile(prefix+"-cert.pub", certPubTxt, 0644)
+
+	return err
+}
+
 // InstallCert adds the private key and signed certificate to the ssh agent.
 func InstallCert(a agent.Agent, cert *ssh.Certificate, key Key) error {
 	t := time.Unix(int64(cert.ValidBefore), 0)
diff --git a/client/config.go b/client/config.go
index 1cc9401fc6a2a340e5d5d77979b5926e0e9f2f8e..07bbb8c940ff35e53e8fc0a0c665fea1c3fffe18 100644
--- a/client/config.go
+++ b/client/config.go
@@ -1,6 +1,7 @@
 package client
 
 import (
+	"github.com/mitchellh/go-homedir"
 	"github.com/spf13/pflag"
 	"github.com/spf13/viper"
 )
@@ -12,6 +13,7 @@ type Config struct {
 	Keysize                int    `mapstructure:"key_size"`
 	Validity               string `mapstructure:"validity"`
 	ValidateTLSCertificate bool   `mapstructure:"validate_tls_certificate"`
+	PublicFilePrefix       string `mapstructure:"public_file_prefix"`
 }
 
 func setDefaults() {
@@ -19,6 +21,7 @@ func setDefaults() {
 	viper.BindPFlag("key_type", pflag.Lookup("key_type"))
 	viper.BindPFlag("key_size", pflag.Lookup("key_size"))
 	viper.BindPFlag("validity", pflag.Lookup("validity"))
+	viper.BindPFlag("public_file_prefix", pflag.Lookup("public_file_prefix"))
 	viper.SetDefault("validateTLSCertificate", true)
 }
 
@@ -34,5 +37,10 @@ func ReadConfig(path string) (*Config, error) {
 	if err := viper.Unmarshal(c); err != nil {
 		return nil, err
 	}
+	p, err := homedir.Expand(c.PublicFilePrefix)
+	if err != nil {
+		return nil, err
+	}
+	c.PublicFilePrefix = p
 	return c, nil
 }
diff --git a/cmd/cashier/main.go b/cmd/cashier/main.go
index 26c6cbf4e63d905a8357e8fa656c9d20fe941bc7..53deffd2ab1a8f04b156460f5f9ee99ec80cf021 100644
--- a/cmd/cashier/main.go
+++ b/cmd/cashier/main.go
@@ -16,12 +16,13 @@ import (
 )
 
 var (
-	u, _     = user.Current()
-	cfg      = pflag.String("config", path.Join(u.HomeDir, ".cashier.conf"), "Path to config file")
-	ca       = pflag.String("ca", "http://localhost:10000", "CA server")
-	keysize  = pflag.Int("key_size", 2048, "Key size. Ignored for ed25519 keys")
-	validity = pflag.Duration("validity", time.Hour*24, "Key validity")
-	keytype  = pflag.String("key_type", "rsa", "Type of private key to generate - rsa, ecdsa or ed25519")
+	u, _             = user.Current()
+	cfg              = pflag.String("config", path.Join(u.HomeDir, ".cashier.conf"), "Path to config file")
+	ca               = pflag.String("ca", "http://localhost:10000", "CA server")
+	keysize          = pflag.Int("key_size", 2048, "Key size. Ignored for ed25519 keys")
+	validity         = pflag.Duration("validity", time.Hour*24, "Key validity")
+	keytype          = pflag.String("key_type", "rsa", "Type of private key to generate - rsa, ecdsa or ed25519")
+	publicFilePrefix = pflag.String("public_file_prefix", "", "Prefix for filename for public key and cert (optional, no default)")
 )
 
 func main() {
@@ -58,5 +59,8 @@ func main() {
 	if err := client.InstallCert(a, cert, priv); err != nil {
 		log.Fatalln(err)
 	}
+	if err := client.SavePublicFiles(c.PublicFilePrefix, cert, pub); err != nil {
+		log.Fatalln(err)
+	}
 	fmt.Println("Credentials added.")
 }
diff --git a/run_tests.sh b/run_tests.sh
new file mode 100755
index 0000000000000000000000000000000000000000..87d5cbd8bae27d6395fff8ba5723ec33404e2441
--- /dev/null
+++ b/run_tests.sh
@@ -0,0 +1,16 @@
+#! /bin/sh
+
+# This can be used as a pre-commit script.  Just run
+#   cp run_tests.sh .git/hooks/pre-commit
+# and it will run before each commit.
+
+set -xue
+
+go install -v ./cmd/cashier ./cmd/cashierd
+go list ./... |grep -v vendor/ |xargs go test
+gofmt -d $(find * -type f -name '*.go' -not -path 'vendor/*')
+go list ./... |grep -v vendor/ |xargs go vet
+if ! type -f golint > /dev/null; then
+  go get -u github.com/golang/lint/golint
+fi
+go list ./... |grep -v vendor/ |xargs -L1 golint -set_exit_status
diff --git a/server/auth/gitlab/gitlab.go b/server/auth/gitlab/gitlab.go
index ec735c529cadb2535562da362b8029f686297817..f76b2e8d75044265371cd2686d9c0202c57c7558 100644
--- a/server/auth/gitlab/gitlab.go
+++ b/server/auth/gitlab/gitlab.go
@@ -26,7 +26,7 @@ type Config struct {
 }
 
 // New creates a new Gitlab provider from a configuration.
-func New(c *config.Auth) (auth.Provider, error) {
+func New(c *config.Auth) (*Config, error) {
 	uw := make(map[string]bool)
 	for _, u := range c.UsersWhitelist {
 		uw[u] = true
diff --git a/server/templates/token.go b/server/templates/token.go
index ffaba910ca9944b49e5238aaf7671e5242ebf972..83e49730590ed93335dfc72b50a01b0d736b0b57 100644
--- a/server/templates/token.go
+++ b/server/templates/token.go
@@ -35,7 +35,7 @@ const Token = `
 			<h2>Access Token</h2>
 		</div>
 		<div>
-			<textarea style="font-size: 15pt" class="u-full-width code" readonly spellcheck="false" onclick="this.focus();this.select();">{{.Token}}</textarea>
+			<textarea style="font-size: 12pt" class="u-full-width code" readonly spellcheck="false" onclick="this.focus();this.select();">{{.Token}}</textarea>
 			<h3>
 				The token will expire in &lt; 1 hour.
 			</h3>