Merge branch 'master' into 1278-add-clustered-db-info
diff --git a/.gitignore b/.gitignore
index a1cba1e..0883030 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,7 +28,6 @@
 share/www
 src/b64url/
 src/bear/
-src/bcrypt/
 src/config/
 src/couch/priv/couch_js/config.h
 src/couch/priv/couchjs
diff --git a/.travis.yml b/.travis.yml
index b2e7ff0..acb0b51 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,7 +3,7 @@
 
 os: linux
 otp_release:
-   - 20.1
+   - 20.3
    - 19.3
    - 18.3
    - 17.5
diff --git a/INSTALL.Unix.md b/INSTALL.Unix.md
index bfd9c89..f0baf58 100644
--- a/INSTALL.Unix.md
+++ b/INSTALL.Unix.md
@@ -74,6 +74,9 @@
 
     sudo apt-get --no-install-recommends -y install \
         python-sphinx
+        
+    sudo pip install --upgrade sphinx_rtd_theme nose requests hypothesis
+
 
 Be sure to update the version numbers to match your system's available
 packages.
diff --git a/LICENSE b/LICENSE
index 6034c71..83a1aa7 100644
--- a/LICENSE
+++ b/LICENSE
@@ -2158,123 +2158,6 @@
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 
-The Erlang code is subject to this license:
-
-%% Copyright (c) 2011 Hunter Morris <hunter.morris@smarkets.com>
-
-%% Permission to use, copy, modify, and distribute this software for any
-%% purpose with or without fee is hereby granted, provided that the above
-%% copyright notice and this permission notice appear in all copies.
-
-%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-The underlying blowfish code is derived from OpenBSD libc and is
-subject to the following license:
-
-/*
- * Blowfish block cipher for OpenBSD
- * Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
- * All rights reserved.
- *
- * Implementation advice by David Mazieres <dm@lcs.mit.edu>.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *      This product includes software developed by Niels Provos.
- * 4. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-The underlying bcrypt (hashing) code is derived from OpenBSD libc and is
-subject to the following license:
-
-/*
- * Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *      This product includes software developed by Niels Provos.
- * 4. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-The asynchronous queue code (c_src/async_queue.c and
-c_src/async_queue.h) is from the esnappy project, copyright 2011
-Konstantin V. Sorokin. It is subject to the following license:
-
-Copyright (c) 2011 Konstantin V. Sorokin
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the copyright holder nor the names of contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
 For the src/hyper component:
 
 The MIT License (MIT)
diff --git a/Makefile b/Makefile
index 27d9531..0ab7b4e 100644
--- a/Makefile
+++ b/Makefile
@@ -13,20 +13,49 @@
 include version.mk
 
 REBAR?=$(shell echo `pwd`/bin/rebar)
+
+# Handle the following scenarios:
+#   1. When building from a tarball, use version.mk.
+#   2. When building from a clean release tag (#.#.#), use that tag.
+#   3. When building from a clean RC tag (#.#.#-RC#), use JUST the version
+#      number inside the tarball, but use the full name for the name of the
+#      tarball itself.
+#   4. When not on a clean tag, use version.mk + git sha + dirty status.
+
+COUCHDB_GIT_SHA=$(git_sha)
+
 IN_RELEASE = $(shell if [ ! -d .git ]; then echo true; fi)
 ifeq ($(IN_RELEASE), true)
+
+# 1. Building from tarball, use version.mk.
 COUCHDB_VERSION = $(vsn_major).$(vsn_minor).$(vsn_patch)
+
 else
-# IN_RC generates a tarball that has the -RCx suffix in the name if needed
+
+# Gather some additional information.
+# We do it this way so we don't bake shell-isms into Makefile
+# to make it easier to port to Windows. I know, I know. -jst
+# IN_RC contains the -RCx suffix in the name if present
 IN_RC = $(shell git describe --tags --always --first-parent \
-							| grep -Eo -- '-RC[0-9]+' 2>/dev/null)
-RELTAG = $(shell git describe --dirty --abbrev=0 --tags --always --first-parent \
-				| grep -Eo '^[0-9]+\.[0-9]\.[0-9]+')
-ifeq ($(RELTAG),)
-COUCHDB_VERSION_SUFFIX = $(shell git rev-parse --short --verify HEAD)
+        | grep -Eo -- '-RC[0-9]+' 2>/dev/null)
+# ON_TAG matches *ONLY* if we are on a release or RC tag
+ON_TAG = $(shell git describe --tags --always --first-parent \
+        | grep -Eo -- '^[0-9]+\.[0-9]\.[0-9]+(-RC[0-9]+)?$$' 2>/dev/null)
+# RELTAG contains the #.#.# from git describe, which might be used
+RELTAG = $(shell git describe --tags --always --first-parent \
+        | grep -Eo -- '^[0-9]+\.[0-9]\.[0-9]+' 2>/dev/null)
+# DIRTY identifies if we're not on a commit
+DIRTY = $(shell git describe --dirty | grep -Eo -- '-dirty' 2>/dev/null)
+# COUCHDB_GIT_SHA is our current git hash.
+COUCHDB_GIT_SHA=$(shell git rev-parse --short --verify HEAD)
+
+ifeq ($(ON_TAG),)
+# 4. Not on a tag.
+COUCHDB_VERSION_SUFFIX = $(COUCHDB_GIT_SHA)$(DIRTY)
 COUCHDB_VERSION = $(vsn_major).$(vsn_minor).$(vsn_patch)-$(COUCHDB_VERSION_SUFFIX)
 else
-COUCHDB_VERSION = $(RELTAG)
+# 2 and 3. On a tag.
+COUCHDB_VERSION = $(RELTAG)$(DIRTY)
 endif
 endif
 
@@ -82,7 +111,7 @@
 .PHONY: couch
 # target: couch - Build CouchDB core, use ERL_OPTS to provide custom compiler's options
 couch: config.erl
-	@COUCHDB_VERSION=$(COUCHDB_VERSION) $(REBAR) compile $(COMPILE_OPTS)
+	@COUCHDB_VERSION=$(COUCHDB_VERSION) COUCHDB_GIT_SHA=$(COUCHDB_GIT_SHA) $(REBAR) compile $(COMPILE_OPTS)
 	@cp src/couch/priv/couchjs bin/
 
 
@@ -225,7 +254,7 @@
 .PHONY: list-eunit-suites
 # target: list-eunit-suites - List EUnit target test suites
 list-eunit-suites:
-	@find ./src/ -type f -name *_test.erl -o -name *_tests.erl -printf "%f\n" \
+	@find ./src/ -type f -name *_test.erl -o -name *_tests.erl -exec basename {} \; \
 		| cut -d '.' -f -1 \
 		| sort
 
@@ -233,7 +262,7 @@
 .PHONY: list-js-suites
 # target: list-js-suites - List JavaScript test suites
 list-js-suites:
-	@find ./test/javascript/tests/ -type f -name *.js -printf "%f\n" \
+	@find ./test/javascript/tests/ -type f -name *.js -exec basename {} \; \
 		| cut -d '.' -f -1 \
 		| sort
 
diff --git a/Makefile.win b/Makefile.win
index 5a2a73a..67c15fc 100644
--- a/Makefile.win
+++ b/Makefile.win
@@ -14,13 +14,50 @@
 
 SHELL=cmd.exe
 REBAR?=$(shell where rebar.cmd)
+
+# Handle the following scenarios:
+#   1. When building from a tarball, use version.mk.
+#   2. When building from a clean release tag (#.#.#), use that tag.
+#   3. When building from a clean RC tag (#.#.#-RC#), use JUST the version
+#      number inside the tarball, but use the full name for the name of the
+#      tarball itself.
+#   4. When not on a clean tag, use version.mk + git sha + dirty status.
+
+COUCHDB_GIT_SHA=$(git_sha)
 IN_RELEASE = $(shell if not exist .git echo true)
+
 ifeq ($(IN_RELEASE), true)
-COUCHDB_VERSION_SUFFIX=
+
+# 1. Building from tarball, use version.mk.
 COUCHDB_VERSION = $(vsn_major).$(vsn_minor).$(vsn_patch)
+
 else
-COUCHDB_VERSION_SUFFIX = -$(shell git rev-parse --short --verify HEAD)
-COUCHDB_VERSION = $(vsn_major).$(vsn_minor).$(vsn_patch)$(COUCHDB_VERSION_SUFFIX)
+
+# Gather some additional information.
+# We do it this way so we don't bake shell-isms into Makefile
+# to make it easier to port to Windows. I know, I know. -jst
+# COUCHDB_GIT_SHA is our current git hash.
+COUCHDB_GIT_SHA=$(shell git rev-parse --short --verify HEAD)
+# IN_RC contains the -RCx suffix in the name if present
+IN_RC = $(shell git describe --tags --always --first-parent \
+        | grep -Eo -- '-RC[0-9]+' 2>/dev/null)
+# ON_TAG matches *ONLY* if we are on a release or RC tag
+ON_TAG = $(shell git describe --tags --always --first-parent \
+        | grep -Eo -- '^[0-9]+\.[0-9]\.[0-9]+(-RC[0-9]+)?$$' 2>/dev/null)
+# RELTAG contains the #.#.# from git describe, which might be used
+RELTAG = $(shell git describe --tags --always --first-parent \
+        | grep -Eo -- '^[0-9]+\.[0-9]\.[0-9]+' 2>/dev/null)
+# DIRTY identifies if we're not on a commit
+DIRTY = $(shell git describe --dirty | grep -Eo -- '-dirty' 2>/dev/null)
+
+ifeq ($(ON_TAG),)
+# 4. Not on a tag.
+COUCHDB_VERSION_SUFFIX = $(COUCHDB_GIT_SHA)$(DIRTY)
+COUCHDB_VERSION = $(vsn_major).$(vsn_minor).$(vsn_patch)-$(COUCHDB_VERSION_SUFFIX)
+else
+# 2 and 3. On a tag.
+COUCHDB_VERSION = $(RELTAG)$(DIRTY)
+endif
 endif
 
 DESTDIR=
@@ -53,7 +90,7 @@
 .PHONY: couch
 # target: couch - Build CouchDB core
 couch: config.erl
-	@set COUCHDB_VERSION=$(COUCHDB_VERSION) && $(REBAR) compile
+	@set COUCHDB_VERSION=$(COUCHDB_VERSION) && set COUCHDB_GIT_SHA=$(COUCHDB_GIT_SHA) && $(REBAR) compile
 	@copy src\couch\priv\couchjs.exe bin
 
 
diff --git a/NOTICE b/NOTICE
index 481e755..f703af2 100644
--- a/NOTICE
+++ b/NOTICE
@@ -178,13 +178,6 @@
 
   Copyright (c) 2015 Twitter, Inc.
 
-* erlang-bcrypt
-  - Erlang code: Copyright (c) 2011 Hunter Morris <hunter.morris@smarkets.com>
-  - Blowfish block cipher & bcrypt (hashing) code for OpenBSD, Copyright
-    1997 Niels Provos <provos@physnet.uni-hamburg.de>
-  - The asynchronous queue code (c_src/async_queue.c and c_src/async_queue.h)
-    is from the esnappy project, copyright 2011 Konstantin V. Sorokin.
-
 * hyper
 
   Copyright (c) 2014 Game Analytics ApS
diff --git a/build-aux/couchdb-build-release.sh b/build-aux/couchdb-build-release.sh
index 4482b71..2d219e5 100755
--- a/build-aux/couchdb-build-release.sh
+++ b/build-aux/couchdb-build-release.sh
@@ -35,8 +35,12 @@
 
 cd ..
 
-# create CONTRIBUTORS file
+
 if test -e .git; then
+    # save git sha in version.mk
+    git_sha=`git rev-parse --short HEAD`
+    echo "git_sha=$git_sha" >> $RELDIR/version.mk
+    # create CONTRIBUTORS file
     OS=`uname -s`
     case "$OS" in
     Linux|CYGWIN*) # GNU sed
diff --git a/configure b/configure
index fa0dfed..370c964 100755
--- a/configure
+++ b/configure
@@ -25,6 +25,7 @@
 WITH_CURL="false"
 WITH_FAUXTON=1
 WITH_DOCS=1
+ERLANG_MD5="false"
 SKIP_DEPS=0
 
 COUCHDB_USER="$(whoami 2>/dev/null || echo couchdb)"
@@ -46,6 +47,7 @@
   -c | --with-curl            request that couchjs is linked to cURL (default false)
   --disable-fauxton           do not build Fauxton
   --disable-docs              do not build any documentation or manpages
+  --erlang-md5                use erlang for md5 hash operations
   --dev                       alias for --with-curl --disable-docs --disable-fauxton
   --skip-deps                 do not update erlang dependencies
   --rebar=PATH                use rebar by specified path (version >=2.6.0 && <3.0 required)
@@ -78,6 +80,12 @@
                 continue
                 ;;
 
+            --erlang-md5)
+                ERLANG_MD5="true"
+                shift
+                continue
+                ;;
+
             --dev)
                 WITH_DOCS=0
                 WITH_FAUXTON=0
@@ -195,6 +203,7 @@
 
 cat > $rootdir/config.erl << EOF
 {with_curl, $WITH_CURL}.
+{erlang_md5, $ERLANG_MD5}.
 EOF
 
 install_local_rebar() {
diff --git a/rebar.config.script b/rebar.config.script
index 1411752..cd46955 100644
--- a/rebar.config.script
+++ b/rebar.config.script
@@ -56,19 +56,16 @@
 
 %% Non-Erlang deps
 {docs,             {url, "https://github.com/apache/couchdb-documentation"},
-                   {tag, "2.1.2"}, [raw]},
+                   {tag, "2.2.0"}, [raw]},
 {fauxton,          {url, "https://github.com/apache/couchdb-fauxton"},
-                   {tag, "v1.1.15"}, [raw]},
+                   {tag, "v1.1.17"}, [raw]},
 %% Third party deps
 {folsom,           "folsom",           {tag, "CouchDB-0.8.2"}},
-{hyper,            "hyper",            {tag, "CouchDB-2.2.0-3"}},
+{hyper,            "hyper",            {tag, "CouchDB-2.2.0-4"}},
 {ibrowse,          "ibrowse",          {tag, "CouchDB-4.0.1"}},
 {jiffy,            "jiffy",            {tag, "CouchDB-0.14.11-2"}},
 {mochiweb,         "mochiweb",         {tag, "v2.17.0"}},
-{meck,             "meck",             {tag, "0.8.8"}},
-{bcrypt,           {url, "https://github.com/apache/couchdb-erlang-bcrypt"},
-                   {tag, "1.0.2"}},
-{triq,             "triq",             {tag, "v1.2.0"}}
+{meck,             "meck",             {tag, "0.8.8"}}
 ],
 
 
@@ -105,7 +102,7 @@
         {plt_location, local},
         {plt_location, COUCHDB_ROOT},
         {plt_extra_apps, [
-            asn1, bcrypt, compiler, crypto, inets, kernel, os_mon, runtime_tools,
+            asn1, compiler, crypto, inets, kernel, os_mon, runtime_tools,
             sasl, setup, ssl, stdlib, syntax_tools, xmerl]},
         {warnings, [unmatched_returns, error_handling, race_conditions]}]},
     {post_hooks, [{compile, "escript support/build_js.escript"}]}
diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini
index 0f0d547..45025e7 100644
--- a/rel/overlay/etc/default.ini
+++ b/rel/overlay/etc/default.ini
@@ -83,7 +83,8 @@
 bind_address = 127.0.0.1
 backlog = 512
 docroot = {{fauxton_root}}
-socket_options = [{recbuf, 262144}, {sndbuf, 262144}, {nodelay, true}]
+socket_options = [{sndbuf, 262144}, {nodelay, true}]
+server_options = [{recbuf, undefined}]
 require_valid_user = false
 ; List of headers that will be kept when the header Prefer: return=minimal is included in a request.
 ; If Server header is left out, Mochiweb will add its own one in.
@@ -98,6 +99,9 @@
 ; uncomment the next line to enable proxy authentication
 ; authentication_handlers = {chttpd_auth, proxy_authentication_handler}, {chttpd_auth, cookie_authentication_handler}, {chttpd_auth, default_authentication_handler}
 
+; prevent non-admins from accessing /_all_dbs
+;admin_only_all_dbs = false
+
 [database_compaction]
 ; larger buffer sizes can originate smaller files
 doc_buffer_size = 524288 ; value in bytes
@@ -135,8 +139,8 @@
 ; Options for the MochiWeb HTTP server.
 ;server_options = [{backlog, 128}, {acceptor_pool_size, 16}]
 ; For more socket options, consult Erlang's module 'inet' man page.
-;socket_options = [{recbuf, 262144}, {sndbuf, 262144}, {nodelay, true}]
-socket_options = [{recbuf, 262144}, {sndbuf, 262144}]
+;socket_options = [{recbuf, undefined}, {sndbuf, 262144}, {nodelay, true}]
+socket_options = [{sndbuf, 262144}]
 enable_cors = false
 enable_xframe_options = false
 ; CouchDB can optionally enforce a maximum uri length;
@@ -149,7 +153,7 @@
 ; x_forwarded_proto = X-Forwarded-Proto
 ; x_forwarded_ssl = X-Forwarded-Ssl
 ; Maximum allowed http request size. Applies to both clustered and local port.
-max_http_request_size = 67108864 ; 64 MB
+max_http_request_size = 4294967296 ; 4GB
 
 ; [httpd_design_handlers]
 ; _view = 
@@ -203,8 +207,7 @@
 timeout = 600 ; number of seconds before automatic logout
 auth_cache_size = 50 ; size is number of cache entries
 allow_persistent_cookies = false ; set to true to allow persistent cookies
-iterations = 10 ; iterations for PBKDF2 password hashing
-log_rounds = 10 ; 2^log_rounds iterations for Bcrypt password hashing
+iterations = 10 ; iterations for password hashing
 ; min_iterations = 1
 ; max_iterations = 1000000000
 ; password_scheme = pbkdf2
@@ -431,13 +434,12 @@
 ; There are currently two plugins available:
 ;   couch_replicator_auth_session - use _session cookie authentication
 ;   couch_replicator_auth_noop - use basic authentication (previous default)
-; Currently previous default behavior is still the default. To start using
-; session auth, use this as the list of plugins:
-; `couch_replicator_auth_session,couch_replicator_auth_noop`.
-; In a future release the session plugin might be used by default.
+; Currently, the new _session cookie authentication is tried first, before
+; falling back to the old basic authenticaion default:
+;auth_plugins = couch_replicator_auth_session,couch_replicator_auth_noop
+; To restore the old behaviour, use the following value:
 ;auth_plugins = couch_replicator_auth_noop
 
-
 [compaction_daemon]
 ; The delay, in seconds, between each check for which database and view indexes
 ; need to be compacted.
diff --git a/rel/overlay/etc/local.ini b/rel/overlay/etc/local.ini
index e3b7b15..ea5467c 100644
--- a/rel/overlay/etc/local.ini
+++ b/rel/overlay/etc/local.ini
@@ -27,7 +27,7 @@
 ; Options for the MochiWeb HTTP server.
 ;server_options = [{backlog, 128}, {acceptor_pool_size, 16}]
 ; For more socket options, consult Erlang's module 'inet' man page.
-;socket_options = [{recbuf, 262144}, {sndbuf, 262144}, {nodelay, true}]
+;socket_options = [{sndbuf, 262144}, {nodelay, true}]
 
 [httpd]
 ; NOTE that this only configures the "backend" node-local port, not the
diff --git a/rel/reltool.config b/rel/reltool.config
index 5e86d96..2c55d09 100644
--- a/rel/reltool.config
+++ b/rel/reltool.config
@@ -15,7 +15,6 @@
     {rel, "couchdb", "2.2.0", [
         %% stdlib
         asn1,
-        bcrypt,
         compiler,
         crypto,
         inets,
@@ -68,7 +67,6 @@
 
     %% stdlib
     {app, asn1, [{incl_cond, include}]},
-    {app, bcrypt, [{incl_cond, include}]},
     {app, compiler, [{incl_cond, include}]},
     {app, crypto, [{incl_cond, include}]},
     {app, inets, [{incl_cond, include}]},
diff --git a/src/chttpd/src/chttpd.erl b/src/chttpd/src/chttpd.erl
index c0179ba..a562839 100644
--- a/src/chttpd/src/chttpd.erl
+++ b/src/chttpd/src/chttpd.erl
@@ -681,7 +681,7 @@
     couch_httpd:doc_etag(Id, Body, {Start, DiskRev}).
 
 make_etag(Term) ->
-    <<SigInt:128/integer>> = crypto:hash(md5, term_to_binary(Term)),
+    <<SigInt:128/integer>> = couch_hash:md5_hash(term_to_binary(Term)),
     list_to_binary(io_lib:format("\"~.36B\"",[SigInt])).
 
 etag_match(Req, CurrentEtag) when is_binary(CurrentEtag) ->
diff --git a/src/chttpd/src/chttpd_auth_request.erl b/src/chttpd/src/chttpd_auth_request.erl
index 05c5e8e..9110ed6 100644
--- a/src/chttpd/src/chttpd_auth_request.erl
+++ b/src/chttpd/src/chttpd_auth_request.erl
@@ -34,7 +34,10 @@
 authorize_request_int(#httpd{path_parts=[<<"favicon.ico">>|_]}=Req) ->
     Req;
 authorize_request_int(#httpd{path_parts=[<<"_all_dbs">>|_]}=Req) ->
-    Req;
+   case config:get_boolean("chttpd", "admin_only_all_dbs", false) of
+       true -> require_admin(Req);
+       false -> Req
+   end;
 authorize_request_int(#httpd{path_parts=[<<"_dbs_info">>|_]}=Req) ->
     Req;
 authorize_request_int(#httpd{path_parts=[<<"_replicator">>], method='PUT'}=Req) ->
diff --git a/src/chttpd/src/chttpd_misc.erl b/src/chttpd/src/chttpd_misc.erl
index 95345d4..596e014 100644
--- a/src/chttpd/src/chttpd_misc.erl
+++ b/src/chttpd/src/chttpd_misc.erl
@@ -49,6 +49,7 @@
     send_json(Req, {[
         {couchdb, WelcomeMessage},
         {version, list_to_binary(couch_server:get_version())},
+        {git_sha, list_to_binary(couch_server:get_git_sha())},
         {features, config:features()}
         ] ++ case config:get("vendor") of
         [] ->
diff --git a/src/chttpd/test/chttpd_welcome_test.erl b/src/chttpd/test/chttpd_welcome_test.erl
index af9732f..b737abd 100644
--- a/src/chttpd/test/chttpd_welcome_test.erl
+++ b/src/chttpd/test/chttpd_welcome_test.erl
@@ -60,6 +60,8 @@
         Version = couch_util:get_value(<<"version">>, Json, undefined),
         CouchDB = couch_util:get_value(<<"couchdb">>, Json, undefined),
         Features = couch_util:get_value(<<"features">>, Json, undefined),
+        Sha = couch_util:get_value(<<"git_sha">>, Json, undefined),
+        ?assertNotEqual(Sha, undefined),
         ?assertEqual(<<"Welcome">>, CouchDB),
         RealVersion = list_to_binary(couch_server:get_version()),
         ?assertEqual(RealVersion, Version),
diff --git a/src/couch/rebar.config.script b/src/couch/rebar.config.script
index 498ce3a..5321cff 100644
--- a/src/couch/rebar.config.script
+++ b/src/couch/rebar.config.script
@@ -39,6 +39,13 @@
         string:strip(Version0, right)
 end,
 
+GitSha = case os:getenv("COUCHDB_GIT_SHA") of
+    false ->
+        ""; % release builds won’t get a fallback
+    GitSha0 ->
+        string:strip(GitSha0, right)
+end,
+
 CouchConfig = case filelib:is_file(os:getenv("COUCHDB_CONFIG")) of
     true ->
         {ok, Result} = file:consult(os:getenv("COUCHDB_CONFIG")),
@@ -64,6 +71,13 @@
 ConfigBin = iolist_to_binary(ConfigSrc),
 ok = CopyIfDifferent(CouchJSConfig, ConfigBin),
 
+MD5Config = case lists:keyfind(erlang_md5, 1, CouchConfig) of
+    {erlang_md5, true} ->
+        [{d, 'ERLANG_MD5', true}];
+    _ ->
+        []
+end,
+
 %% TODO support curl on Windows
 {JS_CFLAGS, JS_LDFLAGS} = case lists:keyfind(with_curl, 1, CouchConfig) of
     {with_curl, true} ->
@@ -142,8 +156,9 @@
     {port_specs, PortSpecs},
     {erl_opts, PlatformDefines ++ [
         {d, 'COUCHDB_VERSION', Version},
+        {d, 'COUCHDB_GIT_SHA', GitSha},
         {i, "../"}
-    ]},
+    ] ++ MD5Config},
     {eunit_compile_opts, PlatformDefines}
 ].
 
diff --git a/src/couch/src/couch.app.src b/src/couch/src/couch.app.src
index cf5cee6..6af2136 100644
--- a/src/couch/src/couch.app.src
+++ b/src/couch/src/couch.app.src
@@ -31,7 +31,6 @@
         kernel,
         stdlib,
         crypto,
-        bcrypt,
         sasl,
         inets,
         ssl,
diff --git a/src/couch/src/couch.erl b/src/couch/src/couch.erl
index f956b4b..fd5c9e1 100644
--- a/src/couch/src/couch.erl
+++ b/src/couch/src/couch.erl
@@ -21,7 +21,6 @@
         inets,
         os_mon,
         crypto,
-        bcrypt,
         public_key,
         ssl,
         ibrowse,
diff --git a/src/couch/src/couch_auth_cache.erl b/src/couch/src/couch_auth_cache.erl
index 425cce0..157b090 100644
--- a/src/couch/src/couch_auth_cache.erl
+++ b/src/couch/src/couch_auth_cache.erl
@@ -92,8 +92,6 @@
     "-pbkdf2-" ++ HashedPwdSaltAndIterations ->
         [HashedPwd, Salt, Iterations] = string:tokens(HashedPwdSaltAndIterations, ","),
         make_admin_doc(HashedPwd, Salt, Iterations);
-    "-bcrypt-" ++ HashedPwd ->
-        make_admin_doc(HashedPwd);
     _Else ->
 	nil
     end.
@@ -111,11 +109,6 @@
      {<<"password_scheme">>, <<"pbkdf2">>},
      {<<"derived_key">>, ?l2b(DerivedKey)}].
 
-make_admin_doc(DerivedKey) ->
-    [{<<"roles">>, [<<"_admin">>]},
-     {<<"password_scheme">>, <<"bcrypt">>},
-     {<<"derived_key">>, ?l2b(DerivedKey)}].
-
 get_from_cache(UserName) ->
     exec_if_auth_db(
         fun(_AuthDb) ->
diff --git a/src/couch/src/couch_bt_engine.erl b/src/couch/src/couch_bt_engine.erl
index a42d116..ee0d6d8 100644
--- a/src/couch/src/couch_bt_engine.erl
+++ b/src/couch/src/couch_bt_engine.erl
@@ -330,7 +330,7 @@
     Body = Compress(Doc#doc.body),
     Atts = Compress(Doc#doc.atts),
     SummaryBin = ?term_to_bin({Body, Atts}),
-    Md5 = crypto:hash(md5, SummaryBin),
+    Md5 = couch_hash:md5_hash(SummaryBin),
     Data = couch_file:assemble_file_chunk(SummaryBin, Md5),
     % TODO: This is a terrible hack to get around the issues
     %       in COUCHDB-3255. We'll need to come back and figure
diff --git a/src/couch/src/couch_db.erl b/src/couch/src/couch_db.erl
index b47cc7e..65ca54a 100644
--- a/src/couch/src/couch_db.erl
+++ b/src/couch/src/couch_db.erl
@@ -938,7 +938,7 @@
             ?l2b(integer_to_list(couch_util:rand32()));
         Atts2 ->
             OldRev = case OldRevs of [] -> 0; [OldRev0|_] -> OldRev0 end,
-            crypto:hash(md5, term_to_binary([Deleted, OldStart, OldRev, Body, Atts2], [{minor_version, 1}]))
+            couch_hash:md5_hash(term_to_binary([Deleted, OldStart, OldRev, Body, Atts2], [{minor_version, 1}]))
     end.
 
 new_revs([], OutBuckets, IdRevsAcc) ->
diff --git a/src/couch/src/couch_file.erl b/src/couch/src/couch_file.erl
index 9f668ea..55cb956 100644
--- a/src/couch/src/couch_file.erl
+++ b/src/couch/src/couch_file.erl
@@ -132,7 +132,7 @@
     
 append_binary_md5(Fd, Bin) ->
     ioq:call(Fd,
-        {append_bin, assemble_file_chunk(Bin, crypto:hash(md5, Bin))},
+        {append_bin, assemble_file_chunk(Bin, couch_hash:md5_hash(Bin))},
         erlang:get(io_priority)).
 
 append_raw_chunk(Fd, Chunk) ->
@@ -175,7 +175,7 @@
     {ok, IoList, <<>>} ->
         {ok, IoList};
     {ok, IoList, Md5} ->
-        case crypto:hash(md5, IoList) of
+        case couch_hash:md5_hash(IoList) of
         Md5 ->
             {ok, IoList};
         _ ->
@@ -333,7 +333,7 @@
 
 write_header(Fd, Data) ->
     Bin = term_to_binary(Data),
-    Md5 = crypto:hash(md5, Bin),
+    Md5 = couch_hash:md5_hash(Bin),
     % now we assemble the final header binary and write to disk
     FinalBin = <<Md5/binary, Bin/binary>>,
     ioq:call(Fd, {write_header, FinalBin}, erlang:get(io_priority)).
@@ -559,7 +559,7 @@
     end,
     <<Md5Sig:16/binary, HeaderBin/binary>> =
         iolist_to_binary(remove_block_prefixes(?PREFIX_SIZE, RawBin)),
-    Md5Sig = crypto:hash(md5, HeaderBin),
+    Md5Sig = couch_hash:md5_hash(HeaderBin),
     {ok, HeaderBin}.
 
 
diff --git a/src/couch/src/couch_hash.erl b/src/couch/src/couch_hash.erl
new file mode 100644
index 0000000..842b374
--- /dev/null
+++ b/src/couch/src/couch_hash.erl
@@ -0,0 +1,45 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_hash).
+
+-export([md5_hash/1, md5_hash_final/1, md5_hash_init/0, md5_hash_update/2]).
+
+-ifdef(ERLANG_MD5).
+
+md5_hash(Data) ->
+    erlang:md5(Data).
+
+md5_hash_final(Context) ->
+    erlang:md5_final(Context).
+
+md5_hash_init() ->
+    erlang:md5_init().
+
+md5_hash_update(Context, Data) ->
+    erlang:md5_update(Context, Data).
+
+-else.
+
+md5_hash(Data) ->
+    crypto:hash(md5, Data).
+
+md5_hash_final(Context) ->
+    crypto:hash_final(Context).
+
+md5_hash_init() ->
+    crypto:hash_init(md5).
+
+md5_hash_update(Context, Data) ->
+    crypto:hash_update(Context, Data).
+
+-endif.
diff --git a/src/couch/src/couch_httpd.erl b/src/couch/src/couch_httpd.erl
index 050282a..a8cfca6 100644
--- a/src/couch/src/couch_httpd.erl
+++ b/src/couch/src/couch_httpd.erl
@@ -622,7 +622,7 @@
      <<$", Rev/binary, $">>.
 
 make_etag(Term) ->
-    <<SigInt:128/integer>> = crypto:hash(md5, term_to_binary(Term)),
+    <<SigInt:128/integer>> = couch_hash:md5_hash(term_to_binary(Term)),
     iolist_to_binary([$", io_lib:format("~.36B", [SigInt]), $"]).
 
 etag_match(Req, CurrentEtag) when is_binary(CurrentEtag) ->
diff --git a/src/couch/src/couch_httpd_auth.erl b/src/couch/src/couch_httpd_auth.erl
index 74cbe5a..6ac7b75 100644
--- a/src/couch/src/couch_httpd_auth.erl
+++ b/src/couch/src/couch_httpd_auth.erl
@@ -309,12 +309,7 @@
             Secret = ?l2b(ensure_cookie_auth_secret()),
             UserSalt = couch_util:get_value(<<"salt">>, UserProps),
             CurrentTime = make_cookie_time(),
-            Cookie = case UserSalt of
-                undefined ->
-                    cookie_auth_cookie(Req, ?b2l(UserName), <<Secret/binary>>, CurrentTime);
-                _ ->
-                    cookie_auth_cookie(Req, ?b2l(UserName), <<Secret/binary, UserSalt/binary>>, CurrentTime)
-            end,
+            Cookie = cookie_auth_cookie(Req, ?b2l(UserName), <<Secret/binary, UserSalt/binary>>, CurrentTime),
             % TODO document the "next" feature in Futon
             {Code, Headers} = case couch_httpd:qs_value(Req, "next", nil) of
                 nil ->
@@ -406,10 +401,7 @@
             Iterations = couch_util:get_value(<<"iterations">>, UserProps, 10000),
             verify_iterations(Iterations),
             {couch_passwords:pbkdf2(Pass, UserSalt, Iterations),
-             couch_util:get_value(<<"derived_key">>, UserProps, nil)};
-        <<"bcrypt">> ->
-            UserHash = couch_util:get_value(<<"derived_key">>, UserProps, nil),
-            {couch_passwords:bcrypt(Pass, UserHash), UserHash}
+             couch_util:get_value(<<"derived_key">>, UserProps, nil)}
     end,
     couch_passwords:verify(PasswordHash, ExpectedHash).
 
diff --git a/src/couch/src/couch_native_process.erl b/src/couch/src/couch_native_process.erl
index 8f8ce8b..eee8b28 100644
--- a/src/couch/src/couch_native_process.erl
+++ b/src/couch/src/couch_native_process.erl
@@ -363,11 +363,11 @@
 % thanks to erlview, via:
 % http://erlang.org/pipermail/erlang-questions/2003-November/010544.html
 makefun(State, Source) ->
-    Sig = crypto:hash(md5, Source),
+    Sig = couch_hash:md5_hash(Source),
     BindFuns = bindings(State, Sig),
     {Sig, makefun(State, Source, BindFuns)}.
 makefun(State, Source, {DDoc}) ->
-    Sig = crypto:hash(md5, lists:flatten([Source, term_to_binary(DDoc)])),
+    Sig = couch_hash:md5_hash(lists:flatten([Source, term_to_binary(DDoc)])),
     BindFuns = bindings(State, Sig, {DDoc}),
     {Sig, makefun(State, Source, BindFuns)};
 makefun(_State, Source, BindFuns) when is_list(BindFuns) ->
diff --git a/src/couch/src/couch_passwords.erl b/src/couch/src/couch_passwords.erl
index 77e1361..baf78f5 100644
--- a/src/couch/src/couch_passwords.erl
+++ b/src/couch/src/couch_passwords.erl
@@ -12,7 +12,7 @@
 
 -module(couch_passwords).
 
--export([simple/2, pbkdf2/3, pbkdf2/4, bcrypt/2, verify/2]).
+-export([simple/2, pbkdf2/3, pbkdf2/4, verify/2]).
 -export([hash_admin_password/1, get_unhashed_admins/0]).
 
 -include_lib("couch/include/couch_db.hrl").
@@ -51,10 +51,7 @@
                                         Salt ,list_to_integer(Iterations)),
     ?l2b("-pbkdf2-" ++ ?b2l(DerivedKey) ++ ","
         ++ ?b2l(Salt) ++ ","
-        ++ Iterations);
-hash_admin_password("bcrypt", ClearPassword) ->
-    LogRounds = list_to_integer(config:get("couch_httpd_auth", "log_rounds", "10")),
-    ?l2b("-bcrypt-" ++ couch_passwords:bcrypt(couch_util:to_binary(ClearPassword), LogRounds)).
+        ++ Iterations).
 
 -spec get_unhashed_admins() -> list().
 get_unhashed_admins() ->
@@ -63,8 +60,6 @@
             false; % already hashed
         ({_User, "-pbkdf2-" ++ _}) ->
             false; % already hashed
-        ({_User, "-bcrypt-" ++ _}) ->
-            false; % already hashed
         ({_User, _ClearPassword}) ->
             true
         end,
@@ -128,16 +123,6 @@
     pbkdf2(Password, Salt, Iterations, BlockIndex, Iteration + 1,
                    Next, crypto:exor(Next, Acc)).
 
-%% Define the bcrypt functions to hash a password
--spec bcrypt(binary(), binary()) -> binary();
-    (binary(), integer()) -> binary().
-bcrypt(Password, Salt) when is_binary(Salt) ->
-    {ok, Hash} = bcrypt:hashpw(Password, Salt),
-    list_to_binary(Hash);
-bcrypt(Password, LogRounds) when is_integer(LogRounds) ->
-    {ok, Salt} = bcrypt:gen_salt(LogRounds),
-    bcrypt(Password, list_to_binary(Salt)).
-
 %% verify two lists for equality without short-circuits to avoid timing attacks.
 -spec verify(string(), string(), integer()) -> boolean().
 verify([X|RestX], [Y|RestY], Result) ->
diff --git a/src/couch/src/couch_query_servers.erl b/src/couch/src/couch_query_servers.erl
index de8ef1e..7047364 100644
--- a/src/couch/src/couch_query_servers.erl
+++ b/src/couch/src/couch_query_servers.erl
@@ -90,11 +90,8 @@
 finalize(<<"_approx_count_distinct",_/binary>>, Reduction) ->
     true = hyper:is_hyper(Reduction),
     {ok, round(hyper:card(Reduction))};
-finalize(<<"_stats",_/binary>>, {_, _, _, _, _} = Unpacked) ->
+finalize(<<"_stats",_/binary>>, Unpacked) ->
     {ok, pack_stats(Unpacked)};
-finalize(<<"_stats",_/binary>>, {Packed}) ->
-    % Legacy code path before we had the finalize operation
-    {ok, {Packed}};
 finalize(_RedSrc, Reduction) ->
     {ok, Reduction}.
 
@@ -299,8 +296,12 @@
       get_number(<<"sumsqr">>, PreRed)
     }.
 
+
 pack_stats({Sum, Cnt, Min, Max, Sqr}) ->
     {[{<<"sum">>,Sum}, {<<"count">>,Cnt}, {<<"min">>,Min}, {<<"max">>,Max}, {<<"sumsqr">>,Sqr}]};
+pack_stats({Packed}) ->
+    % Legacy code path before we had the finalize operation
+    {Packed};
 pack_stats(Stats) when is_list(Stats) ->
     lists:map(fun pack_stats/1, Stats).
 
@@ -609,4 +610,49 @@
                   {18, 2, 5, 13, 194}
                  ], stat_values([2,3,5], [7,11,13])).
 
+reduce_stats_test() ->
+    ?assertEqual([
+        {[{<<"sum">>,2},{<<"count">>,1},{<<"min">>,2},{<<"max">>,2},{<<"sumsqr">>,4}]}
+    ], test_reduce(<<"_stats">>, [[[null, key], 2]])),
+
+    ?assertEqual([[
+        {[{<<"sum">>,1},{<<"count">>,1},{<<"min">>,1},{<<"max">>,1},{<<"sumsqr">>,1}]},
+        {[{<<"sum">>,2},{<<"count">>,1},{<<"min">>,2},{<<"max">>,2},{<<"sumsqr">>,4}]}
+    ]], test_reduce(<<"_stats">>, [[[null, key],[1,2]]])),
+
+    ?assertEqual(
+         {[{<<"sum">>,2},{<<"count">>,1},{<<"min">>,2},{<<"max">>,2},{<<"sumsqr">>,4}]}
+    , element(2, finalize(<<"_stats">>, {2, 1, 2, 2, 4}))),
+
+    ?assertEqual([
+        {[{<<"sum">>,1},{<<"count">>,1},{<<"min">>,1},{<<"max">>,1},{<<"sumsqr">>,1}]},
+        {[{<<"sum">>,2},{<<"count">>,1},{<<"min">>,2},{<<"max">>,2},{<<"sumsqr">>,4}]}
+    ], element(2, finalize(<<"_stats">>, [
+        {1, 1, 1, 1, 1},
+        {2, 1, 2, 2, 4}
+    ]))),
+
+    ?assertEqual([
+        {[{<<"sum">>,1},{<<"count">>,1},{<<"min">>,1},{<<"max">>,1},{<<"sumsqr">>,1}]},
+        {[{<<"sum">>,2},{<<"count">>,1},{<<"min">>,2},{<<"max">>,2},{<<"sumsqr">>,4}]}
+    ], element(2, finalize(<<"_stats">>, [
+        {1, 1, 1, 1, 1},
+        {[{<<"sum">>,2},{<<"count">>,1},{<<"min">>,2},{<<"max">>,2},{<<"sumsqr">>,4}]}
+    ]))),
+
+    ?assertEqual([
+        {[{<<"sum">>,1},{<<"count">>,1},{<<"min">>,1},{<<"max">>,1},{<<"sumsqr">>,1}]},
+        {[{<<"sum">>,2},{<<"count">>,1},{<<"min">>,2},{<<"max">>,2},{<<"sumsqr">>,4}]}
+    ], element(2, finalize(<<"_stats">>, [
+        {[{<<"sum">>,1},{<<"count">>,1},{<<"min">>,1},{<<"max">>,1},{<<"sumsqr">>,1}]},
+        {2, 1, 2, 2, 4}
+    ]))),
+    ok.
+
+test_reduce(Reducer, KVs) ->
+    ?assertMatch({ok, _}, reduce(<<"javascript">>, [Reducer], KVs)),
+    {ok, Reduced} = reduce(<<"javascript">>, [Reducer], KVs),
+    {ok, Finalized} = finalize(Reducer, Reduced),
+    Finalized.
+
 -endif.
diff --git a/src/couch/src/couch_server.erl b/src/couch/src/couch_server.erl
index 002f08e..ede8227 100644
--- a/src/couch/src/couch_server.erl
+++ b/src/couch/src/couch_server.erl
@@ -15,7 +15,7 @@
 -behaviour(config_listener).
 -vsn(3).
 
--export([open/2,create/2,delete/2,get_version/0,get_version/1,get_uuid/0]).
+-export([open/2,create/2,delete/2,get_version/0,get_version/1,get_git_sha/0,get_uuid/0]).
 -export([all_databases/0, all_databases/2]).
 -export([init/1, handle_call/3,sup_start_link/0]).
 -export([handle_cast/2,code_change/3,handle_info/2,terminate/2]).
@@ -57,6 +57,7 @@
   [Version|_Rest] = string:tokens(get_version(), "+"),
   Version.
 
+get_git_sha() -> ?COUCHDB_GIT_SHA.
 
 get_uuid() ->
     case config:get("couchdb", "uuid", undefined) of
diff --git a/src/couch/src/couch_stream.erl b/src/couch/src/couch_stream.erl
index 83b0611..0335629 100644
--- a/src/couch/src/couch_stream.erl
+++ b/src/couch/src/couch_stream.erl
@@ -98,9 +98,9 @@
 foldl(Engine, <<>>, Fun, Acc) ->
     foldl(Engine, Fun, Acc);
 foldl(Engine, Md5, UserFun, UserAcc) ->
-    InitAcc = {crypto:hash_init(md5), UserFun, UserAcc},
+    InitAcc = {couch_hash:md5_hash_init(), UserFun, UserAcc},
     {Md5Acc, _, OutAcc} = foldl(Engine, fun foldl_md5/2, InitAcc),
-    Md5 = crypto:hash_final(Md5Acc),
+    Md5 = couch_hash:md5_hash_final(Md5Acc),
     OutAcc.
 
 
@@ -128,7 +128,7 @@
 
 
 foldl_md5(Bin, {Md5Acc, UserFun, UserAcc}) ->
-    NewMd5Acc = crypto:hash_update(Md5Acc, Bin),
+    NewMd5Acc = couch_hash:md5_hash_update(Md5Acc, Bin),
     {NewMd5Acc, UserFun, UserFun(Bin, UserAcc)}.
 
 
@@ -201,8 +201,8 @@
     {ok, #stream{
             engine=Engine,
             opener_monitor=erlang:monitor(process, OpenerPid),
-            md5=crypto:hash_init(md5),
-            identity_md5=crypto:hash_init(md5),
+            md5=couch_hash:md5_hash_init(),
+            identity_md5=couch_hash:md5_hash_init(),
             encoding_fun=EncodingFun,
             end_encoding_fun=EndEncodingFun,
             max_buffer=couch_util:get_value(
@@ -227,7 +227,7 @@
         encoding_fun = EncodingFun} = Stream,
     if BinSize + BufferLen > Max ->
         WriteBin = lists:reverse(Buffer, [Bin]),
-        IdenMd5_2 = crypto:hash_update(IdenMd5, WriteBin),
+        IdenMd5_2 = couch_hash:md5_hash_update(IdenMd5, WriteBin),
         case EncodingFun(WriteBin) of
         [] ->
             % case where the encoder did some internal buffering
@@ -238,7 +238,7 @@
         WriteBin2 ->
             NewEngine = do_write(Engine, WriteBin2),
             WrittenLen2 = WrittenLen + iolist_size(WriteBin2),
-            Md5_2 = crypto:hash_update(Md5, WriteBin2)
+            Md5_2 = couch_hash:md5_hash_update(Md5, WriteBin2)
         end,
 
         {reply, ok, Stream#stream{
@@ -268,9 +268,9 @@
         end_encoding_fun = EndEncodingFun} = Stream,
 
     WriteBin = lists:reverse(Buffer),
-    IdenMd5Final = crypto:hash_final(crypto:hash_update(IdenMd5, WriteBin)),
+    IdenMd5Final = couch_hash:md5_hash_final(couch_hash:md5_hash_update(IdenMd5, WriteBin)),
     WriteBin2 = EncodingFun(WriteBin) ++ EndEncodingFun(),
-    Md5Final = crypto:hash_final(crypto:hash_update(Md5, WriteBin2)),
+    Md5Final = couch_hash:md5_hash_final(couch_hash:md5_hash_update(Md5, WriteBin2)),
     Result = case WriteBin2 of
     [] ->
         {do_finalize(Engine), WrittenLen, IdenLen, Md5Final, IdenMd5Final};
diff --git a/src/couch/src/couch_users_db.erl b/src/couch/src/couch_users_db.erl
index dd6d320..c7b41f1 100644
--- a/src/couch/src/couch_users_db.erl
+++ b/src/couch/src/couch_users_db.erl
@@ -23,7 +23,6 @@
 -define(SIMPLE, <<"simple">>).
 -define(PASSWORD_SHA, <<"password_sha">>).
 -define(PBKDF2, <<"pbkdf2">>).
--define(BCRYPT, <<"bcrypt">>).
 -define(ITERATIONS, <<"iterations">>).
 -define(SALT, <<"salt">>).
 -define(replace(L, K, V), lists:keystore(K, 1, L, {K, V})).
@@ -60,7 +59,7 @@
 %    newDoc.salt = salt
 %    newDoc.password = null
 save_doc(#doc{body={Body}} = Doc) ->
-    %% Support all schemes to smooth migration from legacy scheme
+    %% Support both schemes to smooth migration from legacy scheme
     Scheme = config:get("couch_httpd_auth", "password_scheme", "pbkdf2"),
     case {couch_util:get_value(?PASSWORD, Body), Scheme} of
     {null, _} -> % server admins don't have a user-db password entry
@@ -85,13 +84,6 @@
         Body3 = ?replace(Body2, ?SALT, Salt),
         Body4 = proplists:delete(?PASSWORD, Body3),
         Doc#doc{body={Body4}};
-    {ClearPassword, "bcrypt"} ->
-        LogRounds = list_to_integer(config:get("couch_httpd_auth", "log_rounds", "10")),
-        DerivedKey = couch_passwords:bcrypt(ClearPassword, LogRounds),
-        Body0 = ?replace(Body, ?PASSWORD_SCHEME, ?BCRYPT),
-        Body1 = ?replace(Body0, ?DERIVED_KEY, DerivedKey),
-        Body2 = proplists:delete(?PASSWORD, Body1),
-        Doc#doc{body={Body2}};
     {_ClearPassword, Scheme} ->
         couch_log:error("[couch_httpd_auth] password_scheme value of '~p' is invalid.", [Scheme]),
         throw({forbidden, "Server cannot hash passwords at this time."})
diff --git a/src/couch/src/test_engine_util.erl b/src/couch/src/test_engine_util.erl
index fef9e9f..6cc6bcc 100644
--- a/src/couch/src/test_engine_util.erl
+++ b/src/couch/src/test_engine_util.erl
@@ -186,7 +186,7 @@
     [not_found] = Engine:open_docs(St, [DocId]),
     Atts = [couch_att:to_disk_term(Att) || Att <- Atts0],
 
-    Rev = crypto:hash(md5, term_to_binary({DocId, Body, Atts})),
+    Rev = couch_hash:md5_hash(term_to_binary({DocId, Body, Atts})),
 
     Doc0 = #doc{
         id = DocId,
@@ -323,11 +323,11 @@
 
 
 gen_revision(conflict, DocId, _PrevRev, Body, Atts) ->
-    crypto:hash(md5, term_to_binary({DocId, Body, Atts}));
+    couch_hash:md5_hash(term_to_binary({DocId, Body, Atts}));
 gen_revision(delete, DocId, PrevRev, Body, Atts) ->
     gen_revision(update, DocId, PrevRev, Body, Atts);
 gen_revision(update, DocId, PrevRev, Body, Atts) ->
-    crypto:hash(md5, term_to_binary({DocId, PrevRev, Body, Atts})).
+    couch_hash:md5_hash(term_to_binary({DocId, PrevRev, Body, Atts})).
 
 
 gen_path(conflict, _RevPos, _PrevRevId, Rev, Leaf) ->
@@ -373,7 +373,7 @@
 
 write_att(Stream, FileName, OrigData, <<>>) ->
     {StreamEngine, Len, Len, Md5, Md5} = couch_stream:close(Stream),
-    couch_util:check_md5(Md5, crypto:hash(md5, OrigData)),
+    couch_util:check_md5(Md5, couch_hash:md5_hash(OrigData)),
     Len = size(OrigData),
     couch_att:new([
         {name, FileName},
diff --git a/src/couch/src/test_request.erl b/src/couch/src/test_request.erl
index 4dfde1a..48f49bd 100644
--- a/src/couch/src/test_request.erl
+++ b/src/couch/src/test_request.erl
@@ -101,7 +101,11 @@
         {error, {'EXIT', {normal, _}}} ->
             % Connection closed right after a successful request that
             % used the same connection.
-            request(Method, Url, Headers, Body, N - 1);
+            request(Method, Url, Headers, Body, Opts, N - 1);
+        {error, retry_later} ->
+            % CouchDB is busy, let’s wait a bit
+            timer:sleep(3000 div N),
+            request(Method, Url, Headers, Body, Opts, N - 1);
         Error ->
             Error
     end.
diff --git a/src/couch/src/test_util.erl b/src/couch/src/test_util.erl
index 738e9a3..efb5064 100644
--- a/src/couch/src/test_util.erl
+++ b/src/couch/src/test_util.erl
@@ -101,6 +101,9 @@
         io:format(standard_error, "Application ~s was left running!~n", [App]),
         application:stop(App),
         start_applications([App|Apps], Acc);
+    {error, Reason} ->
+        io:format(standard_error, "Cannot start application '~s', reason ~p~n", [App, Reason]),
+        throw({error, {cannot_start, App, Reason}});
     ok ->
         start_applications(Apps, [App|Acc])
     end.
diff --git a/src/couch/test/couch_passwords_tests.erl b/src/couch/test/couch_passwords_tests.erl
index a566273..dea6d6b 100644
--- a/src/couch/test/couch_passwords_tests.erl
+++ b/src/couch/test/couch_passwords_tests.erl
@@ -14,6 +14,7 @@
 
 -include_lib("couch/include/couch_eunit.hrl").
 
+
 pbkdf2_test_()->
     {"PBKDF2",
      [
@@ -51,44 +52,3 @@
                {ok, <<"eefe3d61cd4da4e4e9945b3d6ba2158c2634e984">>},
                couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 16777216, 20)
            )}}]}.
-
-
-bcrypt_test_() ->
-    {
-        "Bcrypt",
-        {
-            setup,
-            fun() ->
-                test_util:start_applications([bcrypt])
-            end,
-            fun test_util:stop_applications/1,
-            [
-                {"Log rounds: 4",
-                {timeout, 1, fun bcrypt_logRounds_4/0}},
-                {"Log rounds: 5",
-                {timeout, 1, fun bcrypt_logRounds_5/0}},
-                {"Log rounds: 12",
-                {timeout, 5, fun bcrypt_logRounds_12/0}},
-                {"Null byte",
-                {timeout, 5, fun bcrypt_null_byte/0}}
-
-            ]
-        }
-    }.
-
-bcrypt_logRounds_4() ->
-    bcrypt_assert_equal(<<"password">>, 4).
-
-bcrypt_logRounds_5() ->
-    bcrypt_assert_equal(<<"password">>, 5).
-
-bcrypt_logRounds_12() ->
-    bcrypt_assert_equal(<<"password">>, 12).
-
-bcrypt_null_byte() ->
-    bcrypt_assert_equal(<<"passw\0rd">>, 12).
-
-bcrypt_assert_equal(Password, Rounds) when is_integer(Rounds) ->
-    HashPass = couch_passwords:bcrypt(Password, Rounds),
-    ReHashPass = couch_passwords:bcrypt(Password, HashPass),
-    ?assertEqual(HashPass, ReHashPass).
diff --git a/src/couch/test/couchdb_attachments_tests.erl b/src/couch/test/couchdb_attachments_tests.erl
index a85a01f..04859db 100644
--- a/src/couch/test/couchdb_attachments_tests.erl
+++ b/src/couch/test/couchdb_attachments_tests.erl
@@ -208,7 +208,7 @@
         Headers = [
             {"Content-Length", "34"},
             {"Content-Type", "text/plain"},
-            {"Content-MD5", ?b2l(base64:encode(crypto:hash(md5, Body)))},
+            {"Content-MD5", ?b2l(base64:encode(couch_hash:md5_hash(Body)))},
             {"Host", Host}
         ],
         {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
@@ -224,7 +224,7 @@
         Body = [chunked_body([Part1, Part2]), "\r\n"],
         Headers = [
             {"Content-Type", "text/plain"},
-            {"Content-MD5", ?b2l(base64:encode(crypto:hash(md5, AttData)))},
+            {"Content-MD5", ?b2l(base64:encode(couch_hash:md5_hash(AttData)))},
             {"Host", Host},
             {"Transfer-Encoding", "chunked"}
         ],
@@ -239,7 +239,7 @@
         AttData = <<"We all live in a yellow submarine!">>,
         <<Part1:21/binary, Part2:13/binary>> = AttData,
         Body = [chunked_body([Part1, Part2]),
-                "Content-MD5: ", base64:encode(crypto:hash(md5, AttData)),
+                "Content-MD5: ", base64:encode(couch_hash:md5_hash(AttData)),
                 "\r\n\r\n"],
         Headers = [
             {"Content-Type", "text/plain"},
diff --git a/src/couch_epi/src/couch_epi_data.erl b/src/couch_epi/src/couch_epi_data.erl
index 93e39f6..bbed828 100644
--- a/src/couch_epi/src/couch_epi_data.erl
+++ b/src/couch_epi/src/couch_epi_data.erl
@@ -111,4 +111,4 @@
 
 hash_of_file(FilePath) ->
     {ok, Data} = file:read_file(FilePath),
-    crypto:hash(md5, Data).
+    couch_hash:md5_hash(Data).
diff --git a/src/couch_epi/src/couch_epi_util.erl b/src/couch_epi/src/couch_epi_util.erl
index e99db46..ea4b10e 100644
--- a/src/couch_epi/src/couch_epi_util.erl
+++ b/src/couch_epi/src/couch_epi_util.erl
@@ -22,7 +22,7 @@
     VSNs.
 
 hash(Term) ->
-    <<SigInt:128/integer>> = crypto:hash(md5, term_to_binary(Term)),
+    <<SigInt:128/integer>> = couch_hash:md5_hash(term_to_binary(Term)),
     lists:flatten(io_lib:format("\"~.36B\"",[SigInt])).
 
 module_exists(Module) ->
diff --git a/src/couch_event/src/couch_event_os_sup.erl b/src/couch_event/src/couch_event_os_sup.erl
deleted file mode 100644
index f219d00..0000000
--- a/src/couch_event/src/couch_event_os_sup.erl
+++ /dev/null
@@ -1,82 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
-
-% This causes an OS process to spawned and it is notified every time a database
-% is updated.
-%
-% The notifications are in the form of a the database name sent as a line of
-% text to the OS processes stdout.
-
-
--module(couch_event_os_sup).
--behaviour(supervisor).
--behaviour(config_listener).
-
--vsn(2).
-
--export([
-    start_link/0,
-    init/1
-]).
-
--export([
-    handle_config_change/5,
-    handle_config_terminate/3
-]).
-
-
-start_link() ->
-    supervisor:start_link({local, ?MODULE}, ?MODULE, []).
-
-
-init([]) ->
-    UpdateNotifierExes = config:get("update_notification"),
-    Children = [
-        {
-            config_listener_mon,
-            {config_listener_mon, start_link, [?MODULE, nil]},
-            permanent,
-            5000,
-            worker,
-            [config_listener_mon]
-        }
-    | [child(Id, Exe) || {Id, Exe} <- UpdateNotifierExes]],
-
-    {ok, {
-        {one_for_one, 10, 3600},
-        Children
-    }}.
-
-
-handle_config_change("update_notification", Id, deleted, _, _) ->
-    supervisor:terminate_child(?MODULE, Id),
-    supervisor:delete_child(?MODULE, Id),
-    {ok, nil};
-handle_config_change("update_notification", Id, Exe, _, _) when is_list(Exe) ->
-    supervisor:start_child(?MODULE, child(Id, Exe)),
-    {ok, nil};
-handle_config_change(_, _, _, _, _) ->
-    {ok, nil}.
-
-handle_config_terminate(_Server, _Reason, _State) ->
-    ok.
-
-child(Id, Arg) ->
-    {
-        Id,
-        {couch_event_os_listener, start_link, [Arg]},
-        permanent,
-        1000,
-        supervisor,
-        [couch_event_os_listener]
-    }.
diff --git a/src/couch_event/src/couch_event_sup2.erl b/src/couch_event/src/couch_event_sup2.erl
index 36fbe54..2d88b93 100644
--- a/src/couch_event/src/couch_event_sup2.erl
+++ b/src/couch_event/src/couch_event_sup2.erl
@@ -38,13 +38,6 @@
             5000,
             worker,
             [couch_event_server]
-        },
-        {couch_event_os_sup,
-            {couch_event_os_sup, start_link, []},
-            permanent,
-            5000,
-            supervisor,
-            [couch_event_os_sup]
         }
     ],
     {ok, {{one_for_one, 5, 10}, Children}}.
diff --git a/src/couch_index/test/couch_index_ddoc_updated_tests.erl b/src/couch_index/test/couch_index_ddoc_updated_tests.erl
index 40dadcc..0e23adf 100644
--- a/src/couch_index/test/couch_index_ddoc_updated_tests.erl
+++ b/src/couch_index/test/couch_index_ddoc_updated_tests.erl
@@ -118,7 +118,7 @@
         (idx_name, {_DbName, DDoc}) ->
             DDoc#doc.id;
         (signature, {_DbName, DDoc}) ->
-            crypto:hash(md5, term_to_binary(DDoc));
+            couch_hash:md5_hash(term_to_binary(DDoc));
         (update_seq, Seq) ->
             Seq
     end),
diff --git a/src/couch_mrview/src/couch_mrview.erl b/src/couch_mrview/src/couch_mrview.erl
index 82bbd79..533dd2d 100644
--- a/src/couch_mrview/src/couch_mrview.erl
+++ b/src/couch_mrview/src/couch_mrview.erl
@@ -220,7 +220,7 @@
 query_all_docs(Db, Args0, Callback, Acc) ->
     Sig = couch_util:with_db(Db, fun(WDb) ->
         {ok, Info} = couch_db:get_db_info(WDb),
-        couch_index_util:hexsig(crypto:hash(md5, term_to_binary(Info)))
+        couch_index_util:hexsig(couch_hash:md5_hash(term_to_binary(Info)))
     end),
     Args1 = Args0#mrargs{view_type=map},
     Args2 = couch_mrview_util:validate_args(Args1),
diff --git a/src/couch_mrview/src/couch_mrview_util.erl b/src/couch_mrview/src/couch_mrview_util.erl
index eb461d0..120a9b8 100644
--- a/src/couch_mrview/src/couch_mrview_util.erl
+++ b/src/couch_mrview/src/couch_mrview_util.erl
@@ -157,7 +157,7 @@
         keyseq_indexed=KeySeqIndexed
     },
     SigInfo = {Views, Language, DesignOpts, couch_index_util:sort_lib(Lib)},
-    {ok, IdxState#mrst{sig=crypto:hash(md5, term_to_binary(SigInfo))}}.
+    {ok, IdxState#mrst{sig=couch_hash:md5_hash(term_to_binary(SigInfo))}}.
 
 
 set_view_type(_Args, _ViewName, []) ->
@@ -203,7 +203,7 @@
         keyseq_indexed=KeySeqIndexed
     } = State,
     Term = view_sig_term(BaseSig, UpdateSeq, PurgeSeq, KeySeqIndexed, SeqIndexed),
-    couch_index_util:hexsig(crypto:hash(md5, term_to_binary(Term)));
+    couch_index_util:hexsig(couch_hash:md5_hash(term_to_binary(Term)));
 view_sig(Db, State, {_Nth, _Lang, View}, Args) ->
     view_sig(Db, State, View, Args);
 view_sig(_Db, State, View, Args0) ->
@@ -217,7 +217,7 @@
         extra=[]
     },
     Term = view_sig_term(Sig, UpdateSeq, PurgeSeq, KeySeqIndexed, SeqIndexed, Args),
-    couch_index_util:hexsig(crypto:hash(md5, term_to_binary(Term))).
+    couch_index_util:hexsig(couch_hash:md5_hash(term_to_binary(Term))).
 
 view_sig_term(BaseSig, UpdateSeq, PurgeSeq, false, false) ->
     {BaseSig, UpdateSeq, PurgeSeq};
@@ -1008,7 +1008,7 @@
         {ViewInfo, State#mrst.language, State#mrst.design_opts,
             couch_index_util:sort_lib(State#mrst.lib)}
     end,
-    crypto:hash(md5, term_to_binary(SigData)).
+    couch_hash:md5_hash(term_to_binary(SigData)).
 
 old_view_format(View) ->
 {
diff --git a/src/couch_replicator/src/couch_replicator_auth.erl b/src/couch_replicator/src/couch_replicator_auth.erl
index 60273fc..7f51cdd 100644
--- a/src/couch_replicator/src/couch_replicator_auth.erl
+++ b/src/couch_replicator/src/couch_replicator_auth.erl
@@ -28,7 +28,7 @@
 -type code() :: non_neg_integer().
 
 
--define(DEFAULT_PLUGINS, "couch_replicator_auth_noop").
+-define(DEFAULT_PLUGINS, "couch_replicator_auth_session,couch_replicator_auth_noop").
 
 
 % Behavior API
diff --git a/src/couch_replicator/src/couch_replicator_ids.erl b/src/couch_replicator/src/couch_replicator_ids.erl
index e8faf8e..e10b980 100644
--- a/src/couch_replicator/src/couch_replicator_ids.erl
+++ b/src/couch_replicator/src/couch_replicator_ids.erl
@@ -112,7 +112,7 @@
         {error, FilterParseError} ->
             throw({error, FilterParseError})
         end,
-    couch_util:to_hex(crypto:hash(md5, term_to_binary(Base2))).
+    couch_util:to_hex(couch_hash:md5_hash(term_to_binary(Base2))).
 
 
 maybe_append_options(Options, RepOptions) ->
diff --git a/src/couch_replicator/test/couch_replicator_many_leaves_tests.erl b/src/couch_replicator/test/couch_replicator_many_leaves_tests.erl
index b2445a2..eee5b16 100644
--- a/src/couch_replicator/test/couch_replicator_many_leaves_tests.erl
+++ b/src/couch_replicator/test/couch_replicator_many_leaves_tests.erl
@@ -141,7 +141,7 @@
 
 add_doc_siblings(Db, DocId, NumLeaves, AccDocs, AccRevs) ->
     Value = ?l2b(?i2l(NumLeaves)),
-    Rev = crypto:hash(md5, Value),
+    Rev = couch_hash:md5_hash(Value),
     Doc = #doc{
         id = DocId,
         revs = {1, [Rev]},
diff --git a/src/couch_replicator/test/couch_replicator_test_helper.erl b/src/couch_replicator/test/couch_replicator_test_helper.erl
index 8ee2114..fd04091 100644
--- a/src/couch_replicator/test/couch_replicator_test_helper.erl
+++ b/src/couch_replicator/test/couch_replicator_test_helper.erl
@@ -93,16 +93,16 @@
 att_md5(Att) ->
     Md50 = couch_att:foldl(
         Att,
-        fun(Chunk, Acc) -> crypto:hash_update(Acc, Chunk) end,
-        crypto:hash_init(md5)),
-    crypto:hash_final(Md50).
+        fun(Chunk, Acc) -> couch_hash:md5_hash_update(Acc, Chunk) end,
+        couch_hash:md5_hash_init()),
+    couch_hash:md5_hash_final(Md50).
 
 att_decoded_md5(Att) ->
     Md50 = couch_att:foldl_decode(
         Att,
-        fun(Chunk, Acc) -> crypto:hash_update(Acc, Chunk) end,
-        crypto:hash_init(md5)),
-    crypto:hash_final(Md50).
+        fun(Chunk, Acc) -> couch_hash:md5_hash_update(Acc, Chunk) end,
+        couch_hash:md5_hash_init()),
+    couch_hash:md5_hash_final(Md50).
 
 db_url(DbName) ->
     iolist_to_binary([
diff --git a/src/fabric/src/fabric.erl b/src/fabric/src/fabric.erl
index 5c517a3..185ffeb 100644
--- a/src/fabric/src/fabric.erl
+++ b/src/fabric/src/fabric.erl
@@ -289,13 +289,14 @@
     not_implemented.
 
 %% @doc spawns a process to upload attachment data and
-%%      returns a function that shards can use to communicate
-%%      with the spawned middleman process
+%%      returns a fabric attachment receiver context tuple
+%%      with the spawned middleman process, an empty binary,
+%%      or exits with an error tuple {Error, Arg}
 -spec att_receiver(#httpd{}, Length :: undefined | chunked | pos_integer() |
         {unknown_transfer_encoding, any()}) ->
-    function() | binary().
+    {fabric_attachment_receiver, pid(), chunked | pos_integer()} | binary().
 att_receiver(Req, Length) ->
-    fabric_doc_attachments:receiver(Req, Length).
+    fabric_doc_atts:receiver(Req, Length).
 
 %% @equiv all_docs(DbName, [], Callback, Acc0, QueryArgs)
 all_docs(DbName, Callback, Acc, QueryArgs) ->
diff --git a/src/mem3/src/mem3_rep.erl b/src/mem3/src/mem3_rep.erl
index 3d91877..8d996d6 100644
--- a/src/mem3/src/mem3_rep.erl
+++ b/src/mem3/src/mem3_rep.erl
@@ -106,8 +106,8 @@
 
 
 make_local_id(SourceThing, TargetThing, Filter) ->
-    S = couch_util:encodeBase64Url(crypto:hash(md5, term_to_binary(SourceThing))),
-    T = couch_util:encodeBase64Url(crypto:hash(md5, term_to_binary(TargetThing))),
+    S = couch_util:encodeBase64Url(couch_hash:md5_hash(term_to_binary(SourceThing))),
+    T = couch_util:encodeBase64Url(couch_hash:md5_hash(term_to_binary(TargetThing))),
     F = case is_function(Filter) of
         true ->
             {new_uniq, Hash} = erlang:fun_info(Filter, new_uniq),
@@ -339,7 +339,7 @@
 
 find_repl_doc(SrcDb, TgtUUIDPrefix) ->
     SrcUUID = couch_db:get_uuid(SrcDb),
-    S = couch_util:encodeBase64Url(crypto:hash(md5, term_to_binary(SrcUUID))),
+    S = couch_util:encodeBase64Url(couch_hash:md5_hash(term_to_binary(SrcUUID))),
     DocIdPrefix = <<"_local/shard-sync-", S/binary, "-">>,
     FoldFun = fun(#doc{id = DocId, body = {BodyProps}} = Doc, _) ->
         TgtUUID = couch_util:get_value(<<"target_uuid">>, BodyProps, <<>>),
diff --git a/src/setup/src/setup.erl b/src/setup/src/setup.erl
index 9433186..3ae455f 100644
--- a/src/setup/src/setup.erl
+++ b/src/setup/src/setup.erl
@@ -30,15 +30,29 @@
 require_node_count(_) ->
     ok.
 
-error_bind_address() ->
-    throw({error, "Cluster setup requires bind_addres != 127.0.0.1"}).
+error_local_bind_address() ->
+    throw({error, "Cluster setup requires a remote bind_address (not 127.0.0.1 nor ::1)"}).
 
-require_bind_address("127.0.0.1", undefined) ->
-    error_bind_address();
-require_bind_address("127.0.0.1", <<"127.0.0.1">>) ->
-    error_bind_address();
-require_bind_address(_, _) ->
-    ok.
+error_invalid_bind_address(InvalidBindAddress) ->
+    throw({error, io:format("Setup requires a valid IP bind_address. " ++
+                         "~p is invalid.", [InvalidBindAddress])}).
+
+require_remote_bind_address(OldBindAddress, NewBindAddress) ->
+    case {OldBindAddress, NewBindAddress} of
+        {"127.0.0.1", undefined} -> error_local_bind_address();
+        {_, <<"127.0.0.1">>} -> error_local_bind_address();
+        {"::1", undefined} -> error_local_bind_address();
+        {_, <<"::1">>} -> error_local_bind_address();
+        {_, undefined} -> ok;
+        {_, PresentNewBindAddress} -> require_valid_bind_address(PresentNewBindAddress)
+    end.
+
+require_valid_bind_address(BindAddress) ->
+    ListBindAddress = binary_to_list(BindAddress),
+    case inet_parse:address(ListBindAddress) of
+        {ok, _} -> ok;
+        {error, _} -> error_invalid_bind_address(ListBindAddress)
+    end.
 
 is_cluster_enabled() ->
     % bind_address != 127.0.0.1 AND admins != empty
@@ -122,7 +136,6 @@
         {ok, "201", _, _} ->
             ok;
         Else ->
-            couch_log:notice("send_req: ~p~n", [Else]),
             {error, Else}
     end.
 
@@ -143,13 +156,13 @@
     % if bind_address == 127.0.0.1 and no bind_address in req -> error
     CurrentBindAddress = config:get("chttpd","bind_address"),
     NewBindAddress = proplists:get_value(bind_address, Options),
-    ok = require_bind_address(CurrentBindAddress, NewBindAddress),
+    ok = require_remote_bind_address(CurrentBindAddress, NewBindAddress),
     NodeCount = couch_util:get_value(node_count, Options),
     ok = require_node_count(NodeCount),
     Port = proplists:get_value(port, Options),
 
     setup_node(NewCredentials, NewBindAddress, NodeCount, Port),
-    couch_log:notice("Enable Cluster: ~p~n", [Options]).
+    couch_log:debug("Enable Cluster: ~p~n", [Options]).
 
 set_admin(Username, Password) ->
     config:set("admins", binary_to_list(Username), binary_to_list(Password)).
@@ -162,6 +175,7 @@
             set_admin(Username, Password)
     end,
 
+    ok = require_valid_bind_address(NewBindAddress),
     case NewBindAddress of
         undefined ->
             config:set("chttpd", "bind_address", "0.0.0.0");
@@ -211,7 +225,7 @@
     setup_node(NewCredentials, NewBindAddress, 1, Port),
     Dbs = proplists:get_value(ensure_dbs_exist, Options, cluster_system_dbs()),
     finish_cluster_int(Dbs, has_cluster_system_dbs(Dbs)),
-    couch_log:notice("Enable Single Node: ~p~n", [Options]).
+    couch_log:debug("Enable Single Node: ~p~n", [Options]).
 
 
 add_node(Options) ->
@@ -220,7 +234,7 @@
 add_node_int(_Options, false) ->
     {error, cluster_not_enabled};
 add_node_int(Options, true) ->
-    couch_log:notice("add node_int: ~p~n", [Options]),
+    couch_log:debug("add node_int: ~p~n", [Options]),
     ErlangCookie = erlang:get_cookie(),
 
     % POST to nodeB/_setup
@@ -251,7 +265,6 @@
             % when done, PUT :5986/nodes/nodeB
             create_node_doc(Host, Name);
         Else ->
-            couch_log:notice("send_req: ~p~n", [Else]),
             Else
     end.
 
diff --git a/test/javascript/tests/design_docs.js b/test/javascript/tests/design_docs.js
index 6e12001..ed1e72f 100644
--- a/test/javascript/tests/design_docs.js
+++ b/test/javascript/tests/design_docs.js
@@ -373,7 +373,13 @@
     }
 
     T(db.deleteDoc(designDoc).ok);
-    T(db.open(designDoc._id) == null);
+    waitForSuccess(function() {
+      var ddoc = db.open(designDoc._id)
+      if (ddoc != null) {
+        throw({});
+      }
+      return true;
+    }, 'db.open(designDoc._id)');
     T(db.view("test/no_docs") == null);
 
     T(db.ensureFullCommit().ok);
diff --git a/test/javascript/tests/users_db_security.js b/test/javascript/tests/users_db_security.js
index c55c764..1db6c14 100644
--- a/test/javascript/tests/users_db_security.js
+++ b/test/javascript/tests/users_db_security.js
@@ -15,8 +15,6 @@
   var usersDb = new CouchDB(db_name, {"X-Couch-Full-Commit":"false"});
   try { usersDb.createDb(); } catch (e) { /* ignore if exists*/ }
 
