diff --git a/vendor/git.autistici.org/ai3/go-common/README.md b/vendor/git.autistici.org/ai3/go-common/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..756a743b6f9008194e37d701fd4a23eb818fee6e
--- /dev/null
+++ b/vendor/git.autistici.org/ai3/go-common/README.md
@@ -0,0 +1,25 @@
+ai3/go-common
+===
+
+Common code for ai3 services and tools.
+
+A quick overview of the contents:
+
+* [client](clientutil/) and [server](serverutil/) HTTP-based
+  "RPC" implementation, just JSON POST requests but with retries,
+  backoff, timeouts, tracing, etc.
+
+* [server implementation of a generic line-based protocol over a UNIX
+  socket](unix/).
+
+* a [LDAP connection pool](ldap/).
+
+* utilities to [serialize composite data types](ldap/compositetypes/)
+  used in our LDAP database.
+
+* a [password hashing library](pwhash/) that uses fancy advanced
+  crypto by default but is also backwards compatible with old
+  libc crypto.
+
+* utilities to [manage encryption keys](userenckey/), themselves
+  encrypted with a password and a KDF.
diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/balancer.go b/vendor/git.autistici.org/ai3/go-common/clientutil/balancer.go
index 9d9b7dd6cdf2f9f66b93d3f6c7f0778de44060fb..d2ca8270fcf27deb3e48e3526a91bb2c8cf004a1 100644
--- a/vendor/git.autistici.org/ai3/go-common/clientutil/balancer.go
+++ b/vendor/git.autistici.org/ai3/go-common/clientutil/balancer.go
@@ -98,40 +98,69 @@ func newBalancedBackend(config *BackendConfig, resolver resolver) (*balancedBack
 // with a JSON-encoded request body. It will attempt to decode the
 // response body as JSON.
 func (b *balancedBackend) Call(ctx context.Context, shard, path string, req, resp interface{}) error {
+	// Serialize the request body.
 	data, err := json.Marshal(req)
 	if err != nil {
 		return err
 	}
 
-	var tg targetGenerator = b.backendTracker
-	if b.sharded && shard != "" {
-		tg = newShardedGenerator(shard, b.baseURI.Host, b.resolver)
+	// Create the target sequence for this call. If there are multiple
+	// targets, reduce the timeout on each individual call accordingly to
+	// accomodate eventual failover.
+	seq, err := b.makeSequence(shard)
+	if err != nil {
+		return err
+	}
+	innerTimeout := 1 * time.Hour
+	if deadline, ok := ctx.Deadline(); ok {
+		innerTimeout = time.Until(deadline) / time.Duration(seq.Len())
 	}
-	seq := newSequence(tg)
-	b.log.Printf("%016x: initialized", seq.ID())
 
-	var httpResp *http.Response
-	err = backoff.Retry(func() error {
+	// Call the backends in the sequence until one succeeds, with an
+	// exponential backoff policy controlled by the outer Context.
+	return backoff.Retry(func() error {
 		req, rerr := b.newJSONRequest(path, shard, data)
 		if rerr != nil {
 			return rerr
 		}
-		httpResp, rerr = b.do(ctx, seq, req)
-		return rerr
+		innerCtx, cancel := context.WithTimeout(ctx, innerTimeout)
+		defer cancel()
+
+		// When do() returns successfully, we already know that the
+		// response had an HTTP status of 200.
+		httpResp, rerr := b.do(innerCtx, seq, req)
+		if rerr != nil {
+			return rerr
+		}
+		defer httpResp.Body.Close() // nolint
+
+		// Decode the response, unless the 'resp' output is nil.
+		if httpResp.Header.Get("Content-Type") != "application/json" {
+			return errors.New("not a JSON response")
+		}
+		if resp == nil {
+			return nil
+		}
+		return json.NewDecoder(httpResp.Body).Decode(resp)
 	}, backoff.WithContext(newExponentialBackOff(), ctx))
-	if err != nil {
-		return err
-	}
-	defer httpResp.Body.Close() // nolint
+}
 
-	if httpResp.Header.Get("Content-Type") != "application/json" {
-		return errors.New("not a JSON response")
+// Initialize a new target sequence.
+func (b *balancedBackend) makeSequence(shard string) (*sequence, error) {
+	var tg targetGenerator = b.backendTracker
+	if b.sharded {
+		if shard == "" {
+			return nil, fmt.Errorf("call without shard to sharded service %s", b.baseURI.String())
+		}
+		tg = newShardedGenerator(shard, b.baseURI.Host, b.resolver)
 	}
 
-	if resp == nil {
-		return nil
+	seq := newSequence(tg)
+	if seq.Len() == 0 {
+		return nil, errNoTargets
 	}
-	return json.NewDecoder(httpResp.Body).Decode(resp)
+	b.log.Printf("%016x: initialized", seq.ID())
+	return seq, nil
 }
 
 // Return the URI to be used for the request. This is used both in the
@@ -210,6 +239,8 @@ func newSequence(tg targetGenerator) *sequence {
 
 func (s *sequence) ID() uint64 { return s.id }
 
+func (s *sequence) Len() int { return len(s.targets) }
+
 func (s *sequence) reloadTargets() {
 	targets := s.tg.getTargets()
 	if len(targets) > 0 {
diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/transport.go b/vendor/git.autistici.org/ai3/go-common/clientutil/transport.go
index 843a760b1e25eb1c21b2c8552b9c6ad107ea6d34..1cc6c6d34594869c8be446fd325c27d1ad2c3289 100644
--- a/vendor/git.autistici.org/ai3/go-common/clientutil/transport.go
+++ b/vendor/git.autistici.org/ai3/go-common/clientutil/transport.go
@@ -7,6 +7,8 @@ import (
 	"net/http"
 	"sync"
 	"time"
+
+	"git.autistici.org/ai3/go-common/tracing"
 )
 
 // The transportCache is just a cache of http transports, each
@@ -29,12 +31,12 @@ func newTransportCache(tlsConfig *tls.Config) *transportCache {
 }
 
 func (m *transportCache) newTransport(addr string) http.RoundTripper {
-	return &http.Transport{
+	return tracing.WrapTransport(&http.Transport{
 		TLSClientConfig: m.tlsConfig,
 		DialContext: func(ctx context.Context, network, _ string) (net.Conn, error) {
 			return netDialContext(ctx, network, addr)
 		},
-	}
+	})
 }
 
 func (m *transportCache) getTransport(addr string) http.RoundTripper {
diff --git a/vendor/git.autistici.org/ai3/go-common/serverutil/http.go b/vendor/git.autistici.org/ai3/go-common/serverutil/http.go
index 09cc9bb39440f01b502cf375d9a3ec28777e4955..604ca98f54357b6d423f341f8e71cd589697555a 100644
--- a/vendor/git.autistici.org/ai3/go-common/serverutil/http.go
+++ b/vendor/git.autistici.org/ai3/go-common/serverutil/http.go
@@ -3,16 +3,18 @@ package serverutil
 import (
 	"context"
 	"crypto/tls"
+	"fmt"
 	"io"
 	"log"
 	"net"
 	"net/http"
-	"net/http/pprof"
+	_ "net/http/pprof"
 	"os"
 	"os/signal"
 	"syscall"
 	"time"
 
+	"git.autistici.org/ai3/go-common/tracing"
 	"github.com/coreos/go-systemd/daemon"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -77,6 +79,10 @@ func (config *ServerConfig) buildHTTPServer(h http.Handler) (*http.Server, error
 // the listener, otherwise it will handle graceful termination on
 // SIGINT or SIGTERM and return nil.
 func Serve(h http.Handler, config *ServerConfig, addr string) error {
+	// Wrap with tracing handler (exclude metrics and other
+	// debugging endpoints).
+	h = tracing.WrapHandler(h, guessEndpointName(addr))
+
 	// Create the HTTP server.
 	srv, err := config.buildHTTPServer(h)
 	if err != nil {
@@ -139,8 +145,10 @@ func defaultHandler(h http.Handler) http.Handler {
 	// Add an endpoint to serve Prometheus metrics.
 	root.Handle("/metrics", promhttp.Handler())
 
-	// Add the net/http/pprof debug handlers.
-	root.Handle("/debug/pprof/", pprof.Handler(""))
+	// Let the default net/http handler deal with /debug/
+	// URLs. Packages such as net/http/pprof register their
+	// handlers there in ways that aren't reproducible.
+	root.Handle("/debug/", http.DefaultServeMux)
 
 	// Forward everything else to the main handler, adding
 	// Prometheus instrumentation (requests to /metrics and
@@ -151,6 +159,18 @@ func defaultHandler(h http.Handler) http.Handler {
 	return root
 }
 
+func guessEndpointName(addr string) string {
+	_, port, err := net.SplitHostPort(addr)
+	if err != nil {
+		return addr
+	}
+	host, err := os.Hostname()
+	if err != nil {
+		return addr
+	}
+	return fmt.Sprintf("%s:%s", host, port)
+}
+
 // HTTP-related metrics.
 var (
 	// Since we instrument the root HTTP handler, we don't really
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 18625a2718d70e64e464e2f1f13a90349261e7e0..0f4b4c6e49cd930a57976f52d2623f4c2543779b 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -3,22 +3,22 @@
 	"ignore": "test",
 	"package": [
 		{
-			"checksumSHA1": "pLvPnUablirQucyALgrso9hLG4E=",
+			"checksumSHA1": "oUOxU+Tw1/jOzWVP05HuGvVSC/A=",
 			"path": "git.autistici.org/ai3/go-common",
-			"revision": "6916834dec86e761a3091c9628cbff9b6c389867",
-			"revisionTime": "2018-10-29T11:03:54Z"
+			"revision": "54f0ac4c46184ae44486a31ca2705076abcc5321",
+			"revisionTime": "2019-06-30T08:30:15Z"
 		},
 		{
-			"checksumSHA1": "Xd4ClmFykFMOg8b2ZFXimSS3Uj0=",
+			"checksumSHA1": "kJwm6y9JXhybelO2zUl7UbzIdP0=",
 			"path": "git.autistici.org/ai3/go-common/clientutil",
-			"revision": "6916834dec86e761a3091c9628cbff9b6c389867",
-			"revisionTime": "2018-10-29T11:03:54Z"
+			"revision": "54f0ac4c46184ae44486a31ca2705076abcc5321",
+			"revisionTime": "2019-06-30T08:30:15Z"
 		},
 		{
-			"checksumSHA1": "RyFydcBJvLBevfsriijLqHtZ0hs=",
+			"checksumSHA1": "TKGUNmKxj7KH3qhwiCh/6quUnwc=",
 			"path": "git.autistici.org/ai3/go-common/serverutil",
-			"revision": "6916834dec86e761a3091c9628cbff9b6c389867",
-			"revisionTime": "2018-10-29T11:03:54Z"
+			"revision": "54f0ac4c46184ae44486a31ca2705076abcc5321",
+			"revisionTime": "2019-06-30T08:30:15Z"
 		},
 		{
 			"checksumSHA1": "FRxoT4jwgKDffIm5RwpFWjVVilc=",