-  var passwordSchemes = ['pbkdf2', 'bcrypt'];
-
   if (debug) debugger;
 
   var loginUser = function(username) {
@@ -32,13 +30,7 @@
     // the actual tests
     var username1 = username.replace(/[0-9]$/, "");
     var password = pws[username];
-    waitForSuccess(function() {
-      var req = CouchDB.login(username1, pws[username]);
-      if (req.ok) {
-        return true
-      }
-      throw({});
-    }, 'loginUser');
+    T(CouchDB.login(username1, pws[username]).ok);
   };
 
   var open_as = function(db, docId, username) {
@@ -94,7 +86,7 @@
     }
   };
 
-  var testFun = function(scheme, derivedKeyTest, saltTest)
+  var testFun = function()
   {
 
     // _users db
@@ -113,12 +105,11 @@
 
     // jan's gonna be admin as he's the first user
     TEquals(true, usersDb.save(userDoc).ok, "should save document");
+    wait(5000)
     userDoc = open_as(usersDb, "org.couchdb.user:jchris", "jchris");
     TEquals(undefined, userDoc.password, "password field should be null 1");
-    TEquals(scheme, userDoc.password_scheme, "password_scheme should be " + scheme);
-    derivedKeyTest(userDoc.derived_key);
-    saltTest(userDoc.salt);
-
+    TEquals(40, userDoc.derived_key.length, "derived_key should exist");
+    TEquals(32, userDoc.salt.length, "salt should exist");
 
     // create server admin
 
@@ -150,13 +141,10 @@
     var jchrisDoc = open_as(usersDb, "org.couchdb.user:jchris", "jan");
 
     TEquals(undefined, jchrisDoc.password, "password field should be null 2");
-    TEquals(scheme, jchrisDoc.password_scheme, "password_scheme should be " + scheme);
-    derivedKeyTest(jchrisDoc.derived_key);
-    saltTest(jchrisDoc.salt);
+    TEquals(40, jchrisDoc.derived_key.length, "derived_key should exist");
+    TEquals(32, jchrisDoc.salt.length, "salt should exist");
 
-    if(userDoc.salt || jchrisDoc.salt) {
-      TEquals(true, userDoc.salt != jchrisDoc.salt, "should have new salt");
-    }
+    TEquals(true, userDoc.salt != jchrisDoc.salt, "should have new salt");
     TEquals(true, userDoc.derived_key != jchrisDoc.derived_key,
       "should have new derived_key");
 
@@ -239,7 +227,7 @@
       TEquals("forbidden", e.error, "non-admins can't read design docs");
     }
 
-    // admin should be able to read _list
+    // admin shold be able to read _list
     var listPath = ddoc["_id"] + "/_list/names/test";
     var result = request_as(usersDb, listPath, "jan");
     var lines = result.responseText.split("\n");
@@ -385,140 +373,14 @@
     });
   };
 
-  var derivedKeyTests = {
-    pbkdf2: function(derived_key) {
-      TEquals(40, derived_key.length, "derived_key should exist");
-    },
-    bcrypt: function(derived_key) {
-      TEquals(60, derived_key.length, "derived_key should exist");
-    }
-  };
-  var saltTests = {
-    pbkdf2: function(salt) {
-      TEquals(32, salt.length, "salt should exist");
-    },
-    bcrypt: function(salt) {
-      TEquals(undefined, salt, "salt should not exist");
-    }
-  };
-  passwordSchemes.forEach(function(scheme){
-    run_on_modified_server(
-      [{
-        section: "couch_httpd_auth",
-        key: "iterations", value: "1"
-      }, {
-        section: "couch_httpd_auth",
-        key: "password_scheme", value: scheme
-      }, {
-        section: "admins",
-        key: "jan", value: "apple"
-      }],
-      function() {
-        try {
-          testFun(scheme, derivedKeyTests[scheme], saltTests[scheme]);
-        } catch (e) {
-          throw(e)
-        } finally {
-          CouchDB.login("jan", "apple");
-          usersDb.deleteDb(); // cleanup
-          waitForSuccess(function() {
-            var req = CouchDB.request("GET", db_name);
-            if (req.status == 404) {
-              return true
-            }
-            throw({});
-          }, 'usersDb.deleteDb')
-
-          usersDb.createDb();
-          waitForSuccess(function() {
-            var req = CouchDB.request("GET", db_name);
-            if (req.status == 200) {
-              return true
-            }
-            throw({});
-          }, 'usersDb.creteDb')
-        }
-      }
-    );
-  });
-
-  var testFunUpdatePasswordScheme = function() {
-    var userDocs = {
-      jchris: {
-        _id: "org.couchdb.user:jchris",
-        type: "user",
-        name: "jchris",
-        password: "mp3",
-        roles: []
-      },
-      fdmanana: {
-        _id: "org.couchdb.user:fdmanana",
-        type: "user",
-        name: "fdmanana",
-        password: "foobar",
-        roles: []
-      }
-    };
-
-    // create new user (has pbkdf2 hash)
-    TEquals(true, usersDb.save(userDocs.jchris).ok, "should save document");
-    wait(5000);
-    var userDoc = open_as(usersDb, "org.couchdb.user:jchris", "jchris");
-    TEquals(undefined, userDoc.password, "password field should be null 1");
-    TEquals("pbkdf2", userDoc.password_scheme, "password_scheme should be pbkdf2");
-    derivedKeyTests.pbkdf2(userDoc.derived_key);
-    saltTests.pbkdf2(userDoc.salt);
-
-    // change scheme to bcrypt
-    CouchDB.login("jan", "apple");
-    var xhr = CouchDB.request("PUT", "/_node/node1@127.0.0.1/_config/couch_httpd_auth/password_scheme", {
-      body : JSON.stringify("bcrypt"),
-      headers: {"X-Couch-Persist": "false"}
-    });
-    TEquals(200, xhr.status);
-    xhr = CouchDB.request("GET", "/_node/node1@127.0.0.1/_config/couch_httpd_auth/password_scheme");
-    var scheme = JSON.parse(xhr.responseText);
-    TEquals("bcrypt", scheme);
-
-    // create new user (has bcrypt hash)
-    TEquals(true, usersDb.save(userDocs.fdmanana).ok, "should save document");
-    wait(5000);
-    userDoc = open_as(usersDb, "org.couchdb.user:fdmanana", "fdmanana");
-    TEquals(undefined, userDoc.password, "password field should be null 1");
-    TEquals("bcrypt", userDoc.password_scheme, "password_scheme should be bcrypt");
-    derivedKeyTests.bcrypt(userDoc.derived_key);
-    saltTests.bcrypt(userDoc.salt);
-
-    // test that both users can still log in
-    TEquals(true, CouchDB.login(userDocs.jchris.name, userDocs.jchris.password).ok);
-    TEquals(true, CouchDB.login(userDocs.fdmanana.name, userDocs.fdmanana.password).ok);
-
-    // change scheme back to pbkdf2
-    CouchDB.login("jan", "apple");
-    var xhr = CouchDB.request("PUT", "/_node/node1@127.0.0.1/_config/couch_httpd_auth/password_scheme", {
-        body : JSON.stringify("pbkdf2"),
-        headers: {"X-Couch-Persist": "false"}
-    });
-    TEquals(200, xhr.status);
-    xhr = CouchDB.request("GET", "/_node/node1@127.0.0.1/_config/couch_httpd_auth/password_scheme");
-    var scheme = JSON.parse(xhr.responseText);
-    TEquals("pbkdf2", scheme);
-
-    // test that both users can still log in
-    TEquals(true, CouchDB.login(userDocs.jchris.name, userDocs.jchris.password).ok);
-    TEquals(true, CouchDB.login(userDocs.fdmanana.name, userDocs.fdmanana.password).ok);
-  };
   run_on_modified_server(
-    [{
-      section: "couch_httpd_auth",
-      key: "iterations", value: "1"
-    }, {
-      section: "admins",
-      key: "jan", value: "apple"
-    }],
+    [{section: "couch_httpd_auth",
+      key: "iterations", value: "1"},
+   {section: "admins",
+    key: "jan", value: "apple"}],
     function() {
       try {
-        testFunUpdatePasswordScheme();
+        testFun();
       } finally {
         CouchDB.login("jan", "apple");
         usersDb.deleteDb(); // cleanup
@@ -540,6 +402,5 @@
       }
     }
   );
-
   CouchDB.logout();
 };