about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.dir-locals.el16
-rw-r--r--configure.ac20
-rw-r--r--corepkgs/buildenv.nix5
-rw-r--r--corepkgs/buildenv.pl2
-rw-r--r--doc/manual/command-ref/nix-collect-garbage.xml1
-rw-r--r--doc/manual/command-ref/nix-shell.xml22
-rw-r--r--doc/manual/command-ref/nix-store.xml27
-rw-r--r--doc/manual/expressions/builtins.xml36
-rw-r--r--doc/manual/expressions/derivations.xml2
-rw-r--r--doc/manual/expressions/language-values.xml9
-rw-r--r--doc/manual/installation/prerequisites-source.xml2
-rw-r--r--doc/manual/release-notes/release-notes.xml1
-rw-r--r--doc/manual/release-notes/rl-0.10.1.xml4
-rw-r--r--doc/manual/release-notes/rl-0.10.xml4
-rw-r--r--doc/manual/release-notes/rl-0.11.xml4
-rw-r--r--doc/manual/release-notes/rl-0.12.xml4
-rw-r--r--doc/manual/release-notes/rl-0.13.xml4
-rw-r--r--doc/manual/release-notes/rl-0.14.xml6
-rw-r--r--doc/manual/release-notes/rl-0.15.xml4
-rw-r--r--doc/manual/release-notes/rl-0.16.xml4
-rw-r--r--doc/manual/release-notes/rl-0.6.xml4
-rw-r--r--doc/manual/release-notes/rl-0.7.xml4
-rw-r--r--doc/manual/release-notes/rl-0.8.1.xml4
-rw-r--r--doc/manual/release-notes/rl-0.8.xml4
-rw-r--r--doc/manual/release-notes/rl-0.9.1.xml4
-rw-r--r--doc/manual/release-notes/rl-0.9.2.xml4
-rw-r--r--doc/manual/release-notes/rl-0.9.xml4
-rw-r--r--doc/manual/release-notes/rl-1.0.xml4
-rw-r--r--doc/manual/release-notes/rl-1.1.xml4
-rw-r--r--doc/manual/release-notes/rl-1.10.xml2
-rw-r--r--doc/manual/release-notes/rl-1.11.xml133
-rw-r--r--doc/manual/release-notes/rl-1.12.xml24
-rw-r--r--doc/manual/release-notes/rl-1.2.xml4
-rw-r--r--doc/manual/release-notes/rl-1.3.xml4
-rw-r--r--doc/manual/release-notes/rl-1.4.xml4
-rw-r--r--doc/manual/release-notes/rl-1.5.1.xml4
-rw-r--r--doc/manual/release-notes/rl-1.5.2.xml4
-rw-r--r--doc/manual/release-notes/rl-1.5.xml4
-rw-r--r--doc/manual/release-notes/rl-1.6.1.xml4
-rw-r--r--doc/manual/release-notes/rl-1.6.xml4
-rw-r--r--doc/manual/release-notes/rl-1.7.xml4
-rw-r--r--doc/manual/release-notes/rl-1.8.xml2
-rw-r--r--doc/manual/release-notes/rl-1.9.xml2
-rw-r--r--perl/lib/Nix/Store.xs120
-rw-r--r--release.nix45
-rw-r--r--scripts/download-from-binary-cache.pl.in2
-rwxr-xr-xscripts/download-using-manifests.pl.in3
-rw-r--r--scripts/install-nix-from-closure.sh2
-rwxr-xr-xscripts/nix-build.in5
-rw-r--r--scripts/nix-profile.sh.in6
-rwxr-xr-xscripts/nix-push.in9
-rw-r--r--src/libexpr/common-opts.cc2
-rw-r--r--src/libexpr/common-opts.hh2
-rw-r--r--src/libexpr/eval.cc70
-rw-r--r--src/libexpr/eval.hh13
-rw-r--r--src/libexpr/get-drvs.cc21
-rw-r--r--src/libexpr/get-drvs.hh3
-rw-r--r--src/libexpr/json-to-value.cc24
-rw-r--r--src/libexpr/lexer.l31
-rw-r--r--src/libexpr/nixexpr.cc14
-rw-r--r--src/libexpr/nixexpr.hh9
-rw-r--r--src/libexpr/parser.y5
-rw-r--r--src/libexpr/primops.cc104
-rw-r--r--src/libexpr/value-to-json.cc4
-rw-r--r--src/libexpr/value-to-json.hh13
-rw-r--r--src/libexpr/value-to-xml.cc4
-rw-r--r--src/libexpr/value.hh11
-rw-r--r--src/libmain/shared.cc11
-rw-r--r--src/libmain/shared.hh9
-rw-r--r--src/libstore/build.cc304
-rw-r--r--src/libstore/crypto.cc88
-rw-r--r--src/libstore/crypto.hh40
-rw-r--r--src/libstore/derivations.cc83
-rw-r--r--src/libstore/derivations.hh40
-rw-r--r--src/libstore/download.cc2
-rw-r--r--src/libstore/download.hh5
-rw-r--r--src/libstore/gc.cc49
-rw-r--r--src/libstore/local-store.cc81
-rw-r--r--src/libstore/local-store.hh14
-rw-r--r--src/libstore/local.mk2
-rw-r--r--src/libstore/misc.cc108
-rw-r--r--src/libstore/misc.hh40
-rw-r--r--src/libstore/nar-info.cc134
-rw-r--r--src/libstore/nar-info.hh43
-rw-r--r--src/libstore/pathlocks.cc6
-rw-r--r--src/libstore/profiles.cc4
-rw-r--r--src/libstore/profiles.hh4
-rw-r--r--src/libstore/remote-store.cc12
-rw-r--r--src/libstore/remote-store.hh2
-rw-r--r--src/libstore/store-api.cc49
-rw-r--r--src/libstore/store-api.hh86
-rw-r--r--src/libutil/affinity.cc10
-rw-r--r--src/libutil/compression.cc74
-rw-r--r--src/libutil/compression.hh2
-rw-r--r--src/libutil/hash.cc12
-rw-r--r--src/libutil/hash.hh17
-rw-r--r--src/libutil/types.hh60
-rw-r--r--src/libutil/util.cc11
-rw-r--r--src/libutil/util.hh28
-rw-r--r--src/nix-collect-garbage/nix-collect-garbage.cc2
-rw-r--r--src/nix-daemon/nix-daemon.cc91
-rw-r--r--src/nix-env/nix-env.cc46
-rw-r--r--src/nix-env/user-env.cc12
-rw-r--r--src/nix-instantiate/nix-instantiate.cc7
-rw-r--r--src/nix-prefetch-url/nix-prefetch-url.cc4
-rw-r--r--src/nix-store/dotgraph.cc128
-rw-r--r--src/nix-store/dotgraph.hh4
-rw-r--r--src/nix-store/nix-store.cc83
-rw-r--r--src/nix-store/xmlgraph.cc34
-rw-r--r--src/nix-store/xmlgraph.hh4
-rw-r--r--tests/dump-db.sh1
-rw-r--r--tests/lang/eval-okay-attrs5.nix2
-rw-r--r--tests/lang/eval-okay-fromjson.nix6
-rw-r--r--tests/lang/eval-okay-tojson.exp2
-rw-r--r--tests/lang/eval-okay-tojson.nix1
-rw-r--r--tests/lang/eval-okay-types.exp2
-rw-r--r--tests/lang/eval-okay-types.nix10
-rw-r--r--tests/lang/eval-okay-xml.exp.xml3
-rw-r--r--tests/lang/eval-okay-xml.nix2
-rw-r--r--tests/lang/parse-okay-regression-751.nix2
-rw-r--r--tests/repair.sh12
-rw-r--r--version2
122 files changed, 1899 insertions, 925 deletions
diff --git a/.dir-locals.el b/.dir-locals.el
new file mode 100644
index 000000000000..2d1117f4bda6
--- /dev/null
+++ b/.dir-locals.el
@@ -0,0 +1,16 @@
+((c++-mode . (
+  (c-file-style . "k&r")
+  (c-basic-offset . 4)
+  (indent-tabs-mode . nil)
+  (tab-width . 4)
+  (show-trailing-whitespace . t)
+  (indicate-empty-lines . t)
+  (eval . (c-set-offset 'innamespace 0))
+  (eval . (c-set-offset 'defun-open 0))
+  (eval . (c-set-offset 'inline-open 0))
+  (eval . (c-set-offset 'arglist-intro '+))
+  (eval . (c-set-offset 'arglist-cont 0))
+  (eval . (c-set-offset 'arglist-cont-nonempty '+))
+  (eval . (c-set-offset 'substatement-open 0))
+  (eval . (c-set-offset 'access-label '-))
+  )))
diff --git a/configure.ac b/configure.ac
index 88e64573da88..3a24053bb6a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -48,6 +48,7 @@ test "$localstatedir" = '${prefix}/var' && localstatedir=/nix/var
 
 
 # Solaris-specific stuff.
+AC_STRUCT_DIRENT_D_TYPE
 if test "$sys_name" = sunos; then
     # Solaris requires -lsocket -lnsl for network functions
     LIBS="-lsocket -lnsl $LIBS"
@@ -76,18 +77,7 @@ static char buf[1024];]],
 AC_LANG_POP(C++)
 
 
-# Check for chroot support (requires chroot() and bind mounts).
-AC_CHECK_FUNCS([chroot])
-AC_CHECK_FUNCS([unshare])
 AC_CHECK_FUNCS([statvfs])
-AC_CHECK_HEADERS([sched.h])
-AC_CHECK_HEADERS([sys/param.h])
-AC_CHECK_HEADERS([sys/mount.h], [], [],
-[#ifdef HAVE_SYS_PARAM_H
-# include <sys/param.h>
-# endif
-])
-AC_CHECK_HEADERS([sys/syscall.h])
 
 
 # Check for lutimes, optionally used for changing the mtime of
@@ -95,10 +85,6 @@ AC_CHECK_HEADERS([sys/syscall.h])
 AC_CHECK_FUNCS([lutimes])
 
 
-# Check for sched_setaffinity.
-AC_CHECK_FUNCS([sched_setaffinity])
-
-
 # Check whether the store optimiser can optimise symlinks.
 AC_MSG_CHECKING([whether it is possible to create a link to a symlink])
 ln -s bla tmp_link
@@ -122,10 +108,6 @@ AC_CHECK_HEADER([err.h], [], [bsddiff_compat_include="-Icompat-include"])
 AC_SUBST([bsddiff_compat_include])
 
 
-# Check for <linux/fs.h> (for immutable file support).
-AC_CHECK_HEADERS([linux/fs.h])
-
-
 AC_DEFUN([NEED_PROG],
 [
 AC_PATH_PROG($1, $2)
diff --git a/corepkgs/buildenv.nix b/corepkgs/buildenv.nix
index ab1ce13f2cf6..70981a752c3c 100644
--- a/corepkgs/buildenv.nix
+++ b/corepkgs/buildenv.nix
@@ -8,7 +8,7 @@ derivation {
   builder = perl;
   args = [ "-w" ./buildenv.pl ];
 
-  manifest = manifest;
+  inherit manifest;
 
   # !!! grmbl, need structured data for passing this in a clean way.
   derivations =
@@ -23,6 +23,9 @@ derivation {
   # network traffic, so don't do that.
   preferLocalBuild = true;
 
+  # Also don't bother substituting.
+  allowSubstitutes = false;
+
   __sandboxProfile = ''
     (allow sysctl-read)
     (allow file-read*
diff --git a/corepkgs/buildenv.pl b/corepkgs/buildenv.pl
index 264442104320..dacc53701a01 100644
--- a/corepkgs/buildenv.pl
+++ b/corepkgs/buildenv.pl
@@ -149,7 +149,7 @@ foreach my $pkg (@pkgs) {
 
 
 # Symlink to the packages that have been "propagated" by packages
-# installed by the user (i.e., package X declares that it want Y
+# installed by the user (i.e., package X declares that it wants Y
 # installed as well).  We do these later because they have a lower
 # priority in case of collisions.
 my $priorityCounter = 1000; # don't care about collisions
diff --git a/doc/manual/command-ref/nix-collect-garbage.xml b/doc/manual/command-ref/nix-collect-garbage.xml
index c88851299152..35a78c5b2015 100644
--- a/doc/manual/command-ref/nix-collect-garbage.xml
+++ b/doc/manual/command-ref/nix-collect-garbage.xml
@@ -28,6 +28,7 @@
       <arg choice='plain'><option>--print-dead</option></arg>
       <arg choice='plain'><option>--delete</option></arg>
     </group>
+    <arg><option>--max-freed</option> <replaceable>bytes</replaceable></arg>
     <arg><option>--dry-run</option></arg>
   </cmdsynopsis>
 </refsynopsisdiv>
diff --git a/doc/manual/command-ref/nix-shell.xml b/doc/manual/command-ref/nix-shell.xml
index 9e3e6d1882ba..c64c93ec3ac2 100644
--- a/doc/manual/command-ref/nix-shell.xml
+++ b/doc/manual/command-ref/nix-shell.xml
@@ -176,6 +176,22 @@ also <xref linkend="sec-common-options" />.</phrase></para>
 </refsection>
 
 
+<refsection><title>Environment variables</title>
+
+<variablelist>
+
+  <varlistentry><term><envar>NIX_BUILD_SHELL</envar></term>
+    
+    <listitem><para>Shell used to start the interactive environment. 
+    Defaults to the <command>bash</command> found in <envar>PATH</envar>.</para></listitem>
+
+  </varlistentry>
+
+</variablelist>
+
+</refsection>
+
+
 <refsection><title>Examples</title>
 
 <para>To build the dependencies of the package Pan, and start an
@@ -251,9 +267,9 @@ dependencies in Nixpkgs.</para>
 
 <para>The lines starting with <literal>#! nix-shell</literal> specify
 <command>nix-shell</command> options (see above). Note that you cannot
-write <literal>#1 /usr/bin/env nix-shell -i ...</literal> because
-<command>/usr/bin/env</command> does not support passing options to
-the interpreter.</para>
+write <literal>#! /usr/bin/env nix-shell -i ...</literal> because
+many operating systems only allow one argument in
+<literal>#!</literal> lines.</para>
 
 <para>For example, here is a Python script that depends on Python and
 the <literal>prettytable</literal> package:
diff --git a/doc/manual/command-ref/nix-store.xml b/doc/manual/command-ref/nix-store.xml
index e21d53d8b9f0..bf03c802b87d 100644
--- a/doc/manual/command-ref/nix-store.xml
+++ b/doc/manual/command-ref/nix-store.xml
@@ -194,6 +194,25 @@ printed.)</para>
 
   </varlistentry>
 
+  <varlistentry><term><option>--check</option></term>
+
+    <listitem><para>This option allows you to check whether a
+    derivation is deterministic. It rebuilds the specified derivation
+    and checks whether the result is bitwise-identical with the
+    existing outputs, printing an error if that’s not the case. The
+    outputs of the specified derivation must already exist. When used
+    with <option>-K</option>, if an output path is not identical to
+    the corresponding output from the previous build, the new output
+    path is left in
+    <filename>/nix/store/<replaceable>name</replaceable>-check.</filename></para>
+
+    <para>See also the <option>build-repeat</option> configuration
+    option, which repeats a derivation a number of times and prevents
+    its outputs from being registered as “valid” in the Nix store
+    unless they are identical.</para></listitem>
+
+  </varlistentry>
+
 </variablelist>
 
 </refsection>
@@ -212,6 +231,14 @@ $ nix-store -r $(nix-instantiate ./test.nix)
 This is essentially what <link
 linkend="sec-nix-build"><command>nix-build</command></link> does.</para>
 
+<para>To test whether a previously-built derivation is deterministic:
+
+<screen>
+$ nix-build -r '&lt;nixpkgs>' -A hello --check -K
+</screen>
+
+</para>
+
 </refsection>
 
 
diff --git a/doc/manual/expressions/builtins.xml b/doc/manual/expressions/builtins.xml
index 3b664479d27b..eae5f5a029bf 100644
--- a/doc/manual/expressions/builtins.xml
+++ b/doc/manual/expressions/builtins.xml
@@ -32,7 +32,7 @@ available as <function>builtins.derivation</function>.</para>
   <varlistentry><term><function>builtins.add</function>
   <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
 
-    <listitem><para>Return the sum of the integers
+    <listitem><para>Return the sum of the numbers
     <replaceable>e1</replaceable> and
     <replaceable>e2</replaceable>.</para></listitem>
 
@@ -204,7 +204,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
   <varlistentry><term><function>builtins.div</function>
   <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
 
-    <listitem><para>Return the quotient of the integers
+    <listitem><para>Return the quotient of the numbers
     <replaceable>e1</replaceable> and
     <replaceable>e2</replaceable>.</para></listitem>
 
@@ -335,7 +335,7 @@ stdenv.mkDerivation {
   </varlistentry>
 
 
-  <varlistentry><term><function>builtins.foldl’</function> 
+  <varlistentry><term><function>builtins.foldl’</function>
     <replaceable>op</replaceable> <replaceable>nul</replaceable> <replaceable>list</replaceable></term>
 
     <listitem><para>Reduce a list by applying a binary operator, from
@@ -348,6 +348,24 @@ stdenv.mkDerivation {
   </varlistentry>
 
 
+  <varlistentry><term><function>builtins.functionArgs</function>
+    <replaceable>f</replaceable></term>
+
+    <listitem><para>
+    Return a set containing the names of the formal arguments expected
+    by the function <replaceable>f</replaceable>.
+    The value of each attribute is a Boolean denoting whether the corresponding
+    argument has a default value.  For instance,
+    <literal>functionArgs ({ x, y ? 123}: ...)  =  { x = false; y = true; }</literal>.
+    </para>
+
+    <para>"Formal argument" here refers to the attributes pattern-matched by
+    the function.  Plain lambdas are not included, e.g.
+    <literal>functionArgs (x: ...)  =  { }</literal>.
+    </para></listitem>
+  </varlistentry>
+
+
   <varlistentry><term><function>builtins.fromJSON</function> <replaceable>e</replaceable></term>
 
     <listitem><para>Convert a JSON string to a Nix
@@ -602,12 +620,12 @@ x: x + 456</programlisting>
   <varlistentry><term><function>builtins.lessThan</function>
   <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
 
-    <listitem><para>Return <literal>true</literal> if the integer
-    <replaceable>e1</replaceable> is less than the integer
+    <listitem><para>Return <literal>true</literal> if the number
+    <replaceable>e1</replaceable> is less than the number
     <replaceable>e2</replaceable>, and <literal>false</literal>
     otherwise.  Evaluation aborts if either
     <replaceable>e1</replaceable> or <replaceable>e2</replaceable>
-    does not evaluate to an integer.</para></listitem>
+    does not evaluate to a number.</para></listitem>
 
   </varlistentry>
 
@@ -658,7 +676,7 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
   <varlistentry><term><function>builtins.mul</function>
   <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
 
-    <listitem><para>Return the product of the integers
+    <listitem><para>Return the product of the numbers
     <replaceable>e1</replaceable> and
     <replaceable>e2</replaceable>.</para></listitem>
 
@@ -815,7 +833,7 @@ builtins.sort builtins.lessThan [ 483 249 526 147 42 77 ]
   <varlistentry><term><function>builtins.sub</function>
   <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
 
-    <listitem><para>Return the difference between the integers
+    <listitem><para>Return the difference between the numbers
     <replaceable>e1</replaceable> and
     <replaceable>e2</replaceable>.</para></listitem>
 
@@ -942,7 +960,7 @@ in foo</programlisting>
   <varlistentry><term><function>builtins.toJSON</function> <replaceable>e</replaceable></term>
 
     <listitem><para>Return a string containing a JSON representation
-    of <replaceable>e</replaceable>.  Strings, integers, booleans,
+    of <replaceable>e</replaceable>.  Strings, integers, floats, booleans,
     nulls and lists are mapped to their JSON equivalents.  Sets
     (except derivations) are represented as objects.  Derivations are
     translated to a JSON string containing the derivation’s output
diff --git a/doc/manual/expressions/derivations.xml b/doc/manual/expressions/derivations.xml
index 90e2786faaab..f2a73dccfe18 100644
--- a/doc/manual/expressions/derivations.xml
+++ b/doc/manual/expressions/derivations.xml
@@ -43,7 +43,7 @@ of which specify the inputs of the build.</para>
 
     <itemizedlist>
 
-      <listitem><para>Strings and integers are just passed
+      <listitem><para>Strings and numbers are just passed
       verbatim.</para></listitem>
 
       <listitem><para>A <emphasis>path</emphasis> (e.g.,
diff --git a/doc/manual/expressions/language-values.xml b/doc/manual/expressions/language-values.xml
index 0bf6632d6e3a..f1174ecb5d8d 100644
--- a/doc/manual/expressions/language-values.xml
+++ b/doc/manual/expressions/language-values.xml
@@ -140,8 +140,13 @@ stdenv.mkDerivation {
 
   </listitem>
 
-  <listitem><para><emphasis>Integers</emphasis>, e.g.,
-  <literal>123</literal>.</para></listitem>
+  <listitem><para>Numbers, which can be <emphasis>integers</emphasis> (like
+  <literal>123</literal>) or <emphasis>floating point</emphasis> (like
+  <literal>123.43</literal> or <literal>.27e13</literal>).</para>
+
+  <para>Numbers are type-compatible: pure integer operations will always
+  return integers, whereas any operation involving at least one floating point
+  number will have a floating point number as a result.</para></listitem>
 
   <listitem><para><emphasis>Paths</emphasis>, e.g.,
   <filename>/bin/sh</filename> or <filename>./builder.sh</filename>.
diff --git a/doc/manual/installation/prerequisites-source.xml b/doc/manual/installation/prerequisites-source.xml
index 01d52c74030a..49036d940bd4 100644
--- a/doc/manual/installation/prerequisites-source.xml
+++ b/doc/manual/installation/prerequisites-source.xml
@@ -36,7 +36,7 @@
   distribution does not provide them.</para></listitem>
 
   <listitem><para>The <link
-  xlink:href="http://www.hpl.hp.com/personal/Hans_Boehm/gc/">Boehm
+  xlink:href="http://www.hboehm.info/gc/">Boehm
   garbage collector</link> to reduce the evaluator’s memory
   consumption (optional).  To enable it, install
   <literal>pkgconfig</literal> and the Boehm garbage collector, and
diff --git a/doc/manual/release-notes/release-notes.xml b/doc/manual/release-notes/release-notes.xml
index 0aa3e8717aa1..8c2deb394183 100644
--- a/doc/manual/release-notes/release-notes.xml
+++ b/doc/manual/release-notes/release-notes.xml
@@ -12,6 +12,7 @@
 </partintro>
 -->
 
+<xi:include href="rl-1.12.xml" />
 <xi:include href="rl-1.11.xml" />
 <xi:include href="rl-1.10.xml" />
 <xi:include href="rl-1.9.xml" />
diff --git a/doc/manual/release-notes/rl-0.10.1.xml b/doc/manual/release-notes/rl-0.10.1.xml
index 05cd2f654353..95829323d4fb 100644
--- a/doc/manual/release-notes/rl-0.10.1.xml
+++ b/doc/manual/release-notes/rl-0.10.1.xml
@@ -4,10 +4,10 @@
       version="5.0"
       xml:id="ch-relnotes-0.10.1">
 
-<title>Release 0.10.1 (October 11, 2006)</title>
+<title>Release 0.10.1 (2006-10-11)</title>
 
 <para>This release fixes two somewhat obscure bugs that occur when
 evaluating Nix expressions that are stored inside the Nix store
 (<literal>NIX-67</literal>).  These do not affect most users.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.10.xml b/doc/manual/release-notes/rl-0.10.xml
index 7815ae75b1b8..9afec4de94de 100644
--- a/doc/manual/release-notes/rl-0.10.xml
+++ b/doc/manual/release-notes/rl-0.10.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ch-relnotes-0.10">
 
-<title>Release 0.10 (October 6, 2006)</title>
+<title>Release 0.10 (2006-10-06)</title>
 
 <note><para>This version of Nix uses Berkeley DB 4.4 instead of 4.3.
 The database is upgraded automatically, but you should be careful not
@@ -320,4 +320,4 @@ irreversible.</para></warning>
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.11.xml b/doc/manual/release-notes/rl-0.11.xml
index 9c5d8b8beb4b..7ad0ab5b71ad 100644
--- a/doc/manual/release-notes/rl-0.11.xml
+++ b/doc/manual/release-notes/rl-0.11.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-0.11">
 
-<title>Release 0.11 (December 31, 2007)</title>
+<title>Release 0.11 (2007-12-31)</title>
 
 <para>Nix 0.11 has many improvements over the previous stable release.
 The most important improvement is secure multi-user support.  It also
@@ -258,4 +258,4 @@ on Nix.  Here is an (incomplete) list:</para>
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.12.xml b/doc/manual/release-notes/rl-0.12.xml
index 1f04609b1654..fdba8c4d577f 100644
--- a/doc/manual/release-notes/rl-0.12.xml
+++ b/doc/manual/release-notes/rl-0.12.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-0.12">
 
-<title>Release 0.12 (November 20, 2008)</title>
+<title>Release 0.12 (2008-11-20)</title>
 
 <itemizedlist>
 
@@ -172,4 +172,4 @@ the following paths will be downloaded/copied (30.02 MiB):
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.13.xml b/doc/manual/release-notes/rl-0.13.xml
index 9cf144e4ea23..cce2e4a26b05 100644
--- a/doc/manual/release-notes/rl-0.13.xml
+++ b/doc/manual/release-notes/rl-0.13.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-0.13">
 
-<title>Release 0.13 (November 5, 2009)</title>
+<title>Release 0.13 (2009-11-05)</title>
 
 <para>This is primarily a bug fix release.  It has some new
 features:</para>
@@ -103,4 +103,4 @@ features:</para>
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.14.xml b/doc/manual/release-notes/rl-0.14.xml
index a671db3b8588..e5fe9da78e7e 100644
--- a/doc/manual/release-notes/rl-0.14.xml
+++ b/doc/manual/release-notes/rl-0.14.xml
@@ -2,7 +2,9 @@
       xmlns:xlink="http://www.w3.org/1999/xlink"
       xmlns:xi="http://www.w3.org/2001/XInclude"
       version="5.0"
-      xml:id="ssec-relnotes-0.14"><title>Release 0.14 (February 4, 2010)</title>
+      xml:id="ssec-relnotes-0.14">
+
+<title>Release 0.14 (2010-02-04)</title>
 
 <para>This release has the following improvements:</para>
 
@@ -41,4 +43,4 @@
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.15.xml b/doc/manual/release-notes/rl-0.15.xml
index b76f674ba123..9f58a8efc5d4 100644
--- a/doc/manual/release-notes/rl-0.15.xml
+++ b/doc/manual/release-notes/rl-0.15.xml
@@ -4,11 +4,11 @@
       version="5.0"
       xml:id="ssec-relnotes-0.15">
 
-<title>Release 0.15 (March 17, 2010)</title>
+<title>Release 0.15 (2010-03-17)</title>
 
 <para>This is a bug-fix release.  Among other things, it fixes
 building on Mac OS X (Snow Leopard), and improves the contents of
 <filename>/etc/passwd</filename> and <filename>/etc/group</filename>
 in <literal>chroot</literal> builds.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.16.xml b/doc/manual/release-notes/rl-0.16.xml
index 5254e5f9d658..af1edc0ebbe0 100644
--- a/doc/manual/release-notes/rl-0.16.xml
+++ b/doc/manual/release-notes/rl-0.16.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-0.16">
 
-<title>Release 0.16 (August 17, 2010)</title>
+<title>Release 0.16 (2010-08-17)</title>
 
 <para>This release has the following improvements:</para>
 
@@ -52,4 +52,4 @@
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.6.xml b/doc/manual/release-notes/rl-0.6.xml
index 83d9dc897c42..6dc6521d3c2a 100644
--- a/doc/manual/release-notes/rl-0.6.xml
+++ b/doc/manual/release-notes/rl-0.6.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ch-relnotes-0.6">
 
-<title>Release 0.6 (November 14, 2004)</title>
+<title>Release 0.6 (2004-11-14)</title>
 
 <itemizedlist>
 
@@ -119,4 +119,4 @@
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.7.xml b/doc/manual/release-notes/rl-0.7.xml
index fc8997fc1e36..6f95db4367db 100644
--- a/doc/manual/release-notes/rl-0.7.xml
+++ b/doc/manual/release-notes/rl-0.7.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ch-relnotes-0.7">
 
-<title>Release 0.7 (January 12, 2005)</title>
+<title>Release 0.7 (2005-01-12)</title>
 
 <itemizedlist>
 
@@ -32,4 +32,4 @@
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.8.1.xml b/doc/manual/release-notes/rl-0.8.1.xml
index b4a855b553a5..f7ffca0f8d59 100644
--- a/doc/manual/release-notes/rl-0.8.1.xml
+++ b/doc/manual/release-notes/rl-0.8.1.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ch-relnotes-0.8.1">
 
-<title>Release 0.8.1 (April 13, 2005)</title>
+<title>Release 0.8.1 (2005-04-13)</title>
 
 <para>This is a bug fix release.</para>
 
@@ -18,4 +18,4 @@
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.8.xml b/doc/manual/release-notes/rl-0.8.xml
index 970abb6e85e0..784b26c6b7d3 100644
--- a/doc/manual/release-notes/rl-0.8.xml
+++ b/doc/manual/release-notes/rl-0.8.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ch-relnotes-0.8">
 
-<title>Release 0.8 (April 11, 2005)</title>
+<title>Release 0.8 (2005-04-11)</title>
 
 <para>NOTE: the hashing scheme in Nix 0.8 changed (as detailed below).
 As a result, <command>nix-pull</command> manifests and channels built
@@ -243,4 +243,4 @@ $ nix-env -f .../i686-linux.nix -i -E 'x: x.firefoxWrapper'</screen>
 
 </para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.9.1.xml b/doc/manual/release-notes/rl-0.9.1.xml
index a7d093aa8e02..85d11f416877 100644
--- a/doc/manual/release-notes/rl-0.9.1.xml
+++ b/doc/manual/release-notes/rl-0.9.1.xml
@@ -4,10 +4,10 @@
       version="5.0"
       xml:id="ch-relnotes-0.9.1">
 
-<title>Release 0.9.1 (September 20, 2005)</title>
+<title>Release 0.9.1 (2005-09-20)</title>
 
 <para>This bug fix release addresses a problem with the ATerm library
 when the <option>--with-aterm</option> flag in
 <command>configure</command> was <emphasis>not</emphasis> used.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.9.2.xml b/doc/manual/release-notes/rl-0.9.2.xml
index 33141c8e94ff..cb705e98ac25 100644
--- a/doc/manual/release-notes/rl-0.9.2.xml
+++ b/doc/manual/release-notes/rl-0.9.2.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ch-relnotes-0.9.2">
 
-<title>Release 0.9.2 (September 21, 2005)</title>
+<title>Release 0.9.2 (2005-09-21)</title>
 
 <para>This bug fix release fixes two problems on Mac OS X:
 
@@ -25,4 +25,4 @@
 
 </para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.9.xml b/doc/manual/release-notes/rl-0.9.xml
index 07dd87cd9f61..fd1e633f78ea 100644
--- a/doc/manual/release-notes/rl-0.9.xml
+++ b/doc/manual/release-notes/rl-0.9.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ch-relnotes-0.9">
 
-<title>Release 0.9 (September 16, 2005)</title>
+<title>Release 0.9 (2005-09-16)</title>
 
 <para>NOTE: this version of Nix uses Berkeley DB 4.3 instead of 4.2.
 The database is upgraded automatically, but you should be careful not
@@ -95,4 +95,4 @@ svnService = derivation {
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.0.xml b/doc/manual/release-notes/rl-1.0.xml
index d000014eb311..ff11168d0932 100644
--- a/doc/manual/release-notes/rl-1.0.xml
+++ b/doc/manual/release-notes/rl-1.0.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.0">
 
-<title>Release 1.0 (May 11, 2012)</title>
+<title>Release 1.0 (2012-05-11)</title>
 
 <para>There have been numerous improvements and bug fixes since the
 previous release.  Here are the most significant:</para>
@@ -116,4 +116,4 @@ previous release.  Here are the most significant:</para>
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.1.xml b/doc/manual/release-notes/rl-1.1.xml
index 7ee076f193e9..2f26e7a24273 100644
--- a/doc/manual/release-notes/rl-1.1.xml
+++ b/doc/manual/release-notes/rl-1.1.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.1">
 
-<title>Release 1.1 (July 18, 2012)</title>
+<title>Release 1.1 (2012-07-18)</title>
 
 <para>This release has the following improvements:</para>
 
@@ -97,4 +97,4 @@
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.10.xml b/doc/manual/release-notes/rl-1.10.xml
index 7a90087487e9..689a95466343 100644
--- a/doc/manual/release-notes/rl-1.10.xml
+++ b/doc/manual/release-notes/rl-1.10.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.10">
 
-<title>Release 1.10 (September 3, 2015)</title>
+<title>Release 1.10 (2015-09-03)</title>
 
 <para>This is primarily a bug fix release. It also has a number of new
 features:</para>
diff --git a/doc/manual/release-notes/rl-1.11.xml b/doc/manual/release-notes/rl-1.11.xml
index aa9a3e101835..efb03d61393f 100644
--- a/doc/manual/release-notes/rl-1.11.xml
+++ b/doc/manual/release-notes/rl-1.11.xml
@@ -4,20 +4,145 @@
       version="5.0"
       xml:id="ssec-relnotes-1.11">
 
-<title>Release 1.11 (TODO: date, 2015)</title>
+<title>Release 1.11 (2016-01-19)</title>
 
-<para>TODO: This is primarily a bug fix release. It also has a number of new
+<para>This is primarily a bug fix release. It also has a number of new
 features:</para>
 
 <itemizedlist>
+
+  <listitem>
+    <para><command>nix-prefetch-url</command> can now download URLs
+    specified in a Nix expression. For example,
+
+<screen>
+$ nix-prefetch-url -A hello.src
+</screen>
+
+    will prefetch the file specified by the
+    <function>fetchurl</function> call in the attribute
+    <literal>hello.src</literal> from the Nix expression in the
+    current directory, and print the cryptographic hash of the
+    resulting file on stdout. This differs from <literal>nix-build -A
+    hello.src</literal> in that it doesn't verify the hash, and is
+    thus useful when you’re updating a Nix expression.</para>
+
+    <para>You can also prefetch the result of functions that unpack a
+    tarball, such as <function>fetchFromGitHub</function>. For example:
+
+<screen>
+$ nix-prefetch-url --unpack https://github.com/NixOS/patchelf/archive/0.8.tar.gz
+</screen>
+
+    or from a Nix expression:
+
+<screen>
+$ nix-prefetch-url -A nix-repl.src
+</screen>
+
+    </para>
+
+  </listitem>
+
+  <listitem>
+    <para>The builtin function
+    <function>&lt;nix/fetchurl.nix></function> now supports
+    downloading and unpacking NARs. This removes the need to have
+    multiple downloads in the Nixpkgs stdenv bootstrap process (like a
+    separate busybox binary for Linux, or curl/mkdir/sh/bzip2 for
+    Darwin). Now all those files can be combined into a single NAR,
+    optionally compressed using <command>xz</command>.</para>
+  </listitem>
+
+  <listitem>
+    <para>Nix now supports SHA-512 hashes for verifying fixed-output
+    derivations, and in <function>builtins.hashString</function>.</para>
+  </listitem>
+
+  <listitem>
+    <para>
+      The new flag <option>--option build-repeat
+      <replaceable>N</replaceable></option> will cause every build to
+      be executed <replaceable>N</replaceable>+1 times. If the build
+      output differs between any round, the build is rejected, and the
+      output paths are not registered as valid. This is primarily
+      useful to verify build determinism. (We already had a
+      <option>--check</option> option to repeat a previously succeeded
+      build. However, with <option>--check</option>, non-deterministic
+      builds are registered in the DB. Preventing that is useful for
+      Hydra to ensure that non-deterministic builds don't end up
+      getting published to the binary cache.)
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      The options <option>--check</option> and <option>--option
+      build-repeat <replaceable>N</replaceable></option>, if they
+      detect a difference between two runs of the same derivation and
+      <option>-K</option> is given, will make the output of the other
+      run available under
+      <filename><replaceable>store-path</replaceable>-check</filename>. This
+      makes it easier to investigate the non-determinism using tools
+      like <command>diffoscope</command>, e.g.,
+
+<screen>
+$ nix-build pkgs/stdenv/linux -A stage1.pkgs.zlib --check -K
+error: derivation ‘/nix/store/l54i8wlw2265…-zlib-1.2.8.drv’ may not
+be deterministic: output ‘/nix/store/11a27shh6n2i…-zlib-1.2.8’
+differs from ‘/nix/store/11a27shh6n2i…-zlib-1.2.8-check’
+
+$ diffoscope /nix/store/11a27shh6n2i…-zlib-1.2.8 /nix/store/11a27shh6n2i…-zlib-1.2.8-check
+…
+├── lib/libz.a
+│   ├── metadata
+│   │ @@ -1,15 +1,15 @@
+│   │ -rw-r--r-- 30001/30000   3096 Jan 12 15:20 2016 adler32.o
+…
+│   │ +rw-r--r-- 30001/30000   3096 Jan 12 15:28 2016 adler32.o
+…
+</screen>
+
+    </para></listitem>
+
+  <listitem>
+    <para>Improved FreeBSD support.</para>
+  </listitem>
+
+  <listitem>
+    <para><command>nix-env -qa --xml --meta</command> now prints
+    license information.</para>
+  </listitem>
+
+  <listitem>
+    <para>The maximum number of parallel TCP connections that the
+    binary cache substituter will use has been decreased from 150 to
+    25. This should prevent upsetting some broken NAT routers, and
+    also improves performance.</para>
+  </listitem>
+
+  <listitem>
+    <para>The Nix language now supports floating point numbers. They are
+    based on regular C++ <literal>float</literal> and compatible with
+    existing integers and number-related operations. Export and import to and
+    from JSON and XML works, too.
+  </para>
+  </listitem>
   <listitem>
     <para>All "chroot"-containing strings got renamed to "sandbox".
-      In particular, some nix options got renamed, but the old names
+      In particular, some Nix options got renamed, but the old names
       are still accepted as lower-priority aliases.
     </para>
   </listitem>
+
 </itemizedlist>
 
-<para>This release has contributions from TODO.</para>
+<para>This release has contributions from Anders Claesson, Anthony
+Cowley, Bjørn Forsman, Brian McKenna, Danny Wilson, davidak, Eelco Dolstra,
+Fabian Schmitthenner, FrankHB, Ilya Novoselov, janus, Jim Garrison, John
+Ericson, Jude Taylor, Ludovic Courtès, Manuel Jacob, Mathnerd314,
+Pascal Wittmann, Peter Simons, Philip Potter, Preston Bennes, Rommel
+M. Martinez, Sander van der Burg, Shea Levy, Tim Cuthbertson, Tuomas
+Tynkkynen, Utku Demir and Vladimír Čunát.</para>
 
 </section>
diff --git a/doc/manual/release-notes/rl-1.12.xml b/doc/manual/release-notes/rl-1.12.xml
new file mode 100644
index 000000000000..d6864b3f55d1
--- /dev/null
+++ b/doc/manual/release-notes/rl-1.12.xml
@@ -0,0 +1,24 @@
+<section xmlns="http://docbook.org/ns/docbook"
+      xmlns:xlink="http://www.w3.org/1999/xlink"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      version="5.0"
+      xml:id="ssec-relnotes-1.12">
+
+<title>Release 1.12 (TBA)</title>
+
+<para>This release has the following new features:</para>
+
+<itemizedlist>
+
+  <listitem>
+    <para>It is no longer necessary to set the
+    <envar>NIX_REMOTE</envar> environment variable if you need to use
+    the Nix daemon. Nix will use the daemon automatically if you don’t
+    have write access to the Nix database.</para>
+  </listitem>
+
+</itemizedlist>
+
+<para>This release has contributions from TBD.</para>
+
+</section>
diff --git a/doc/manual/release-notes/rl-1.2.xml b/doc/manual/release-notes/rl-1.2.xml
index 6c05444ff76e..dc272c420ddb 100644
--- a/doc/manual/release-notes/rl-1.2.xml
+++ b/doc/manual/release-notes/rl-1.2.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.2">
 
-<title>Release 1.2 (December 6, 2012)</title>
+<title>Release 1.2 (2012-12-06)</title>
 
 <para>This release has the following improvements and changes:</para>
 
@@ -154,4 +154,4 @@ $ mount -o remount,ro,bind /nix/store
 <para>This release has contributions from Eelco Dolstra, Florian
 Friesdorf, Mats Erik Andersson and Shea Levy.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.3.xml b/doc/manual/release-notes/rl-1.3.xml
index 04169ed01b82..e2009ee3ba4b 100644
--- a/doc/manual/release-notes/rl-1.3.xml
+++ b/doc/manual/release-notes/rl-1.3.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.3">
 
-<title>Release 1.3 (January 4, 2013)</title>
+<title>Release 1.3 (2013-01-04)</title>
 
 <para>This is primarily a bug fix release.  When this version is first
 run on Linux, it removes any immutable bits from the Nix store and
@@ -16,4 +16,4 @@ efficient.)</para>
 <para>This release has contributions from Eelco Dolstra and Stuart
 Pernsteiner.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.4.xml b/doc/manual/release-notes/rl-1.4.xml
index d48e43f21c1b..aefb22f2b934 100644
--- a/doc/manual/release-notes/rl-1.4.xml
+++ b/doc/manual/release-notes/rl-1.4.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.4">
 
-<title>Release 1.4 (February 26, 2013)</title>
+<title>Release 1.4 (2013-02-26)</title>
 
 <para>This release fixes a security bug in multi-user operation.  It
 was possible for derivations to cause the mode of files outside of the
@@ -36,4 +36,4 @@ xlink:href="https://github.com/NixOS/nix/commit/5526a282b5b44e9296e61e07d7d2626a
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.5.1.xml b/doc/manual/release-notes/rl-1.5.1.xml
index 046960313ffd..035c8dbcbb16 100644
--- a/doc/manual/release-notes/rl-1.5.1.xml
+++ b/doc/manual/release-notes/rl-1.5.1.xml
@@ -4,9 +4,9 @@
       version="5.0"
       xml:id="ssec-relnotes-1.5.1">
 
-<title>Release 1.5.1 (February 28, 2013)</title>
+<title>Release 1.5.1 (2013-02-28)</title>
 
 <para>The bug fix to the bug fix had a bug itself, of course.  But
 this time it will work for sure!</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.5.2.xml b/doc/manual/release-notes/rl-1.5.2.xml
index d2f53bbdc0bd..7e81dd243284 100644
--- a/doc/manual/release-notes/rl-1.5.2.xml
+++ b/doc/manual/release-notes/rl-1.5.2.xml
@@ -4,9 +4,9 @@
       version="5.0"
       xml:id="ssec-relnotes-1.5.2">
 
-<title>Release 1.5.2 (May 13, 2013)</title>
+<title>Release 1.5.2 (2013-05-13)</title>
 
 <para>This is primarily a bug fix release.  It has contributions from
 Eelco Dolstra, Lluís Batlle i Rossell and Shea Levy.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.5.xml b/doc/manual/release-notes/rl-1.5.xml
index 84e0e9024946..8e279d7693e0 100644
--- a/doc/manual/release-notes/rl-1.5.xml
+++ b/doc/manual/release-notes/rl-1.5.xml
@@ -4,9 +4,9 @@
       version="5.0"
       xml:id="ssec-relnotes-1.5">
 
-<title>Release 1.5 (February 27, 2013)</title>
+<title>Release 1.5 (2013-02-27)</title>
 
 <para>This is a brown paper bag release to fix a regression introduced
 by the hard link security fix in 1.4.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.6.1.xml b/doc/manual/release-notes/rl-1.6.1.xml
index 1ca4dfebacdc..9ecc52734737 100644
--- a/doc/manual/release-notes/rl-1.6.1.xml
+++ b/doc/manual/release-notes/rl-1.6.1.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.6.1">
 
-<title>Release 1.6.1 (October 28, 2013)</title>
+<title>Release 1.6.1 (2013-10-28)</title>
 
 <para>This is primarily a bug fix release.  Changes of interest
 are:</para>
@@ -66,4 +66,4 @@ are:</para>
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.6.xml b/doc/manual/release-notes/rl-1.6.xml
index a3d61faf2b73..580563420949 100644
--- a/doc/manual/release-notes/rl-1.6.xml
+++ b/doc/manual/release-notes/rl-1.6.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.6.0">
 
-<title>Release 1.6 (September 10, 2013)</title>
+<title>Release 1.6 (2013-09-10)</title>
 
 <para>In addition to the usual bug fixes, this release has several new
 features:</para>
@@ -124,4 +124,4 @@ in pkgs.bar
 Florian Friesdorf, Gergely Risko, Ivan Kozik, Ludovic Courtès and Shea
 Levy.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.7.xml b/doc/manual/release-notes/rl-1.7.xml
index a9863b99cba0..44ecaa78da5f 100644
--- a/doc/manual/release-notes/rl-1.7.xml
+++ b/doc/manual/release-notes/rl-1.7.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.7">
 
-<title>Release 1.7 (April 11, 2014)</title>
+<title>Release 1.7 (2014-04-11)</title>
 
 <para>In addition to the usual bug fixes, this release has the
 following new features:</para>
@@ -260,4 +260,4 @@ error: attribute `nixUnstabl' missing, at /etc/nixos/configurations/misc/eelco/m
 Eelco Dolstra, Ian-Woo Kim, Ludovic Courtès, Maxim Ivanov, Petr
 Rockai, Ricardo M. Correia and Shea Levy.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.8.xml b/doc/manual/release-notes/rl-1.8.xml
index b7acc1773baa..48caac2c6b60 100644
--- a/doc/manual/release-notes/rl-1.8.xml
+++ b/doc/manual/release-notes/rl-1.8.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.8">
 
-<title>Release 1.8 (December 14, 2014)</title>
+<title>Release 1.8 (2014-12-14)</title>
 
 <itemizedlist>
 
diff --git a/doc/manual/release-notes/rl-1.9.xml b/doc/manual/release-notes/rl-1.9.xml
index e0c79e751ed7..c8406bd2077c 100644
--- a/doc/manual/release-notes/rl-1.9.xml
+++ b/doc/manual/release-notes/rl-1.9.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.9">
 
-<title>Release 1.9 (June 12, 2015)</title>
+<title>Release 1.9 (2015-06-12)</title>
 
 <para>In addition to the usual bug fixes, this release has the
 following new features:</para>
diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs
index 07d81aa3ab05..44c88a87bfc6 100644
--- a/perl/lib/Nix/Store.xs
+++ b/perl/lib/Nix/Store.xs
@@ -6,10 +6,11 @@
 #undef do_open
 #undef do_close
 
-#include <store-api.hh>
-#include <globals.hh>
-#include <misc.hh>
-#include <util.hh>
+#include "derivations.hh"
+#include "globals.hh"
+#include "store-api.hh"
+#include "util.hh"
+#include "crypto.hh"
 
 #if HAVE_SODIUM
 #include <sodium.h>
@@ -19,19 +20,21 @@
 using namespace nix;
 
 
-void doInit()
+static ref<Store> store()
 {
-    if (!store) {
+    static std::shared_ptr<Store> _store;
+    if (!_store) {
         try {
             settings.processEnvironment();
             settings.loadConfFile();
             settings.update();
             settings.lockCPU = false;
-            store = openStore();
+            _store = openStore();
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
     }
+    return ref<Store>(_store);
 }
 
 
@@ -45,7 +48,7 @@ PROTOTYPES: ENABLE
 
 void init()
     CODE:
-        doInit();
+        store();
 
 
 void setVerbosity(int level)
@@ -56,10 +59,9 @@ void setVerbosity(int level)
 int isValidPath(char * path)
     CODE:
         try {
-            doInit();
-            RETVAL = store->isValidPath(path);
+            RETVAL = store()->isValidPath(path);
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
     OUTPUT:
         RETVAL
@@ -68,50 +70,46 @@ int isValidPath(char * path)
 SV * queryReferences(char * path)
     PPCODE:
         try {
-            doInit();
             PathSet paths;
-            store->queryReferences(path, paths);
+            store()->queryReferences(path, paths);
             for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i)
                 XPUSHs(sv_2mortal(newSVpv(i->c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * queryPathHash(char * path)
     PPCODE:
         try {
-            doInit();
-            Hash hash = store->queryPathHash(path);
+            Hash hash = store()->queryPathHash(path);
             string s = "sha256:" + printHash32(hash);
             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * queryDeriver(char * path)
     PPCODE:
         try {
-            doInit();
-            Path deriver = store->queryDeriver(path);
+            Path deriver = store()->queryDeriver(path);
             if (deriver == "") XSRETURN_UNDEF;
             XPUSHs(sv_2mortal(newSVpv(deriver.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * queryPathInfo(char * path, int base32)
     PPCODE:
         try {
-            doInit();
-            ValidPathInfo info = store->queryPathInfo(path);
+            ValidPathInfo info = store()->queryPathInfo(path);
             if (info.deriver == "")
                 XPUSHs(&PL_sv_undef);
             else
                 XPUSHs(sv_2mortal(newSVpv(info.deriver.c_str(), 0)));
-            string s = "sha256:" + (base32 ? printHash32(info.hash) : printHash(info.hash));
+            string s = "sha256:" + (base32 ? printHash32(info.narHash) : printHash(info.narHash));
             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
             mXPUSHi(info.registrationTime);
             mXPUSHi(info.narSize);
@@ -120,56 +118,53 @@ SV * queryPathInfo(char * path, int base32)
                 av_push(arr, newSVpv(i->c_str(), 0));
             XPUSHs(sv_2mortal(newRV((SV *) arr)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * queryPathFromHashPart(char * hashPart)
     PPCODE:
         try {
-            doInit();
-            Path path = store->queryPathFromHashPart(hashPart);
+            Path path = store()->queryPathFromHashPart(hashPart);
             XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * computeFSClosure(int flipDirection, int includeOutputs, ...)
     PPCODE:
         try {
-            doInit();
             PathSet paths;
             for (int n = 2; n < items; ++n)
-                computeFSClosure(*store, SvPV_nolen(ST(n)), paths, flipDirection, includeOutputs);
+                store()->computeFSClosure(SvPV_nolen(ST(n)), paths, flipDirection, includeOutputs);
             for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i)
                 XPUSHs(sv_2mortal(newSVpv(i->c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * topoSortPaths(...)
     PPCODE:
         try {
-            doInit();
             PathSet paths;
             for (int n = 0; n < items; ++n) paths.insert(SvPV_nolen(ST(n)));
-            Paths sorted = topoSortPaths(*store, paths);
+            Paths sorted = store()->topoSortPaths(paths);
             for (Paths::iterator i = sorted.begin(); i != sorted.end(); ++i)
                 XPUSHs(sv_2mortal(newSVpv(i->c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * followLinksToStorePath(char * path)
     CODE:
         try {
-            doInit();
+            store();
             RETVAL = newSVpv(followLinksToStorePath(path).c_str(), 0);
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
     OUTPUT:
         RETVAL
@@ -178,24 +173,22 @@ SV * followLinksToStorePath(char * path)
 void exportPaths(int fd, int sign, ...)
     PPCODE:
         try {
-            doInit();
             Paths paths;
             for (int n = 2; n < items; ++n) paths.push_back(SvPV_nolen(ST(n)));
             FdSink sink(fd);
-            exportPaths(*store, paths, sign, sink);
+            store()->exportPaths(paths, sign, sink);
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 void importPaths(int fd)
     PPCODE:
         try {
-            doInit();
             FdSource source(fd);
-            store->importPaths(false, source);
+            store()->importPaths(false, source);
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
@@ -206,7 +199,7 @@ SV * hashPath(char * algo, int base32, char * path)
             string s = base32 ? printHash32(h) : printHash(h);
             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
@@ -217,7 +210,7 @@ SV * hashFile(char * algo, int base32, char * path)
             string s = base32 ? printHash32(h) : printHash(h);
             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
@@ -228,7 +221,7 @@ SV * hashString(char * algo, int base32, char * s)
             string s = base32 ? printHash32(h) : printHash(h);
             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
@@ -239,28 +232,21 @@ SV * convertHash(char * algo, char * s, int toBase32)
             string s = toBase32 ? printHash32(h) : printHash(h);
             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
-SV * signString(SV * secretKey_, char * msg)
+SV * signString(char * secretKey_, char * msg)
     PPCODE:
         try {
 #if HAVE_SODIUM
-            STRLEN secretKeyLen;
-            unsigned char * secretKey = (unsigned char *) SvPV(secretKey_, secretKeyLen);
-            if (secretKeyLen != crypto_sign_SECRETKEYBYTES)
-                throw Error("secret key is not valid");
-
-            unsigned char sig[crypto_sign_BYTES];
-            unsigned long long sigLen;
-            crypto_sign_detached(sig, &sigLen, (unsigned char *) msg, strlen(msg), secretKey);
-            XPUSHs(sv_2mortal(newSVpv((char *) sig, sigLen)));
+            auto sig = SecretKey(secretKey_).signDetached(msg);
+            XPUSHs(sv_2mortal(newSVpv(sig.c_str(), sig.size())));
 #else
             throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
 #endif
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
@@ -283,7 +269,7 @@ int checkSignature(SV * publicKey_, SV * sig_, char * msg)
             throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
 #endif
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
     OUTPUT:
         RETVAL
@@ -292,24 +278,22 @@ int checkSignature(SV * publicKey_, SV * sig_, char * msg)
 SV * addToStore(char * srcPath, int recursive, char * algo)
     PPCODE:
         try {
-            doInit();
-            Path path = store->addToStore(baseNameOf(srcPath), srcPath, recursive, parseHashType(algo));
+            Path path = store()->addToStore(baseNameOf(srcPath), srcPath, recursive, parseHashType(algo));
             XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * makeFixedOutputPath(int recursive, char * algo, char * hash, char * name)
     PPCODE:
         try {
-            doInit();
             HashType ht = parseHashType(algo);
             Path path = makeFixedOutputPath(recursive, ht,
                 parseHash16or32(ht, hash), name);
             XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
@@ -318,8 +302,7 @@ SV * derivationFromPath(char * drvPath)
         HV *hash;
     CODE:
         try {
-            doInit();
-            Derivation drv = derivationFromPath(*store, drvPath);
+            Derivation drv = store()->derivationFromPath(drvPath);
             hash = newHV();
 
             HV * outputs = newHV();
@@ -352,7 +335,7 @@ SV * derivationFromPath(char * drvPath)
 
             RETVAL = newRV_noinc((SV *)hash);
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
     OUTPUT:
         RETVAL
@@ -361,8 +344,7 @@ SV * derivationFromPath(char * drvPath)
 void addTempRoot(char * storePath)
     PPCODE:
         try {
-            doInit();
-            store->addTempRoot(storePath);
+            store()->addTempRoot(storePath);
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
diff --git a/release.nix b/release.nix
index adf87f68ada3..79df103ccfc6 100644
--- a/release.nix
+++ b/release.nix
@@ -1,4 +1,5 @@
 { nix ? { outPath = ./.; revCount = 1234; shortRev = "abcdef"; }
+, nixpkgs ? { outPath = <nixpkgs>; revCount = 1234; shortRev = "abcdef"; }
 , officialRelease ? false
 }:
 
@@ -76,7 +77,8 @@ let
 
     build = pkgs.lib.genAttrs systems (system:
 
-      with import <nixpkgs> { inherit system; };
+      # FIXME: temporarily use a different branch for the Darwin build.
+      with import (if system == "x86_64-darwin" then <nixpkgs-darwin> else <nixpkgs>) { inherit system; };
 
       releaseTools.nixBuild {
         name = "nix";
@@ -110,7 +112,8 @@ let
 
     binaryTarball = pkgs.lib.genAttrs systems (system:
 
-      with import <nixpkgs> { inherit system; };
+      # FIXME: temporarily use a different branch for the Darwin build.
+      with import (if system == "x86_64-darwin" then <nixpkgs-darwin> else <nixpkgs>) { inherit system; };
 
       let
         toplevel = builtins.getAttr system jobs.build;
@@ -152,7 +155,7 @@ let
         src = tarball;
 
         buildInputs =
-          [ curl perl bzip2 openssl pkgconfig sqlite
+          [ curl perl bzip2 openssl pkgconfig sqlite xz libsodium
             # These are for "make check" only:
             graphviz libxml2 libxslt
           ];
@@ -177,8 +180,6 @@ let
       };
 
 
-    rpm_fedora18i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora18i386) [];
-    rpm_fedora18x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora18x86_64) [];
     rpm_fedora19i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora19i386) [];
     rpm_fedora19x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora19x86_64) [];
     rpm_fedora20i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora20i386) [];
@@ -187,15 +188,9 @@ let
     rpm_fedora21x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora21x86_64) [ "libsodium-devel" ];
 
 
-    deb_debian7i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian7i386) [];
-    deb_debian7x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian7x86_64) [];
     deb_debian8i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian8i386) [ "libsodium-dev" ];
     deb_debian8x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian8x86_64) [ "libsodium-dev" ];
 
-    deb_ubuntu1210i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1210i386) [];
-    deb_ubuntu1210x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1210x86_64) [];
-    deb_ubuntu1304i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1304i386) [];
-    deb_ubuntu1304x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1304x86_64) [];
     deb_ubuntu1310i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1310i386) [];
     deb_ubuntu1310x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1310x86_64) [];
     deb_ubuntu1404i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1404i386) [];
@@ -232,6 +227,26 @@ let
           touch $out/nix-support/hydra-build-products
         ''); # */
 
+    tests.evalNixpkgs =
+      import <nixpkgs/pkgs/top-level/make-tarball.nix> {
+        inherit nixpkgs;
+        inherit pkgs;
+        nix = build.x86_64-linux;
+        officialRelease = false;
+      };
+
+    tests.evalNixOS =
+      pkgs.runCommand "eval-nixos" { buildInputs = [ build.x86_64-linux ]; }
+        ''
+          export NIX_DB_DIR=$TMPDIR
+          export NIX_STATE_DIR=$TMPDIR
+          nix-store --init
+
+          nix-instantiate ${nixpkgs}/nixos/release-combined.nix -A tested --dry-run
+
+          touch $out
+        '';
+
 
     # Aggregate job containing the release-critical jobs.
     release = pkgs.releaseTools.aggregate {
@@ -249,8 +264,8 @@ let
           binaryTarball.x86_64-darwin
           #binaryTarball.x86_64-freebsd
           binaryTarball.x86_64-linux
-          deb_debian7i386
-          deb_debian7x86_64
+          deb_debian8i386
+          deb_debian8x86_64
           deb_ubuntu1404i386 # LTS
           deb_ubuntu1404x86_64 # LTS
           deb_ubuntu1504i386
@@ -262,6 +277,8 @@ let
           tests.remoteBuilds
           tests.nix-copy-closure
           tests.binaryTarball
+          tests.evalNixpkgs
+          tests.evalNixOS
         ];
     };
 
@@ -302,7 +319,7 @@ let
       src = jobs.tarball;
       diskImage = (diskImageFun vmTools.diskImageFuns)
         { extraPackages =
-            [ "libdbd-sqlite3-perl" "libsqlite3-dev" "libbz2-dev" "libwww-curl-perl" "libcurl-dev" "libssl-dev" "liblzma-dev" ]
+            [ "libdbd-sqlite3-perl" "libsqlite3-dev" "libbz2-dev" "libwww-curl-perl" "libcurl-dev" "libcurl3-nss" "libssl-dev" "liblzma-dev" ]
             ++ extraPackages; };
       memSize = 1024;
       meta.schedulingPriority = 50;
diff --git a/scripts/download-from-binary-cache.pl.in b/scripts/download-from-binary-cache.pl.in
index 60f7f9aef3d2..a4f858610aca 100644
--- a/scripts/download-from-binary-cache.pl.in
+++ b/scripts/download-from-binary-cache.pl.in
@@ -566,7 +566,7 @@ sub downloadBinary {
         die if $requireSignedBinaryCaches && !defined $info->{signedBy};
         print STDERR "\n*** Downloading ‘$url’ ", ($requireSignedBinaryCaches ? "(signed by ‘$info->{signedBy}’) " : ""), "to ‘$storePath’...\n";
         checkURL $url;
-        if (system("$Nix::Config::curl --fail --location --insecure --connect-timeout $curlConnectTimeout -A '$userAgent' '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
+        if (system("$Nix::Config::curl --fail --location --connect-timeout $curlConnectTimeout -A '$userAgent' '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
             warn "download of ‘$url’ failed" . ($! ? ": $!" : "") . "\n";
             next;
         }
diff --git a/scripts/download-using-manifests.pl.in b/scripts/download-using-manifests.pl.in
index 591cd6b43a3a..ffc49f8fffde 100755
--- a/scripts/download-using-manifests.pl.in
+++ b/scripts/download-using-manifests.pl.in
@@ -17,8 +17,7 @@ my $logFile = "$Nix::Config::logDir/downloads";
 # estimating the expected download size.
 my $fast = 1;
 
-# ‘--insecure’ is fine because Nix verifies the hash of the result.
-my $curl = "$Nix::Config::curl --fail --location --insecure";
+my $curl = "$Nix::Config::curl --fail --location";
 
 
 # Open the manifest cache and update it if necessary.
diff --git a/scripts/install-nix-from-closure.sh b/scripts/install-nix-from-closure.sh
index c3da59980a0a..bef5cd4f15fa 100644
--- a/scripts/install-nix-from-closure.sh
+++ b/scripts/install-nix-from-closure.sh
@@ -92,7 +92,7 @@ p=$NIX_LINK/etc/profile.d/nix.sh
 added=
 for i in .bash_profile .bash_login .profile; do
     fn="$HOME/$i"
-    if [ -e "$fn" ]; then
+    if [ -w "$fn" ]; then
         if ! grep -q "$p" "$fn"; then
             echo "modifying $fn..." >&2
             echo "if [ -e $p ]; then . $p; fi # added by Nix installer" >> $fn
diff --git a/scripts/nix-build.in b/scripts/nix-build.in
index 0a4431681cb1..b93e5ab1390a 100755
--- a/scripts/nix-build.in
+++ b/scripts/nix-build.in
@@ -6,6 +6,7 @@ use Nix::Config;
 use Nix::Store;
 use Nix::Utils;
 use File::Basename;
+use Text::ParseWords;
 use Cwd;
 
 binmode STDERR, ":encoding(utf8)";
@@ -56,7 +57,7 @@ if ($runEnv && defined $ARGV[0] && $ARGV[0] !~ /nix-shell/) {
             while (<SCRIPT>) {
                 chomp;
                 if (/^\#\!\s*nix-shell (.*)$/) {
-                    push @ARGV, split(/ /, $1);
+                    push @ARGV, shellwords($1);
                 }
             }
         }
@@ -269,7 +270,7 @@ foreach my $expr (@exprs) {
         my $tmp = $ENV{"TMPDIR"} // $ENV{"XDG_RUNTIME_DIR"} // "/tmp";
         if ($pure) {
             foreach my $name (keys %ENV) {
-                next if grep { $_ eq $name } ("HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER");
+                next if grep { $_ eq $name } ("HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER", "NIX_BUILD_SHELL");
                 delete $ENV{$name};
             }
             # NixOS hack: prevent /etc/bashrc from sourcing /etc/profile.
diff --git a/scripts/nix-profile.sh.in b/scripts/nix-profile.sh.in
index 672d1f035f91..6616b12b0cf4 100644
--- a/scripts/nix-profile.sh.in
+++ b/scripts/nix-profile.sh.in
@@ -11,8 +11,8 @@ if [ -n "$HOME" ]; then
     export PATH=$NIX_LINK/bin:$NIX_LINK/sbin:$PATH
 
     # Subscribe the user to the Nixpkgs channel by default.
-    if [ ! -e $HOME/.nix-channels ]; then
-        echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > $HOME/.nix-channels
+    if [ ! -e "$HOME/.nix-channels" ]; then
+        echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > "$HOME/.nix-channels"
     fi
 
     # Append ~/.nix-defexpr/channels/nixpkgs to $NIX_PATH so that
@@ -23,6 +23,8 @@ if [ -n "$HOME" ]; then
     # Set $SSL_CERT_FILE so that Nixpkgs applications like curl work.
     if [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
         export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
+    elif [ -e /etc/ssl/ca-bundle.pem ]; then # openSUSE Tumbleweed
+        export SSL_CERT_FILE=/etc/ssl/ca-bundle.pem
     elif [ -e /etc/ssl/certs/ca-bundle.crt ]; then # Old NixOS
         export SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt
     elif [ -e /etc/pki/tls/certs/ca-bundle.crt ]; then # Fedora, CentOS
diff --git a/scripts/nix-push.in b/scripts/nix-push.in
index 2d9d83f59b92..54456ac9512e 100755
--- a/scripts/nix-push.in
+++ b/scripts/nix-push.in
@@ -258,13 +258,10 @@ for (my $n = 0; $n < scalar @storePaths2; $n++) {
     }
 
     if (defined $secretKeyFile) {
-        my $s = readFile $secretKeyFile;
-        chomp $s;
-        my ($keyName, $secretKey) = split ":", $s;
-        die "invalid secret key file ‘$secretKeyFile’\n" unless defined $keyName && defined $secretKey;
+        my $secretKey = readFile $secretKeyFile;
         my $fingerprint = fingerprintPath($storePath, $narHash, $narSize, $refs);
-        my $sig = encode_base64(signString(decode_base64($secretKey), $fingerprint), "");
-        $info .= "Sig: $keyName:$sig\n";
+        my $sig = signString($secretKey, $fingerprint);
+        $info .= "Sig: $sig\n";
     }
 
     my $pathHash = substr(basename($storePath), 0, 32);
diff --git a/src/libexpr/common-opts.cc b/src/libexpr/common-opts.cc
index 13760490d9c4..68ab4b5cdcbf 100644
--- a/src/libexpr/common-opts.cc
+++ b/src/libexpr/common-opts.cc
@@ -55,7 +55,7 @@ bool parseSearchPathArg(Strings::iterator & i,
 Path lookupFileArg(EvalState & state, string s)
 {
     if (isUri(s))
-        return downloadFileCached(s, true);
+        return downloadFileCached(state.store, s, true);
     else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
         Path p = s.substr(1, s.size() - 2);
         return state.findFile(p);
diff --git a/src/libexpr/common-opts.hh b/src/libexpr/common-opts.hh
index be0f40202430..cb2732d6fe7e 100644
--- a/src/libexpr/common-opts.hh
+++ b/src/libexpr/common-opts.hh
@@ -4,6 +4,8 @@
 
 namespace nix {
 
+class Store;
+
 /* Some common option parsing between nix-env and nix-instantiate. */
 bool parseAutoArgs(Strings::iterator & i,
     const Strings::iterator & argsEnd, std::map<string, string> & res);
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index df1600bc1963..8ce2f3dfa6af 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -128,6 +128,9 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
     case tExternal:
         str << *v.external;
         break;
+    case tFloat:
+        str << v.fpoint;
+        break;
     default:
         throw Error("invalid value");
     }
@@ -161,6 +164,7 @@ string showType(const Value & v)
         case tPrimOp: return "a built-in function";
         case tPrimOpApp: return "a partially applied built-in function";
         case tExternal: return v.external->showType();
+        case tFloat: return "a float";
     }
     abort();
 }
@@ -244,7 +248,7 @@ static Strings parseNixPath(const string & in)
 }
 
 
-EvalState::EvalState(const Strings & _searchPath)
+EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
     : sWith(symbols.create("<with>"))
     , sOutPath(symbols.create("outPath"))
     , sDrvPath(symbols.create("drvPath"))
@@ -261,6 +265,8 @@ EvalState::EvalState(const Strings & _searchPath)
     , sLine(symbols.create("line"))
     , sColumn(symbols.create("column"))
     , sFunctor(symbols.create("__functor"))
+    , sToString(symbols.create("__toString"))
+    , store(store)
     , baseEnv(allocEnv(128))
     , staticBaseEnv(false, 0)
 {
@@ -577,6 +583,12 @@ Value * ExprInt::maybeThunk(EvalState & state, Env & env)
     return &v;
 }
 
+Value * ExprFloat::maybeThunk(EvalState & state, Env & env)
+{
+    nrAvoided++;
+    return &v;
+}
+
 Value * ExprPath::maybeThunk(EvalState & state, Env & env)
 {
     nrAvoided++;
@@ -664,6 +676,11 @@ void ExprInt::eval(EvalState & state, Env & env, Value & v)
 }
 
 
+void ExprFloat::eval(EvalState & state, Env & env, Value & v)
+{
+    v = this->v;
+}
+
 void ExprString::eval(EvalState & state, Env & env, Value & v)
 {
     v = this->v;
@@ -1209,6 +1226,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
     PathSet context;
     std::ostringstream s;
     NixInt n = 0;
+    NixFloat nf = 0;
 
     bool first = !forceString;
     ValueType firstType = tString;
@@ -1227,15 +1245,30 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
         }
 
         if (firstType == tInt) {
-            if (vTmp.type != tInt)
+            if (vTmp.type == tInt) {
+                n += vTmp.integer;
+            } else if (vTmp.type == tFloat) {
+                // Upgrade the type from int to float;
+                firstType = tFloat;
+                nf = n;
+                nf += vTmp.fpoint;
+            } else
                 throwEvalError("cannot add %1% to an integer, at %2%", showType(vTmp), pos);
-            n += vTmp.integer;
+        } else if (firstType == tFloat) {
+            if (vTmp.type == tInt) {
+                nf += vTmp.integer;
+            } else if (vTmp.type == tFloat) {
+                nf += vTmp.fpoint;
+            } else
+                throwEvalError("cannot add %1% to a float, at %2%", showType(vTmp), pos);
         } else
             s << state.coerceToString(pos, vTmp, context, false, firstType == tString);
     }
 
     if (firstType == tInt)
         mkInt(v, n);
+    else if (firstType == tFloat)
+        mkFloat(v, nf);
     else if (firstType == tPath) {
         if (!context.empty())
             throwEvalError("a string that refers to a store path cannot be appended to a path, at %1%", pos);
@@ -1293,6 +1326,17 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos)
 }
 
 
+NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
+{
+    forceValue(v, pos);
+    if (v.type == tInt)
+        return v.integer;
+    else if (v.type != tFloat)
+        throwTypeError("value is %1% while a float was expected, at %2%", v, pos);
+    return v.fpoint;
+}
+
+
 bool EvalState::forceBool(Value & v)
 {
     forceValue(v);
@@ -1389,7 +1433,14 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
     }
 
     if (v.type == tAttrs) {
-        Bindings::iterator i = v.attrs->find(sOutPath);
+        auto i = v.attrs->find(sToString);
+        if (i != v.attrs->end()) {
+            forceValue(*i->value, pos);
+            Value v1;
+            callFunction(*i->value, v, v1, pos);
+            return coerceToString(pos, v1, context, coerceMore, copyToStore);
+        }
+        i = v.attrs->find(sOutPath);
         if (i == v.attrs->end()) throwTypeError("cannot coerce a set to a string, at %1%", pos);
         return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
     }
@@ -1404,6 +1455,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
         if (v.type == tBool && v.boolean) return "1";
         if (v.type == tBool && !v.boolean) return "";
         if (v.type == tInt) return std::to_string(v.integer);
+        if (v.type == tFloat) return std::to_string(v.fpoint);
         if (v.type == tNull) return "";
 
         if (v.isList()) {
@@ -1465,6 +1517,13 @@ bool EvalState::eqValues(Value & v1, Value & v2)
        uniqList on a list of sets.)  Will remove this eventually. */
     if (&v1 == &v2) return true;
 
+    // Special case type-compatibility between float and int
+    if (v1.type == tInt && v2.type == tFloat)
+        return v1.integer == v2.fpoint;
+    if (v1.type == tFloat && v2.type == tInt)
+        return v1.fpoint == v2.integer;
+
+    // All other types are not compatible with each other.
     if (v1.type != v2.type) return false;
 
     switch (v1.type) {
@@ -1522,6 +1581,9 @@ bool EvalState::eqValues(Value & v1, Value & v2)
         case tExternal:
             return *v1.external == *v2.external;
 
+        case tFloat:
+            return v1.fpoint == v2.fpoint;
+
         default:
             throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2));
     }
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index eb55f6d4d431..40e05712bab1 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -16,6 +16,7 @@
 namespace nix {
 
 
+class Store;
 class EvalState;
 
 
@@ -69,7 +70,7 @@ public:
 
     const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
         sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
-        sFile, sLine, sColumn, sFunctor;
+        sFile, sLine, sColumn, sFunctor, sToString;
     Symbol sDerivationNix;
 
     /* If set, force copying files to the Nix store even if they
@@ -82,6 +83,8 @@ public:
 
     Value vEmptySet;
 
+    const ref<Store> store;
+
 private:
     SrcToStore srcToStore;
 
@@ -97,7 +100,7 @@ private:
 
 public:
 
-    EvalState(const Strings & _searchPath);
+    EvalState(const Strings & _searchPath, ref<Store> store);
     ~EvalState();
 
     void addToSearchPath(const string & s, bool warn = false);
@@ -144,6 +147,7 @@ public:
 
     /* Force `v', and then verify that it has the expected type. */
     NixInt forceInt(Value & v, const Pos & pos);
+    NixFloat forceFloat(Value & v, const Pos & pos);
     bool forceBool(Value & v);
     inline void forceAttrs(Value & v);
     inline void forceAttrs(Value & v, const Pos & pos);
@@ -240,6 +244,8 @@ public:
     /* Print statistics. */
     void printStats();
 
+    void realiseContext(const PathSet & context);
+
 private:
 
     unsigned long nrEnvs = 0;
@@ -290,7 +296,4 @@ struct InvalidPathError : EvalError
 #endif
 };
 
-/* Realise all paths in `context' */
-void realiseContext(const PathSet & context);
-
 }
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index 1002ee6285af..996c2c5f4975 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -106,7 +106,8 @@ bool DrvInfo::checkMeta(Value & v)
             if (!checkMeta(*i.value)) return false;
         return true;
     }
-    else return v.type == tInt || v.type == tBool || v.type == tString;
+    else return v.type == tInt || v.type == tBool || v.type == tString ||
+                v.type == tFloat;
 }
 
 
@@ -127,7 +128,7 @@ string DrvInfo::queryMetaString(const string & name)
 }
 
 
-int DrvInfo::queryMetaInt(const string & name, int def)
+NixInt DrvInfo::queryMetaInt(const string & name, NixInt def)
 {
     Value * v = queryMeta(name);
     if (!v) return def;
@@ -135,12 +136,26 @@ int DrvInfo::queryMetaInt(const string & name, int def)
     if (v->type == tString) {
         /* Backwards compatibility with before we had support for
            integer meta fields. */
-        int n;
+        NixInt n;
         if (string2Int(v->string.s, n)) return n;
     }
     return def;
 }
 
+NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def)
+{
+    Value * v = queryMeta(name);
+    if (!v) return def;
+    if (v->type == tFloat) return v->fpoint;
+    if (v->type == tString) {
+        /* Backwards compatibility with before we had support for
+           float meta fields. */
+        NixFloat n;
+        if (string2Float(v->string.s, n)) return n;
+    }
+    return def;
+}
+
 
 bool DrvInfo::queryMetaBool(const string & name, bool def)
 {
diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh
index 98f762494aa5..365c66c8d710 100644
--- a/src/libexpr/get-drvs.hh
+++ b/src/libexpr/get-drvs.hh
@@ -47,7 +47,8 @@ public:
     StringSet queryMetaNames();
     Value * queryMeta(const string & name);
     string queryMetaString(const string & name);
-    int queryMetaInt(const string & name, int def);
+    NixInt queryMetaInt(const string & name, NixInt def);
+    NixFloat queryMetaFloat(const string & name, NixFloat def);
     bool queryMetaBool(const string & name, bool def);
     void setMeta(const string & name, Value * v);
 
diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc
index 0898b560911b..1daf84600dca 100644
--- a/src/libexpr/json-to-value.cc
+++ b/src/libexpr/json-to-value.cc
@@ -105,17 +105,21 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
         mkString(v, parseJSONString(s));
     }
 
-    else if (isdigit(*s) || *s == '-') {
-        bool neg = false;
-        if (*s == '-') {
-            neg = true;
-            if (!*++s) throw JSONParseError("unexpected end of JSON number");
+    else if (isdigit(*s) || *s == '-' || *s == '.' ) {
+        // Buffer into a string first, then use built-in C++ conversions
+        std::string tmp_number;
+        ValueType number_type = tInt;
+
+        while (isdigit(*s) || *s == '-' || *s == '.' || *s == 'e' || *s == 'E') {
+            if (*s == '.' || *s == 'e' || *s == 'E')
+                number_type = tFloat;
+            tmp_number += *s++;
         }
-        NixInt n = 0;
-        // FIXME: detect overflow
-        while (isdigit(*s)) n = n * 10 + (*s++ - '0');
-        if (*s == '.' || *s == 'e') throw JSONParseError("floating point JSON numbers are not supported");
-        mkInt(v, neg ? -n : n);
+
+        if (number_type == tFloat)
+            mkFloat(v, stod(tmp_number));
+        else
+            mkInt(v, stoi(tmp_number));
     }
 
     else if (strncmp(s, "true", 4) == 0) {
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index 1f2957ec71ad..701c01aff973 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -8,6 +8,7 @@
 
 %x STRING
 %x IND_STRING
+%x INSIDE_DOLLAR_CURLY
 
 
 %{
@@ -85,6 +86,7 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)
 
 ID          [a-zA-Z\_][a-zA-Z0-9\_\'\-]*
 INT         [0-9]+
+FLOAT       (([1-9][0-9]*\.[0-9]*)|(0?\.[0-9]+))([Ee][+-]?[0-9]+)?
 PATH        [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+
 HPATH       \~(\/[a-zA-Z0-9\.\_\-\+]+)+
 SPATH       \<[a-zA-Z0-9\.\_\-\+]+(\/[a-zA-Z0-9\.\_\-\+]+)*\>
@@ -93,6 +95,8 @@ URI         [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~
 
 %%
 
+<INITIAL,INSIDE_DOLLAR_CURLY>{
+
 
 if          { return IF; }
 then        { return THEN; }
@@ -123,12 +127,22 @@ or          { return OR_KW; }
                   throw ParseError(format("invalid integer ‘%1%’") % yytext);
               return INT;
             }
+{FLOAT}     { errno = 0;
+              yylval->nf = strtod(yytext, 0);
+              if (errno != 0)
+                  throw ParseError(format("invalid float ‘%1%’") % yytext);
+              return FLOAT;
+            }
 
-\$\{        { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
-\{          { PUSH_STATE(INITIAL); return '{'; }
-\}          { POP_STATE(); return '}'; }
+\$\{        { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
+}
 
-\"          { PUSH_STATE(STRING); return '"'; }
+\}                           { return '}'; }
+<INSIDE_DOLLAR_CURLY>\}      { POP_STATE(); return '}'; }
+\{                           { return '{'; }
+<INSIDE_DOLLAR_CURLY>\{      { PUSH_STATE(INSIDE_DOLLAR_CURLY); return '{'; }
+
+<INITIAL,INSIDE_DOLLAR_CURLY>\"          { PUSH_STATE(STRING); return '"'; }
 <STRING>([^\$\"\\]|\$[^\{\"\\]|\\.|\$\\.)*\$/\" |
 <STRING>([^\$\"\\]|\$[^\{\"\\]|\\.|\$\\.)+ {
               /* It is impossible to match strings ending with '$' with one
@@ -137,11 +151,11 @@ or          { return OR_KW; }
               yylval->e = unescapeStr(data->symbols, yytext);
               return STR;
             }
-<STRING>\$\{  { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
+<STRING>\$\{  { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
 <STRING>\"  { POP_STATE(); return '"'; }
 <STRING>.   return yytext[0]; /* just in case: shouldn't be reached */
 
-\'\'(\ *\n)?     { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
+<INITIAL,INSIDE_DOLLAR_CURLY>\'\'(\ *\n)?     { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
 <IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
                    yylval->e = new ExprIndStr(yytext);
                    return IND_STR;
@@ -158,7 +172,7 @@ or          { return OR_KW; }
                    yylval->e = unescapeStr(data->symbols, yytext + 2);
                    return IND_STR;
                  }
-<IND_STRING>\$\{ { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
+<IND_STRING>\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
 <IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; }
 <IND_STRING>\'   {
                    yylval->e = new ExprIndStr("'");
@@ -166,6 +180,8 @@ or          { return OR_KW; }
                  }
 <IND_STRING>.    return yytext[0]; /* just in case: shouldn't be reached */
 
+<INITIAL,INSIDE_DOLLAR_CURLY>{
+
 {PATH}      { yylval->path = strdup(yytext); return PATH; }
 {HPATH}     { yylval->path = strdup(yytext); return HPATH; }
 {SPATH}     { yylval->path = strdup(yytext); return SPATH; }
@@ -177,6 +193,7 @@ or          { return OR_KW; }
 
 .           return yytext[0];
 
+}
 
 %%
 
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index 35db52a13acc..b2c9f0528ca9 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -30,8 +30,9 @@ static void showString(std::ostream & str, const string & s)
 
 static void showId(std::ostream & str, const string & s)
 {
-    assert(!s.empty());
-    if (s == "if")
+    if (s.empty())
+        str << "\"\"";
+    else if (s == "if") // FIXME: handle other keywords
         str << '"' << s << '"';
     else {
         char c = s[0];
@@ -67,6 +68,11 @@ void ExprInt::show(std::ostream & str)
     str << n;
 }
 
+void ExprFloat::show(std::ostream & str)
+{
+    str << nf;
+}
+
 void ExprString::show(std::ostream & str)
 {
     showString(str, s);
@@ -225,6 +231,10 @@ void ExprInt::bindVars(const StaticEnv & env)
 {
 }
 
+void ExprFloat::bindVars(const StaticEnv & env)
+{
+}
+
 void ExprString::bindVars(const StaticEnv & env)
 {
 }
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index ef07d4557fe8..5e7bc40c85c9 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -98,6 +98,15 @@ struct ExprInt : Expr
     Value * maybeThunk(EvalState & state, Env & env);
 };
 
+struct ExprFloat : Expr
+{
+    NixFloat nf;
+    Value v;
+    ExprFloat(NixFloat nf) : nf(nf) { mkFloat(v, nf); };
+    COMMON_METHODS
+    Value * maybeThunk(EvalState & state, Env & env);
+};
+
 struct ExprString : Expr
 {
     Symbol s;
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index d34882f361cf..f87aa261935b 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -244,6 +244,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
   nix::Formals * formals;
   nix::Formal * formal;
   nix::NixInt n;
+  nix::NixFloat nf;
   const char * id; // !!! -> Symbol
   char * path;
   char * uri;
@@ -264,6 +265,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
 %token <id> ID ATTRPATH
 %token <e> STR IND_STR
 %token <n> INT
+%token <nf> FLOAT
 %token <path> PATH HPATH SPATH
 %token <uri> URI
 %token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL OR_KW
@@ -366,6 +368,7 @@ expr_simple
           $$ = new ExprVar(CUR_POS, data->symbols.create($1));
   }
   | INT { $$ = new ExprInt($1); }
+  | FLOAT { $$ = new ExprFloat($1); }
   | '"' string_parts '"' { $$ = $2; }
   | IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
       $$ = stripIndentation(CUR_POS, data->symbols, *$2);
@@ -603,7 +606,7 @@ void EvalState::addToSearchPath(const string & s, bool warn)
     }
 
     if (isUri(path))
-        path = downloadFileCached(path, true);
+        path = downloadFileCached(store, path, true);
 
     path = absPath(path);
     if (pathExists(path)) {
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 5b9ecc018f0e..3c899d769253 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -1,15 +1,15 @@
+#include "archive.hh"
+#include "derivations.hh"
+#include "download.hh"
+#include "eval-inline.hh"
 #include "eval.hh"
-#include "misc.hh"
 #include "globals.hh"
+#include "json-to-value.hh"
+#include "names.hh"
 #include "store-api.hh"
 #include "util.hh"
-#include "archive.hh"
-#include "value-to-xml.hh"
 #include "value-to-json.hh"
-#include "json-to-value.hh"
-#include "names.hh"
-#include "eval-inline.hh"
-#include "download.hh"
+#include "value-to-xml.hh"
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -43,7 +43,7 @@ std::pair<string, string> decodeContext(const string & s)
 InvalidPathError::InvalidPathError(const Path & path) :
     EvalError(format("path ‘%1%’ is not valid") % path), path(path) {}
 
-void realiseContext(const PathSet & context)
+void EvalState::realiseContext(const PathSet & context)
 {
     PathSet drvs;
     for (auto & i : context) {
@@ -52,16 +52,14 @@ void realiseContext(const PathSet & context)
         assert(isStorePath(ctx));
         if (!store->isValidPath(ctx))
             throw InvalidPathError(ctx);
-        if (!decoded.second.empty() && isDerivation(ctx))
+        if (!decoded.second.empty() && nix::isDerivation(ctx))
             drvs.insert(decoded.first + "!" + decoded.second);
     }
     if (!drvs.empty()) {
         /* For performance, prefetch all substitute info. */
         PathSet willBuild, willSubstitute, unknown;
         unsigned long long downloadSize, narSize;
-        queryMissing(*store, drvs,
-            willBuild, willSubstitute, unknown, downloadSize, narSize);
-
+        store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize);
         store->buildPaths(drvs);
     }
 }
@@ -75,7 +73,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
     Path path = state.coerceToPath(pos, *args[1], context);
 
     try {
-        realiseContext(context);
+        state.realiseContext(context);
     } catch (InvalidPathError & e) {
         throw EvalError(format("cannot import ‘%1%’, since path ‘%2%’ is not valid, at %3%")
             % path % e.path % pos);
@@ -83,7 +81,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
 
     path = state.checkSourcePath(path);
 
-    if (isStorePath(path) && store->isValidPath(path) && isDerivation(path)) {
+    if (isStorePath(path) && state.store->isValidPath(path) && isDerivation(path)) {
         Derivation drv = readDerivation(path);
         Value & w = *state.allocValue();
         state.mkAttrs(w, 3 + drv.outputs.size());
@@ -145,7 +143,7 @@ static void prim_importNative(EvalState & state, const Pos & pos, Value * * args
     Path path = state.coerceToPath(pos, *args[0], context);
 
     try {
-        realiseContext(context);
+        state.realiseContext(context);
     } catch (InvalidPathError & e) {
         throw EvalError(format("cannot import ‘%1%’, since path ‘%2%’ is not valid, at %3%")
             % path % e.path % pos);
@@ -197,6 +195,7 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu
         case tExternal:
             t = args[0]->external->typeOf();
             break;
+        case tFloat: t = "float"; break;
         default: abort();
     }
     mkString(v, state.symbols.create(t));
@@ -226,6 +225,12 @@ static void prim_isInt(EvalState & state, const Pos & pos, Value * * args, Value
     mkBool(v, args[0]->type == tInt);
 }
 
+/* Determine whether the argument is a float. */
+static void prim_isFloat(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+    state.forceValue(*args[0]);
+    mkBool(v, args[0]->type == tFloat);
+}
 
 /* Determine whether the argument is a string. */
 static void prim_isString(EvalState & state, const Pos & pos, Value * * args, Value & v)
@@ -247,11 +252,17 @@ struct CompareValues
 {
     bool operator () (const Value * v1, const Value * v2) const
     {
+        if (v1->type == tFloat && v2->type == tInt)
+            return v1->fpoint < v2->integer;
+        if (v1->type == tInt && v2->type == tFloat)
+            return v1->integer < v2->fpoint;
         if (v1->type != v2->type)
-            throw EvalError("cannot compare values of different types");
+            throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2));
         switch (v1->type) {
             case tInt:
                 return v1->integer < v2->integer;
+            case tFloat:
+                return v1->fpoint < v2->fpoint;
             case tString:
                 return strcmp(v1->string.s, v2->string.s) < 0;
             case tPath:
@@ -560,11 +571,12 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
            runs. */
         if (path.at(0) == '=') {
             /* !!! This doesn't work if readOnlyMode is set. */
-            PathSet refs; computeFSClosure(*store, string(path, 1), refs);
+            PathSet refs;
+            state.store->computeFSClosure(string(path, 1), refs);
             for (auto & j : refs) {
                 drv.inputSrcs.insert(j);
                 if (isDerivation(j))
-                    drv.inputDrvs[j] = store->queryDerivationOutputNames(j);
+                    drv.inputDrvs[j] = state.store->queryDerivationOutputNames(j);
             }
         }
 
@@ -581,7 +593,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
         /* Handle derivation contexts returned by
            ‘builtins.storePath’. */
         else if (isDerivation(path))
-            drv.inputDrvs[path] = store->queryDerivationOutputNames(path);
+            drv.inputDrvs[path] = state.store->queryDerivationOutputNames(path);
 
         /* Otherwise it's a source file. */
         else
@@ -630,7 +642,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
 
         /* Use the masked derivation expression to compute the output
            path. */
-        Hash h = hashDerivationModulo(*store, drv);
+        Hash h = hashDerivationModulo(*state.store, drv);
 
         for (auto & i : drv.outputs)
             if (i.second.path == "") {
@@ -641,7 +653,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
     }
 
     /* Write the resulting term into the Nix store directory. */
-    Path drvPath = writeDerivation(*store, drv, drvName, state.repair);
+    Path drvPath = writeDerivation(state.store, drv, drvName, state.repair);
 
     printMsg(lvlChatty, format("instantiated ‘%1%’ -> ‘%2%’")
         % drvName % drvPath);
@@ -649,7 +661,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
     /* Optimisation, but required in read-only mode! because in that
        case we don't actually write store derivations, so we can't
        read them later. */
-    drvHashes[drvPath] = hashDerivationModulo(*store, drv);
+    drvHashes[drvPath] = hashDerivationModulo(*state.store, drv);
 
     state.mkAttrs(v, 1 + drv.outputs.size());
     mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton<PathSet>("=" + drvPath));
@@ -695,7 +707,7 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V
         throw EvalError(format("path ‘%1%’ is not in the Nix store, at %2%") % path % pos);
     Path path2 = toStorePath(path);
     if (!settings.readOnlyMode)
-        store->ensurePath(path2);
+        state.store->ensurePath(path2);
     context.insert(path2);
     mkString(v, path, context);
 }
@@ -745,7 +757,7 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va
     PathSet context;
     Path path = state.coerceToPath(pos, *args[0], context);
     try {
-        realiseContext(context);
+        state.realiseContext(context);
     } catch (InvalidPathError & e) {
         throw EvalError(format("cannot read ‘%1%’, since path ‘%2%’ is not valid, at %3%")
             % path % e.path % pos);
@@ -786,7 +798,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
     string path = state.forceStringNoCtx(*args[1], pos);
 
     try {
-        realiseContext(context);
+        state.realiseContext(context);
     } catch (InvalidPathError & e) {
         throw EvalError(format("cannot find ‘%1%’, since path ‘%2%’ is not valid, at %3%")
             % path % e.path % pos);
@@ -801,7 +813,7 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
     PathSet ctx;
     Path path = state.coerceToPath(pos, *args[0], ctx);
     try {
-        realiseContext(ctx);
+        state.realiseContext(ctx);
     } catch (InvalidPathError & e) {
         throw EvalError(format("cannot read ‘%1%’, since path ‘%2%’ is not valid, at %3%")
             % path % e.path % pos);
@@ -879,13 +891,13 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
             if (path.at(0) != '~')
                 throw EvalError(format("in ‘toFile’: the file ‘%1%’ cannot refer to derivation outputs, at %2%") % name % pos);
             path = string(path, 1);
-	}
+        }
         refs.insert(path);
     }
 
     Path storePath = settings.readOnlyMode
         ? computeStorePathForText(name, contents, refs)
-        : store->addTextToStore(name, contents, refs, state.repair);
+        : state.store->addTextToStore(name, contents, refs, state.repair);
 
     /* Note: we don't need to add `context' to the context of the
        result, since `storePath' itself has references to the paths
@@ -951,7 +963,7 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
 
     Path dstPath = settings.readOnlyMode
         ? computeStorePathForPath(path, true, htSHA256, filter).first
-        : store->addToStore(baseNameOf(path), path, true, htSHA256, filter, state.repair);
+        : state.store->addToStore(baseNameOf(path), path, true, htSHA256, filter, state.repair);
 
     mkString(v, dstPath, singleton<PathSet>(dstPath));
 }
@@ -1154,7 +1166,7 @@ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Va
 
 /* Return a set containing the names of the formal arguments expected
    by the function `f'.  The value of each attribute is a Boolean
-   denoting whether has a default value.  For instance,
+   denoting whether the corresponding argument has a default value.  For instance,
 
       functionArgs ({ x, y ? 123}: ...)
    => { x = false; y = true; }
@@ -1374,7 +1386,7 @@ static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Val
 
     state.mkList(v, len);
 
-    for (unsigned int n = 0; n < len; ++n) {
+    for (unsigned int n = 0; n < (unsigned int) len; ++n) {
         Value * arg = state.allocValue();
         mkInt(*arg, n);
         mkApp(*(v.listElems()[n] = state.allocValue()), *args[0], *arg);
@@ -1424,27 +1436,40 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value
 
 static void prim_add(EvalState & state, const Pos & pos, Value * * args, Value & v)
 {
-    mkInt(v, state.forceInt(*args[0], pos) + state.forceInt(*args[1], pos));
+    if (args[0]->type == tFloat || args[1]->type == tFloat)
+        mkFloat(v, state.forceFloat(*args[0], pos) + state.forceFloat(*args[1], pos));
+    else
+        mkInt(v, state.forceInt(*args[0], pos) + state.forceInt(*args[1], pos));
 }
 
 
 static void prim_sub(EvalState & state, const Pos & pos, Value * * args, Value & v)
 {
-    mkInt(v, state.forceInt(*args[0], pos) - state.forceInt(*args[1], pos));
+    if (args[0]->type == tFloat || args[1]->type == tFloat)
+        mkFloat(v, state.forceFloat(*args[0], pos) - state.forceFloat(*args[1], pos));
+    else
+        mkInt(v, state.forceInt(*args[0], pos) - state.forceInt(*args[1], pos));
 }
 
 
 static void prim_mul(EvalState & state, const Pos & pos, Value * * args, Value & v)
 {
-    mkInt(v, state.forceInt(*args[0], pos) * state.forceInt(*args[1], pos));
+    if (args[0]->type == tFloat || args[1]->type == tFloat)
+        mkFloat(v, state.forceFloat(*args[0], pos) * state.forceFloat(*args[1], pos));
+    else
+        mkInt(v, state.forceInt(*args[0], pos) * state.forceInt(*args[1], pos));
 }
 
 
 static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value & v)
 {
-    NixInt i2 = state.forceInt(*args[1], pos);
-    if (i2 == 0) throw EvalError(format("division by zero, at %1%") % pos);
-    mkInt(v, state.forceInt(*args[0], pos) / i2);
+    NixFloat f2 = state.forceFloat(*args[1], pos);
+    if (f2 == 0) throw EvalError(format("division by zero, at %1%") % pos);
+
+    if (args[0]->type == tFloat || args[1]->type == tFloat)
+        mkFloat(v, state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos));
+    else
+        mkInt(v, state.forceInt(*args[0], pos) / state.forceInt(*args[1], pos));
 }
 
 
@@ -1678,7 +1703,7 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
     } else
         url = state.forceStringNoCtx(*args[0], pos);
 
-    Path res = downloadFileCached(url, unpack);
+    Path res = downloadFileCached(state.store, url, unpack);
     mkString(v, res, PathSet({res}));
 }
 
@@ -1736,7 +1761,7 @@ void EvalState::createBaseEnv()
        language feature gets added.  It's not necessary to increase it
        when primops get added, because you can just use `builtins ?
        primOp' to check. */
-    mkInt(v, 3);
+    mkInt(v, 4);
     addConstant("__langVersion", v);
 
     // Miscellaneous
@@ -1753,6 +1778,7 @@ void EvalState::createBaseEnv()
     addPrimOp("__isFunction", 1, prim_isFunction);
     addPrimOp("__isString", 1, prim_isString);
     addPrimOp("__isInt", 1, prim_isInt);
+    addPrimOp("__isFloat", 1, prim_isFloat);
     addPrimOp("__isBool", 1, prim_isBool);
     addPrimOp("__genericClosure", 1, prim_genericClosure);
     addPrimOp("abort", 1, prim_abort);
diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc
index b0cf85e21f18..47ee324a6e4f 100644
--- a/src/libexpr/value-to-json.cc
+++ b/src/libexpr/value-to-json.cc
@@ -84,6 +84,10 @@ void printValueAsJSON(EvalState & state, bool strict,
             v.external->printValueAsJSON(state, strict, str, context);
             break;
 
+        case tFloat:
+            str << v.fpoint;
+            break;
+
         default:
             throw TypeError(format("cannot convert %1% to JSON") % showType(v));
     }
diff --git a/src/libexpr/value-to-json.hh b/src/libexpr/value-to-json.hh
index f6796f2053e9..c59caf5641bc 100644
--- a/src/libexpr/value-to-json.hh
+++ b/src/libexpr/value-to-json.hh
@@ -36,7 +36,18 @@ struct JSONObject
         attr(s);
         escapeJSON(str, t);
     }
-    void attr(const string & s, int n)
+    void attr(const string & s, const char * t)
+    {
+        attr(s);
+        escapeJSON(str, t);
+    }
+    void attr(const string & s, bool b)
+    {
+        attr(s);
+        str << (b ? "true" : "false");
+    }
+    template<typename T>
+    void attr(const string & s, const T & n)
     {
         attr(s);
         str << n;
diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc
index cb52ce1e67bd..00b1918a82aa 100644
--- a/src/libexpr/value-to-xml.cc
+++ b/src/libexpr/value-to-xml.cc
@@ -148,6 +148,10 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
             v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen);
             break;
 
+        case tFloat:
+            doc.writeEmptyElement("float", singletonAttrs("value", (format("%1%") % v.fpoint).str()));
+            break;
+
         default:
             doc.writeEmptyElement("unevaluated");
     }
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh
index e6d1502cb607..62bdd9281f08 100644
--- a/src/libexpr/value.hh
+++ b/src/libexpr/value.hh
@@ -22,6 +22,7 @@ typedef enum {
     tPrimOp,
     tPrimOpApp,
     tExternal,
+    tFloat
 } ValueType;
 
 
@@ -38,6 +39,7 @@ class XMLWriter;
 
 
 typedef long NixInt;
+typedef float NixFloat;
 
 /* External values must descend from ExternalValueBase, so that
  * type-agnostic nix functions (e.g. showType) can be implemented
@@ -141,6 +143,7 @@ struct Value
             Value * left, * right;
         } primOpApp;
         ExternalValueBase * external;
+        NixFloat fpoint;
     };
 
     bool isList() const
@@ -181,6 +184,14 @@ static inline void mkInt(Value & v, NixInt n)
 }
 
 
+static inline void mkFloat(Value & v, NixFloat n)
+{
+    clearValue(v);
+    v.type = tFloat;
+    v.fpoint = n;
+}
+
+
 static inline void mkBool(Value & v, bool b)
 {
     clearValue(v);
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index a6e8f352a0ab..88ed52497fb9 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -4,7 +4,6 @@
 #include "globals.hh"
 #include "store-api.hh"
 #include "util.hh"
-#include "misc.hh"
 
 #include <iostream>
 #include <cctype>
@@ -47,22 +46,22 @@ void printGCWarning()
 }
 
 
-void printMissing(StoreAPI & store, const PathSet & paths)
+void printMissing(ref<Store> store, const PathSet & paths)
 {
     unsigned long long downloadSize, narSize;
     PathSet willBuild, willSubstitute, unknown;
-    queryMissing(store, paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
-    printMissing(willBuild, willSubstitute, unknown, downloadSize, narSize);
+    store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
+    printMissing(store, willBuild, willSubstitute, unknown, downloadSize, narSize);
 }
 
 
-void printMissing(const PathSet & willBuild,
+void printMissing(ref<Store> store, const PathSet & willBuild,
     const PathSet & willSubstitute, const PathSet & unknown,
     unsigned long long downloadSize, unsigned long long narSize)
 {
     if (!willBuild.empty()) {
         printMsg(lvlInfo, format("these derivations will be built:"));
-        Paths sorted = topoSortPaths(*store, willBuild);
+        Paths sorted = store->topoSortPaths(willBuild);
         reverse(sorted.begin(), sorted.end());
         for (auto & i : sorted)
             printMsg(lvlInfo, format("  %1%") % i);
diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh
index 65b288e1ff3e..3f3f6f7232e0 100644
--- a/src/libmain/shared.hh
+++ b/src/libmain/shared.hh
@@ -19,8 +19,6 @@ public:
     Exit(int status) : status(status) { }
 };
 
-class StoreAPI;
-
 int handleExceptions(const string & programName, std::function<void()> fun);
 
 void initNix();
@@ -33,9 +31,11 @@ void printVersion(const string & programName);
 /* Ugh.  No better place to put this. */
 void printGCWarning();
 
-void printMissing(StoreAPI & store, const PathSet & paths);
+class Store;
+
+void printMissing(ref<Store> store, const PathSet & paths);
 
-void printMissing(const PathSet & willBuild,
+void printMissing(ref<Store> store, const PathSet & willBuild,
     const PathSet & willSubstitute, const PathSet & unknown,
     unsigned long long downloadSize, unsigned long long narSize);
 
@@ -66,6 +66,7 @@ template<class N> N getIntArg(const string & opt,
     return n * multiplier;
 }
 
+
 /* Show the manual page for the specified program. */
 void showManPage(const string & name);
 
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 1c751ab98734..249ab2335bdf 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -2,7 +2,6 @@
 
 #include "references.hh"
 #include "pathlocks.hh"
-#include "misc.hh"
 #include "globals.hh"
 #include "local-store.hh"
 #include "util.hh"
@@ -34,47 +33,27 @@
 
 #include <bzlib.h>
 
-/* Includes required for chroot support. */
-#if HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-#if HAVE_SYS_MOUNT_H
-#include <sys/mount.h>
-#endif
-#if HAVE_SYS_SYSCALL_H
-#include <sys/syscall.h>
-#endif
-#if HAVE_SCHED_H
-#include <sched.h>
-#endif
-
-/* In GNU libc 2.11, <sys/mount.h> does not define `MS_PRIVATE', but
-   <linux/fs.h> does.  */
-#if !defined MS_PRIVATE && defined HAVE_LINUX_FS_H
-#include <linux/fs.h>
-#endif
-
-#define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS) && defined(SYS_pivot_root)
-
 /* chroot-like behavior from Apple's sandbox */
 #if __APPLE__
-    #define SANDBOX_ENABLED 1
     #define DEFAULT_ALLOWED_IMPURE_PREFIXES "/System/Library /usr/lib /dev /bin/sh"
 #else
-    #define SANDBOX_ENABLED 0
     #define DEFAULT_ALLOWED_IMPURE_PREFIXES ""
 #endif
 
-#if CHROOT_ENABLED
+/* Includes required for chroot support. */
+#if __linux__
 #include <sys/socket.h>
 #include <sys/ioctl.h>
 #include <net/if.h>
 #include <netinet/ip.h>
-#endif
-
-#if __linux__
 #include <sys/personality.h>
 #include <sys/mman.h>
+#include <sched.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/syscall.h>
+#include <linux/fs.h>
+#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
 #endif
 
 #if HAVE_STATVFS
@@ -641,11 +620,15 @@ HookInstance::HookInstance()
         if (dup2(builderOut.writeSide, 4) == -1)
             throw SysError("dupping builder's stdout/stderr");
 
-        execl(buildHook.c_str(), buildHook.c_str(), settings.thisSystem.c_str(),
-            (format("%1%") % settings.maxSilentTime).str().c_str(),
-            (format("%1%") % settings.printBuildTrace).str().c_str(),
-            (format("%1%") % settings.buildTimeout).str().c_str(),
-            NULL);
+        Strings args = {
+            baseNameOf(buildHook),
+            settings.thisSystem,
+            (format("%1%") % settings.maxSilentTime).str(),
+            (format("%1%") % settings.printBuildTrace).str(),
+            (format("%1%") % settings.buildTimeout).str()
+        };
+
+        execv(buildHook.c_str(), stringsToCharPtrs(args).data());
 
         throw SysError(format("executing ‘%1%’") % buildHook);
     });
@@ -781,10 +764,10 @@ private:
     DirsInChroot dirsInChroot;
     typedef map<string, string> Environment;
     Environment env;
-#if SANDBOX_ENABLED
+
+#if __APPLE__
     typedef string SandboxProfile;
     SandboxProfile additionalSandboxProfile;
-
     AutoDelete autoDelSandbox;
 #endif
 
@@ -922,7 +905,7 @@ DerivationGoal::DerivationGoal(const Path & drvPath, const BasicDerivation & drv
 {
     this->drv = std::unique_ptr<BasicDerivation>(new BasicDerivation(drv));
     state = &DerivationGoal::haveDerivation;
-    name = (format("building of %1%") % showPaths(outputPaths(drv))).str();
+    name = (format("building of %1%") % showPaths(drv.outputPaths())).str();
     trace("created");
 
     /* Prevent the .chroot directory from being
@@ -1034,7 +1017,7 @@ void DerivationGoal::loadDerivation()
     assert(worker.store.isValidPath(drvPath));
 
     /* Get the derivation. */
-    drv = std::unique_ptr<BasicDerivation>(new Derivation(derivationFromPath(worker.store, drvPath)));
+    drv = std::unique_ptr<BasicDerivation>(new Derivation(worker.store.derivationFromPath(drvPath)));
 
     haveDerivation();
 }
@@ -1061,10 +1044,19 @@ void DerivationGoal::haveDerivation()
     for (auto & i : invalidOutputs)
         if (pathFailed(i)) return;
 
+    /* Reject doing a hash build of anything other than a fixed-output
+       derivation. */
+    if (buildMode == bmHash) {
+        if (drv->outputs.size() != 1 ||
+            drv->outputs.find("out") == drv->outputs.end() ||
+            drv->outputs["out"].hashAlgo == "")
+            throw Error(format("cannot do a hash build of non-fixed-output derivation ‘%1%’") % drvPath);
+    }
+
     /* We are first going to try to create the invalid output paths
        through substitutes.  If that doesn't work, we'll build
        them. */
-    if (settings.useSubstitutes && substitutesAllowed(*drv))
+    if (settings.useSubstitutes && drv->substitutesAllowed())
         for (auto & i : invalidOutputs)
             addWaitee(worker.makeSubstitutionGoal(i, buildMode == bmRepair));
 
@@ -1143,8 +1135,10 @@ void DerivationGoal::repairClosure()
 
     /* Get the output closure. */
     PathSet outputClosure;
-    for (auto & i : drv->outputs)
-        computeFSClosure(worker.store, i.second.path, outputClosure);
+    for (auto & i : drv->outputs) {
+        if (!wantOutput(i.first, wantedOutputs)) continue;
+        worker.store.computeFSClosure(i.second.path, outputClosure);
+    }
 
     /* Filter out our own outputs (which we have already checked). */
     for (auto & i : drv->outputs)
@@ -1154,11 +1148,11 @@ void DerivationGoal::repairClosure()
        derivation is responsible for which path in the output
        closure. */
     PathSet inputClosure;
-    if (useDerivation) computeFSClosure(worker.store, drvPath, inputClosure);
+    if (useDerivation) worker.store.computeFSClosure(drvPath, inputClosure);
     std::map<Path, Path> outputsToDrv;
     for (auto & i : inputClosure)
         if (isDerivation(i)) {
-            Derivation drv = derivationFromPath(worker.store, i);
+            Derivation drv = worker.store.derivationFromPath(i);
             for (auto & j : drv.outputs)
                 outputsToDrv[j.second.path] = i;
         }
@@ -1230,10 +1224,10 @@ void DerivationGoal::inputsRealised()
                `i' as input paths.  Only add the closures of output paths
                that are specified as inputs. */
             assert(worker.store.isValidPath(i.first));
-            Derivation inDrv = derivationFromPath(worker.store, i.first);
+            Derivation inDrv = worker.store.derivationFromPath(i.first);
             for (auto & j : i.second)
                 if (inDrv.outputs.find(j) != inDrv.outputs.end())
-                    computeFSClosure(worker.store, inDrv.outputs[j].path, inputPaths);
+                    worker.store.computeFSClosure(inDrv.outputs[j].path, inputPaths);
                 else
                     throw Error(
                         format("derivation ‘%1%’ requires non-existent output ‘%2%’ from input derivation ‘%3%’")
@@ -1242,7 +1236,7 @@ void DerivationGoal::inputsRealised()
 
     /* Second, the input sources. */
     for (auto & i : drv->inputSrcs)
-        computeFSClosure(worker.store, i, inputPaths);
+        worker.store.computeFSClosure(i, inputPaths);
 
     debug(format("added input paths %1%") % showPaths(inputPaths));
 
@@ -1265,46 +1259,6 @@ void DerivationGoal::inputsRealised()
 }
 
 
-static bool isBuiltin(const BasicDerivation & drv)
-{
-    return string(drv.builder, 0, 8) == "builtin:";
-}
-
-
-static bool canBuildLocally(const BasicDerivation & drv)
-{
-    return drv.platform == settings.thisSystem
-        || isBuiltin(drv)
-#if __linux__
-        || (drv.platform == "i686-linux" && settings.thisSystem == "x86_64-linux")
-        || (drv.platform == "armv6l-linux" && settings.thisSystem == "armv7l-linux")
-#elif __FreeBSD__
-        || (drv.platform == "i686-linux" && settings.thisSystem == "x86_64-freebsd")
-        || (drv.platform == "i686-linux" && settings.thisSystem == "i686-freebsd")
-#endif
-        ;
-}
-
-
-static string get(const StringPairs & map, const string & key, const string & def = "")
-{
-    StringPairs::const_iterator i = map.find(key);
-    return i == map.end() ? def : i->second;
-}
-
-
-bool willBuildLocally(const BasicDerivation & drv)
-{
-    return get(drv.env, "preferLocalBuild") == "1" && canBuildLocally(drv);
-}
-
-
-bool substitutesAllowed(const BasicDerivation & drv)
-{
-    return get(drv.env, "allowSubstitutes", "1") == "1";
-}
-
-
 void DerivationGoal::tryToBuild()
 {
     trace("trying to build");
@@ -1327,7 +1281,7 @@ void DerivationGoal::tryToBuild()
        can't acquire the lock, then continue; hopefully some other
        goal can start a build, and if not, the main loop will sleep a
        few seconds and then retry this goal. */
-    if (!outputLocks.lockPaths(outputPaths(*drv), "", false)) {
+    if (!outputLocks.lockPaths(drv->outputPaths(), "", false)) {
         worker.waitForAWhile(shared_from_this());
         return;
     }
@@ -1340,7 +1294,6 @@ void DerivationGoal::tryToBuild()
        now hold the locks on the output paths, no other process can
        build this derivation, so no further checks are necessary. */
     validPaths = checkPathValidity(true, buildMode == bmRepair);
-    assert(buildMode != bmCheck || validPaths.size() == drv->outputs.size());
     if (buildMode != bmCheck && validPaths.size() == drv->outputs.size()) {
         debug(format("skipping build of derivation ‘%1%’, someone beat us to it") % drvPath);
         outputLocks.setDeletion(true);
@@ -1348,7 +1301,7 @@ void DerivationGoal::tryToBuild()
         return;
     }
 
-    missingPaths = outputPaths(*drv);
+    missingPaths = drv->outputPaths();
     if (buildMode != bmCheck)
         for (auto & i : validPaths) missingPaths.erase(i);
 
@@ -1371,7 +1324,7 @@ void DerivationGoal::tryToBuild()
     /* Don't do a remote build if the derivation has the attribute
        `preferLocalBuild' set.  Also, check and repair modes are only
        supported for local builds. */
-    bool buildLocally = buildMode != bmNormal || willBuildLocally(*drv);
+    bool buildLocally = buildMode != bmNormal || drv->willBuildLocally();
 
     /* Is the build hook willing to accept this job? */
     if (!buildLocally) {
@@ -1667,7 +1620,7 @@ HookReply DerivationGoal::tryBuildHook()
        list it since the remote system *probably* already has it.) */
     PathSet allInputs;
     allInputs.insert(inputPaths.begin(), inputPaths.end());
-    computeFSClosure(worker.store, drvPath, allInputs);
+    worker.store.computeFSClosure(drvPath, allInputs);
 
     string s;
     for (auto & i : allInputs) { s += i; s += ' '; }
@@ -1722,7 +1675,7 @@ void DerivationGoal::startBuilder()
     startNest(nest, lvlInfo, f % showPaths(missingPaths) % curRound % nrRounds);
 
     /* Right platform? */
-    if (!canBuildLocally(*drv)) {
+    if (!drv->canBuildLocally()) {
         if (settings.printBuildTrace)
             printMsg(lvlError, format("@ unsupported-platform %1% %2%") % drvPath % drv->platform);
         throw Error(
@@ -1730,6 +1683,10 @@ void DerivationGoal::startBuilder()
             % drv->platform % settings.thisSystem % drvPath);
     }
 
+#if __APPLE__
+    additionalSandboxProfile = get(drv->env, "__sandboxProfile");
+#endif
+
     /* Are we doing a chroot build?  Note that fixed-output
        derivations are never done in a chroot, mainly so that
        functions like fetchurl (which needs a proper /etc/resolv.conf)
@@ -1743,7 +1700,13 @@ void DerivationGoal::startBuilder()
             throw Error("option ‘build-use-sandbox’ must be set to one of ‘true’, ‘false’ or ‘relaxed’");
         if (x == "true") {
             if (get(drv->env, "__noChroot") == "1")
-                throw Error(format("derivation ‘%1%’ has ‘__noChroot’ set, but that's not allowed when ‘build-use-sandbox’ is ‘true’") % drvPath);
+                throw Error(format("derivation ‘%1%’ has ‘__noChroot’ set, "
+                    "but that's not allowed when ‘build-use-sandbox’ is ‘true’") % drvPath);
+#if __APPLE__
+            if (additionalSandboxProfile != "")
+                throw Error(format("derivation ‘%1%’ specifies a sandbox profile, "
+                    "but this is only allowed when ‘build-use-sandbox’ is ‘relaxed’") % drvPath);
+#endif
             useChroot = true;
         }
         else if (x == "false")
@@ -1785,7 +1748,7 @@ void DerivationGoal::startBuilder()
 
     /* In a sandbox, for determinism, always use the same temporary
        directory. */
-    tmpDirInSandbox = useChroot ? "/tmp/nix-build-" + drvName + "-0" : tmpDir;
+    tmpDirInSandbox = useChroot ? canonPath("/tmp", true) + "/nix-build-" + drvName + "-0" : tmpDir;
 
     /* Add all bindings specified in the derivation via the
        environments, except those listed in the passAsFile
@@ -1798,10 +1761,11 @@ void DerivationGoal::startBuilder()
         if (passAsFile.find(i.first) == passAsFile.end()) {
             env[i.first] = i.second;
         } else {
-            Path p = tmpDir + "/.attr-" + std::to_string(fileNr++);
+            string fn = ".attr-" + std::to_string(fileNr++);
+            Path p = tmpDir + "/" + fn;
             writeFile(p, i.second);
             filesToChown.insert(p);
-            env[i.first + "Path"] = p;
+            env[i.first + "Path"] = tmpDirInSandbox + "/" + fn;
         }
     }
 
@@ -1868,14 +1832,14 @@ void DerivationGoal::startBuilder()
            like passing all build-time dependencies of some path to a
            derivation that builds a NixOS DVD image. */
         PathSet paths, paths2;
-        computeFSClosure(worker.store, storePath, paths);
+        worker.store.computeFSClosure(storePath, paths);
         paths2 = paths;
 
         for (auto & j : paths2) {
             if (isDerivation(j)) {
-                Derivation drv = derivationFromPath(worker.store, j);
+                Derivation drv = worker.store.derivationFromPath(j);
                 for (auto & k : drv.outputs)
-                    computeFSClosure(worker.store, k.second.path, paths);
+                    worker.store.computeFSClosure(k.second.path, paths);
             }
         }
 
@@ -1908,7 +1872,7 @@ void DerivationGoal::startBuilder()
     if (useChroot) {
 
         string defaultChrootDirs;
-#if CHROOT_ENABLED
+#if __linux__
         if (isInStore(BASH_PATH))
             defaultChrootDirs = "/bin/sh=" BASH_PATH;
 #endif
@@ -1939,13 +1903,10 @@ void DerivationGoal::startBuilder()
         PathSet closure;
         for (auto & i : dirsInChroot)
             if (isInStore(i.second))
-                computeFSClosure(worker.store, toStorePath(i.second), closure);
+                worker.store.computeFSClosure(toStorePath(i.second), closure);
         for (auto & i : closure)
             dirsInChroot[i] = i;
 
-#if SANDBOX_ENABLED
-        additionalSandboxProfile = get(drv->env, "__sandboxProfile");
-#endif
         string allowed = settings.get("allowed-impure-host-deps", string(DEFAULT_ALLOWED_IMPURE_PREFIXES));
         PathSet allowedPaths = tokenizeString<StringSet>(allowed);
 
@@ -1967,12 +1928,12 @@ void DerivationGoal::startBuilder()
                 }
             }
             if (!found)
-                throw Error(format("derivation '%1%' requested impure path ‘%2%’, but it was not in allowed-impure-host-deps (‘%3%’)") % drvPath % i % allowed);
+                throw Error(format("derivation ‘%1%’ requested impure path ‘%2%’, but it was not in allowed-impure-host-deps (‘%3%’)") % drvPath % i % allowed);
 
             dirsInChroot[i] = i;
         }
 
-#if CHROOT_ENABLED
+#if __linux__
         /* Create a temporary directory in which we set up the chroot
            environment using bind-mounts.  We put it in the Nix store
            to ensure that we can create hard-links to non-directory
@@ -2065,7 +2026,7 @@ void DerivationGoal::startBuilder()
         for (auto & i : drv->outputs)
             dirsInChroot.erase(i.second.path);
 
-#elif SANDBOX_ENABLED
+#elif __APPLE__
         /* We don't really have any parent prep work to do (yet?)
            All work happens in the child, instead. */
 #else
@@ -2148,7 +2109,7 @@ void DerivationGoal::startBuilder()
     builderOut.create();
 
     /* Fork a child to build the package. */
-#if CHROOT_ENABLED
+#if __linux__
     if (useChroot) {
         /* Set up private namespaces for the build:
 
@@ -2189,7 +2150,7 @@ void DerivationGoal::startBuilder()
             size_t stackSize = 1 * 1024 * 1024;
             char * stack = (char *) mmap(0, stackSize,
                 PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
-            if (!stack) throw SysError("allocating stack");
+            if (stack == MAP_FAILED) throw SysError("allocating stack");
             int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD;
             if (!fixedOutput) flags |= CLONE_NEWNET;
             pid_t child = clone(childEntry, stack + stackSize, flags, this);
@@ -2210,7 +2171,7 @@ void DerivationGoal::startBuilder()
 #endif
     {
         ProcessOptions options;
-        options.allowVfork = !buildUser.enabled() && !isBuiltin(*drv);
+        options.allowVfork = !buildUser.enabled() && !drv->isBuiltin();
         pid = startProcess([&]() {
             runChild();
         }, options);
@@ -2250,7 +2211,7 @@ void DerivationGoal::runChild()
 
         commonChildInit(builderOut);
 
-#if CHROOT_ENABLED
+#if __linux__
         if (useChroot) {
 
             /* Initialise the loopback interface. */
@@ -2383,10 +2344,8 @@ void DerivationGoal::runChild()
             if (mkdir("real-root", 0) == -1)
                 throw SysError("cannot create real-root directory");
 
-#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
             if (pivot_root(".", "real-root") == -1)
                 throw SysError(format("cannot pivot old root directory onto ‘%1%’") % (chrootRootDir + "/real-root"));
-#undef pivot_root
 
             if (chroot(".") == -1)
                 throw SysError(format("cannot change root directory to ‘%1%’") % chrootRootDir);
@@ -2466,9 +2425,9 @@ void DerivationGoal::runChild()
         const char *builder = "invalid";
 
         string sandboxProfile;
-        if (isBuiltin(*drv)) {
+        if (drv->isBuiltin()) {
             ;
-#if SANDBOX_ENABLED
+#if __APPLE__
         } else if (useChroot) {
             /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
             PathSet ancestry;
@@ -2527,7 +2486,9 @@ void DerivationGoal::runChild()
             sandboxProfile += "(allow file-read* file-write* process-exec\n";
             for (auto & i : dirsInChroot) {
                 if (i.first != i.second)
-                    throw SysError(format("can't map '%1%' to '%2%': mismatched impure paths not supported on darwin"));
+                    throw Error(format(
+                        "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin")
+                        % i.first % i.second);
 
                 string path = i.first;
                 struct stat st;
@@ -2581,7 +2542,7 @@ void DerivationGoal::runChild()
         writeFull(STDERR_FILENO, string("\1\n"));
 
         /* Execute the program.  This should not return. */
-        if (isBuiltin(*drv)) {
+        if (drv->isBuiltin()) {
             try {
                 logType = ltFlat;
                 if (drv->builder == "builtin:fetchurl")
@@ -2644,6 +2605,8 @@ void DerivationGoal::registerOutputs()
        outputs to allow hard links between outputs. */
     InodesSeen inodesSeen;
 
+    Path checkSuffix = "-check";
+
     /* Check whether the output paths were created, and grep each
        output path to determine what other paths it references.  Also make all
        output paths read-only. */
@@ -2669,7 +2632,7 @@ void DerivationGoal::registerOutputs()
                 && redirectedBadOutputs.find(path) != redirectedBadOutputs.end()
                 && pathExists(redirected))
                 replaceValidPath(path, redirected);
-            if (buildMode == bmCheck)
+            if (buildMode == bmCheck && redirected != "")
                 actualPath = redirected;
         }
 
@@ -2732,12 +2695,29 @@ void DerivationGoal::registerOutputs()
                         format("output path ‘%1%’ should be a non-executable regular file") % path);
             }
 
-            /* Check the hash. */
+            /* Check the hash. In hash mode, move the path produced by
+               the derivation to its content-addressed location. */
             Hash h2 = recursive ? hashPath(ht, actualPath).first : hashFile(ht, actualPath);
-            if (h != h2)
-                throw BuildError(
-                    format("Nix expects output path ‘%1%’ to have %2% hash ‘%3%’, instead it has ‘%4%’")
-                    % path % i.second.hashAlgo % printHash16or32(h) % printHash16or32(h2));
+            if (buildMode == bmHash) {
+                Path dest = makeFixedOutputPath(recursive, ht, h2, drv->env["name"]);
+                printMsg(lvlError, format("build produced path ‘%1%’ with %2% hash ‘%3%’")
+                    % dest % printHashType(ht) % printHash16or32(h2));
+                if (worker.store.isValidPath(dest))
+                    return;
+                if (actualPath != dest) {
+                    PathLocks outputLocks({dest});
+                    if (pathExists(dest))
+                        deletePath(dest);
+                    if (rename(actualPath.c_str(), dest.c_str()) == -1)
+                        throw SysError(format("moving ‘%1%’ to ‘%2%’") % actualPath % dest);
+                }
+                path = actualPath = dest;
+            } else {
+                if (h != h2)
+                    throw BuildError(
+                        format("output path ‘%1%’ has %2% hash ‘%3%’ when ‘%4%’ was expected")
+                        % path % i.second.hashAlgo % printHash16or32(h2) % printHash16or32(h));
+            }
         }
 
         /* Get rid of all weird permissions.  This also checks that
@@ -2753,9 +2733,20 @@ void DerivationGoal::registerOutputs()
         PathSet references = scanForReferences(actualPath, allPaths, hash);
 
         if (buildMode == bmCheck) {
+            if (!worker.store.isValidPath(path)) continue;
             ValidPathInfo info = worker.store.queryPathInfo(path);
-            if (hash.first != info.hash)
-                throw Error(format("derivation ‘%1%’ may not be deterministic: hash mismatch in output ‘%2%’") % drvPath % path);
+            if (hash.first != info.narHash) {
+                if (settings.keepFailed) {
+                    Path dst = path + checkSuffix;
+                    if (pathExists(dst)) deletePath(dst);
+                    if (rename(actualPath.c_str(), dst.c_str()))
+                        throw SysError(format("renaming ‘%1%’ to ‘%2%’") % actualPath % dst);
+                    throw Error(format("derivation ‘%1%’ may not be deterministic: output ‘%2%’ differs from ‘%3%’")
+                        % drvPath % path % dst);
+                } else
+                    throw Error(format("derivation ‘%1%’ may not be deterministic: output ‘%2%’ differs")
+                        % drvPath % path);
+            }
             continue;
         }
 
@@ -2781,7 +2772,7 @@ void DerivationGoal::registerOutputs()
                 for (auto & i : references)
                     /* Don't call computeFSClosure on ourselves. */
                     if (actualPath != i)
-                        computeFSClosure(worker.store, i, used);
+                        worker.store.computeFSClosure(i, used);
             } else
                 used = references;
 
@@ -2800,13 +2791,15 @@ void DerivationGoal::registerOutputs()
         checkRefs("disallowedReferences", false, false);
         checkRefs("disallowedRequisites", false, true);
 
-        worker.store.optimisePath(path); // FIXME: combine with scanForReferences()
+        if (curRound == nrRounds) {
+            worker.store.optimisePath(path); // FIXME: combine with scanForReferences()
 
-        worker.store.markContentsGood(path);
+            worker.store.markContentsGood(path);
+        }
 
         ValidPathInfo info;
         info.path = path;
-        info.hash = hash.first;
+        info.narHash = hash.first;
         info.narSize = hash.second;
         info.references = references;
         info.deriver = drvPath;
@@ -2815,10 +2808,37 @@ void DerivationGoal::registerOutputs()
 
     if (buildMode == bmCheck) return;
 
-    if (curRound > 1 && prevInfos != infos)
-        throw NotDeterministic(
-            format("result of ‘%1%’ differs from previous round; rejecting as non-deterministic")
-            % drvPath);
+    /* Compare the result with the previous round, and report which
+       path is different, if any.*/
+    if (curRound > 1 && prevInfos != infos) {
+        assert(prevInfos.size() == infos.size());
+        for (auto i = prevInfos.begin(), j = infos.begin(); i != prevInfos.end(); ++i, ++j)
+            if (!(*i == *j)) {
+                Path prev = i->path + checkSuffix;
+                if (pathExists(prev))
+                    throw NotDeterministic(
+                        format("output ‘%1%’ of ‘%2%’ differs from ‘%3%’ from previous round")
+                        % i->path % drvPath % prev);
+                else
+                    throw NotDeterministic(
+                        format("output ‘%1%’ of ‘%2%’ differs from previous round")
+                        % i->path % drvPath);
+            }
+        assert(false); // shouldn't happen
+    }
+
+    if (settings.keepFailed) {
+        for (auto & i : drv->outputs) {
+            Path prev = i.second.path + checkSuffix;
+            if (pathExists(prev)) deletePath(prev);
+            if (curRound < nrRounds) {
+                Path dst = i.second.path + checkSuffix;
+                if (rename(i.second.path.c_str(), dst.c_str()))
+                    throw SysError(format("renaming ‘%1%’ to ‘%2%’") % i.second.path % dst);
+            }
+        }
+
+    }
 
     if (curRound < nrRounds) {
         prevInfos = infos;
@@ -3141,6 +3161,7 @@ void SubstitutionGoal::tryNext()
         /* None left.  Terminate this goal and let someone else deal
            with it. */
         debug(format("path ‘%1%’ is required, but there is no substituter that can build it") % storePath);
+
         /* Hack: don't indicate failure if there were no substituters.
            In that case the calling derivation should just do a
            build. */
@@ -3348,7 +3369,7 @@ void SubstitutionGoal::finished()
 
     ValidPathInfo info2;
     info2.path = storePath;
-    info2.hash = hash.first;
+    info2.narHash = hash.first;
     info2.narSize = hash.second;
     info2.references = info.references;
     info2.deriver = info.deriver;
@@ -3740,7 +3761,7 @@ void Worker::waitForInput()
         }
     }
 
-    if (!waitingForAWhile.empty() && lastWokenUp + settings.pollInterval <= after) {
+    if (!waitingForAWhile.empty() && lastWokenUp + (time_t) settings.pollInterval <= after) {
         lastWokenUp = after;
         for (auto & i : waitingForAWhile) {
             GoalPtr goal = i.lock();
@@ -3836,8 +3857,17 @@ void LocalStore::repairPath(const Path & path)
 
     worker.run(goals);
 
-    if (goal->getExitCode() != Goal::ecSuccess)
-        throw Error(format("cannot repair path ‘%1%’") % path, worker.exitStatus());
+    if (goal->getExitCode() != Goal::ecSuccess) {
+        /* Since substituting the path didn't work, if we have a valid
+           deriver, then rebuild the deriver. */
+        Path deriver = queryDeriver(path);
+        if (deriver != "" && isValidPath(deriver)) {
+            goals.clear();
+            goals.insert(worker.makeDerivationGoal(deriver, StringSet(), bmRepair));
+            worker.run(goals);
+        } else
+            throw Error(format("cannot repair path ‘%1%’") % path, worker.exitStatus());
+    }
 }
 
 
diff --git a/src/libstore/crypto.cc b/src/libstore/crypto.cc
new file mode 100644
index 000000000000..c1b57e51d9b4
--- /dev/null
+++ b/src/libstore/crypto.cc
@@ -0,0 +1,88 @@
+#include "crypto.hh"
+#include "util.hh"
+
+#if HAVE_SODIUM
+#include <sodium.h>
+#endif
+
+namespace nix {
+
+static std::pair<std::string, std::string> split(const string & s)
+{
+    size_t colon = s.find(':');
+    if (colon == std::string::npos || colon == 0)
+        return {"", ""};
+    return {std::string(s, 0, colon), std::string(s, colon + 1)};
+}
+
+Key::Key(const string & s)
+{
+    auto ss = split(s);
+
+    name = ss.first;
+    key = ss.second;
+
+    if (name == "" || key == "")
+        throw Error("secret key is corrupt");
+
+    key = base64Decode(key);
+}
+
+SecretKey::SecretKey(const string & s)
+    : Key(s)
+{
+#if HAVE_SODIUM
+    if (key.size() != crypto_sign_SECRETKEYBYTES)
+        throw Error("secret key is not valid");
+#endif
+}
+
+[[noreturn]] static void noSodium()
+{
+    throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
+}
+
+std::string SecretKey::signDetached(const std::string & data) const
+{
+#if HAVE_SODIUM
+    unsigned char sig[crypto_sign_BYTES];
+    unsigned long long sigLen;
+    crypto_sign_detached(sig, &sigLen, (unsigned char *) data.data(), data.size(),
+        (unsigned char *) key.data());
+    return name + ":" + base64Encode(std::string((char *) sig, sigLen));
+#else
+    noSodium();
+#endif
+}
+
+PublicKey::PublicKey(const string & s)
+    : Key(s)
+{
+#if HAVE_SODIUM
+    if (key.size() != crypto_sign_PUBLICKEYBYTES)
+        throw Error("public key is not valid");
+#endif
+}
+
+bool verifyDetached(const std::string & data, const std::string & sig,
+    const PublicKeys & publicKeys)
+{
+#if HAVE_SODIUM
+    auto ss = split(sig);
+
+    auto key = publicKeys.find(ss.first);
+    if (key == publicKeys.end()) return false;
+
+    auto sig2 = base64Decode(ss.second);
+    if (sig2.size() != crypto_sign_BYTES)
+        throw Error("signature is not valid");
+
+    return crypto_sign_verify_detached((unsigned char *) sig2.data(),
+        (unsigned char *) data.data(), data.size(),
+        (unsigned char *) key->second.key.data()) == 0;
+#else
+    noSodium();
+#endif
+}
+
+}
diff --git a/src/libstore/crypto.hh b/src/libstore/crypto.hh
new file mode 100644
index 000000000000..a1489e753649
--- /dev/null
+++ b/src/libstore/crypto.hh
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "types.hh"
+
+#include <map>
+
+namespace nix {
+
+struct Key
+{
+    std::string name;
+    std::string key;
+
+    /* Construct Key from a string in the format
+       ‘<name>:<key-in-base64>’. */
+    Key(const std::string & s);
+
+};
+
+struct SecretKey : Key
+{
+    SecretKey(const std::string & s);
+
+    /* Return a detached signature of the given string. */
+    std::string signDetached(const std::string & s) const;
+};
+
+struct PublicKey : Key
+{
+    PublicKey(const std::string & data);
+};
+
+typedef std::map<std::string, PublicKey> PublicKeys;
+
+/* Return true iff ‘sig’ is a correct signature over ‘data’ using one
+   of the given public keys. */
+bool verifyDetached(const std::string & data, const std::string & sig,
+    const PublicKeys & publicKeys);
+
+}
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index 7959d5bfc3d5..d9b009d40322 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -2,7 +2,6 @@
 #include "store-api.hh"
 #include "globals.hh"
 #include "util.hh"
-#include "misc.hh"
 #include "worker-protocol.hh"
 
 
@@ -27,7 +26,49 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash
 }
 
 
-Path writeDerivation(StoreAPI & store,
+Path BasicDerivation::findOutput(const string & id) const
+{
+    auto i = outputs.find(id);
+    if (i == outputs.end())
+        throw Error(format("derivation has no output ‘%1%’") % id);
+    return i->second.path;
+}
+
+
+bool BasicDerivation::willBuildLocally() const
+{
+    return get(env, "preferLocalBuild") == "1" && canBuildLocally();
+}
+
+
+bool BasicDerivation::substitutesAllowed() const
+{
+    return get(env, "allowSubstitutes", "1") == "1";
+}
+
+
+bool BasicDerivation::isBuiltin() const
+{
+    return string(builder, 0, 8) == "builtin:";
+}
+
+
+bool BasicDerivation::canBuildLocally() const
+{
+    return platform == settings.thisSystem
+        || isBuiltin()
+#if __linux__
+        || (platform == "i686-linux" && settings.thisSystem == "x86_64-linux")
+        || (platform == "armv6l-linux" && settings.thisSystem == "armv7l-linux")
+#elif __FreeBSD__
+        || (platform == "i686-linux" && settings.thisSystem == "x86_64-freebsd")
+        || (platform == "i686-linux" && settings.thisSystem == "i686-freebsd")
+#endif
+        ;
+}
+
+
+Path writeDerivation(ref<Store> store,
     const Derivation & drv, const string & name, bool repair)
 {
     PathSet references;
@@ -38,10 +79,10 @@ Path writeDerivation(StoreAPI & store,
        (that can be missing (of course) and should not necessarily be
        held during a garbage collection). */
     string suffix = name + drvExtension;
-    string contents = unparseDerivation(drv);
+    string contents = drv.unparse();
     return settings.readOnlyMode
         ? computeStorePathForText(suffix, contents, references)
-        : store.addTextToStore(suffix, contents, references, repair);
+        : store->addTextToStore(suffix, contents, references, repair);
 }
 
 
@@ -149,14 +190,14 @@ static void printStrings(string & res, ForwardIterator i, ForwardIterator j)
 }
 
 
-string unparseDerivation(const Derivation & drv)
+string Derivation::unparse() const
 {
     string s;
     s.reserve(65536);
     s += "Derive([";
 
     bool first = true;
-    for (auto & i : drv.outputs) {
+    for (auto & i : outputs) {
         if (first) first = false; else s += ',';
         s += '('; printString(s, i.first);
         s += ','; printString(s, i.second.path);
@@ -167,7 +208,7 @@ string unparseDerivation(const Derivation & drv)
 
     s += "],[";
     first = true;
-    for (auto & i : drv.inputDrvs) {
+    for (auto & i : inputDrvs) {
         if (first) first = false; else s += ',';
         s += '('; printString(s, i.first);
         s += ','; printStrings(s, i.second.begin(), i.second.end());
@@ -175,15 +216,15 @@ string unparseDerivation(const Derivation & drv)
     }
 
     s += "],";
-    printStrings(s, drv.inputSrcs.begin(), drv.inputSrcs.end());
+    printStrings(s, inputSrcs.begin(), inputSrcs.end());
 
-    s += ','; printString(s, drv.platform);
-    s += ','; printString(s, drv.builder);
-    s += ','; printStrings(s, drv.args.begin(), drv.args.end());
+    s += ','; printString(s, platform);
+    s += ','; printString(s, builder);
+    s += ','; printStrings(s, args.begin(), args.end());
 
     s += ",[";
     first = true;
-    for (auto & i : drv.env) {
+    for (auto & i : env) {
         if (first) first = false; else s += ',';
         s += '('; printString(s, i.first);
         s += ','; printString(s, i.second);
@@ -202,11 +243,11 @@ bool isDerivation(const string & fileName)
 }
 
 
-bool isFixedOutputDrv(const Derivation & drv)
+bool BasicDerivation::isFixedOutput() const
 {
-    return drv.outputs.size() == 1 &&
-        drv.outputs.begin()->first == "out" &&
-        drv.outputs.begin()->second.hash != "";
+    return outputs.size() == 1 &&
+        outputs.begin()->first == "out" &&
+        outputs.begin()->second.hash != "";
 }
 
 
@@ -233,10 +274,10 @@ DrvHashes drvHashes;
    paths have been replaced by the result of a recursive call to this
    function, and that for fixed-output derivations we return a hash of
    its output path. */
-Hash hashDerivationModulo(StoreAPI & store, Derivation drv)
+Hash hashDerivationModulo(Store & store, Derivation drv)
 {
     /* Return a fixed hash for fixed-output derivations. */
-    if (isFixedOutputDrv(drv)) {
+    if (drv.isFixedOutput()) {
         DerivationOutputs::const_iterator i = drv.outputs.begin();
         return hashString(htSHA256, "fixed:out:"
             + i->second.hashAlgo + ":"
@@ -259,7 +300,7 @@ Hash hashDerivationModulo(StoreAPI & store, Derivation drv)
     }
     drv.inputDrvs = inputs2;
 
-    return hashString(htSHA256, unparseDerivation(drv));
+    return hashString(htSHA256, drv.unparse());
 }
 
 
@@ -286,10 +327,10 @@ bool wantOutput(const string & output, const std::set<string> & wanted)
 }
 
 
-PathSet outputPaths(const BasicDerivation & drv)
+PathSet BasicDerivation::outputPaths() const
 {
     PathSet paths;
-    for (auto & i : drv.outputs)
+    for (auto & i : outputs)
         paths.insert(i.second.path);
     return paths;
 }
diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh
index f0842045f86b..6f98869b0fe0 100644
--- a/src/libstore/derivations.hh
+++ b/src/libstore/derivations.hh
@@ -50,40 +50,56 @@ struct BasicDerivation
     StringPairs env;
 
     virtual ~BasicDerivation() { };
+
+    /* Return the path corresponding to the output identifier `id' in
+       the given derivation. */
+    Path findOutput(const string & id) const;
+
+    bool willBuildLocally() const;
+
+    bool substitutesAllowed() const;
+
+    bool isBuiltin() const;
+
+    bool canBuildLocally() const;
+
+    /* Return true iff this is a fixed-output derivation. */
+    bool isFixedOutput() const;
+
+    /* Return the output paths of a derivation. */
+    PathSet outputPaths() const;
+
 };
 
 struct Derivation : BasicDerivation
 {
     DerivationInputs inputDrvs; /* inputs that are sub-derivations */
+
+    /* Print a derivation. */
+    std::string unparse() const;
 };
 
 
-class StoreAPI;
+class Store;
 
 
 /* Write a derivation to the Nix store, and return its path. */
-Path writeDerivation(StoreAPI & store,
+Path writeDerivation(ref<Store> store,
     const Derivation & drv, const string & name, bool repair = false);
 
 /* Read a derivation from a file. */
 Derivation readDerivation(const Path & drvPath);
 
-/* Print a derivation. */
-string unparseDerivation(const Derivation & drv);
-
-/* Check whether a file name ends with the extensions for
+/* Check whether a file name ends with the extension for
    derivations. */
 bool isDerivation(const string & fileName);
 
-/* Return true iff this is a fixed-output derivation. */
-bool isFixedOutputDrv(const Derivation & drv);
-
-Hash hashDerivationModulo(StoreAPI & store, Derivation drv);
+Hash hashDerivationModulo(Store & store, Derivation drv);
 
 /* Memoisation of hashDerivationModulo(). */
 typedef std::map<Path, Hash> DrvHashes;
 
-extern DrvHashes drvHashes;
+extern DrvHashes drvHashes; // FIXME: global, not thread-safe
 
 /* Split a string specifying a derivation and a set of outputs
    (/nix/store/hash-foo!out1,out2,...) into the derivation path and
@@ -95,8 +111,6 @@ Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outpu
 
 bool wantOutput(const string & output, const std::set<string> & wanted);
 
-PathSet outputPaths(const BasicDerivation & drv);
-
 struct Source;
 struct Sink;
 
diff --git a/src/libstore/download.cc b/src/libstore/download.cc
index 822e9a8db867..01ce1ea2fd4c 100644
--- a/src/libstore/download.cc
+++ b/src/libstore/download.cc
@@ -188,7 +188,7 @@ DownloadResult downloadFile(string url, const DownloadOptions & options)
 }
 
 
-Path downloadFileCached(const string & url, bool unpack)
+Path downloadFileCached(ref<Store> store, const string & url, bool unpack)
 {
     Path cacheDir = getEnv("XDG_CACHE_HOME", getEnv("HOME", "") + "/.cache") + "/nix/tarballs";
     createDirs(cacheDir);
diff --git a/src/libstore/download.hh b/src/libstore/download.hh
index c1cb25b90c32..7aec8de73e48 100644
--- a/src/libstore/download.hh
+++ b/src/libstore/download.hh
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "types.hh"
+
 #include <string>
 
 namespace nix {
@@ -18,9 +19,11 @@ struct DownloadResult
     string data, etag;
 };
 
+class Store;
+
 DownloadResult downloadFile(string url, const DownloadOptions & options);
 
-Path downloadFileCached(const string & url, bool unpack);
+Path downloadFileCached(ref<Store> store, const string & url, bool unpack);
 
 MakeError(DownloadError, Error)
 
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index 998a7516a13d..d19af1cefaf2 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -1,5 +1,5 @@
+#include "derivations.hh"
 #include "globals.hh"
-#include "misc.hh"
 #include "local-store.hh"
 
 #include <functional>
@@ -83,7 +83,7 @@ void LocalStore::addIndirectRoot(const Path & path)
 }
 
 
-Path addPermRoot(StoreAPI & store, const Path & _storePath,
+Path Store::addPermRoot(const Path & _storePath,
     const Path & _gcRoot, bool indirect, bool allowOutsideRootsDir)
 {
     Path storePath(canonPath(_storePath));
@@ -101,7 +101,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
         if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
             throw Error(format("cannot create symlink ‘%1%’; already exists") % gcRoot);
         makeSymlink(gcRoot, storePath);
-        store.addIndirectRoot(gcRoot);
+        addIndirectRoot(gcRoot);
     }
 
     else {
@@ -127,7 +127,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
        check if the root is in a directory in or linked from the
        gcroots directory. */
     if (settings.checkRootReachability) {
-        Roots roots = store.findRoots();
+        Roots roots = findRoots();
         if (roots.find(gcRoot) == roots.end())
             printMsg(lvlError,
                 format(
@@ -139,7 +139,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
     /* Grab the global GC root, causing us to block while a GC is in
        progress.  This prevents the set of permanent roots from
        increasing while a GC is in progress. */
-    store.syncWithGC();
+    syncWithGC();
 
     return gcRoot;
 }
@@ -260,19 +260,16 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
 }
 
 
-static void foundRoot(StoreAPI & store,
-    const Path & path, const Path & target, Roots & roots)
+void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots)
 {
-    Path storePath = toStorePath(target);
-    if (store.isValidPath(storePath))
-        roots[path] = storePath;
-    else
-        printMsg(lvlInfo, format("skipping invalid root from ‘%1%’ to ‘%2%’") % path % storePath);
-}
-
+    auto foundRoot = [&](const Path & path, const Path & target) {
+        Path storePath = toStorePath(target);
+        if (isValidPath(storePath))
+            roots[path] = storePath;
+        else
+            printMsg(lvlInfo, format("skipping invalid root from ‘%1%’ to ‘%2%’") % path % storePath);
+    };
 
-static void findRoots(StoreAPI & store, const Path & path, unsigned char type, Roots & roots)
-{
     try {
 
         if (type == DT_UNKNOWN)
@@ -280,13 +277,13 @@ static void findRoots(StoreAPI & store, const Path & path, unsigned char type, R
 
         if (type == DT_DIR) {
             for (auto & i : readDirectory(path))
-                findRoots(store, path + "/" + i.name, i.type, roots);
+                findRoots(path + "/" + i.name, i.type, roots);
         }
 
         else if (type == DT_LNK) {
             Path target = readLink(path);
             if (isInStore(target))
-                foundRoot(store, path, target, roots);
+                foundRoot(path, target);
 
             /* Handle indirect roots. */
             else {
@@ -300,14 +297,14 @@ static void findRoots(StoreAPI & store, const Path & path, unsigned char type, R
                     struct stat st2 = lstat(target);
                     if (!S_ISLNK(st2.st_mode)) return;
                     Path target2 = readLink(target);
-                    if (isInStore(target2)) foundRoot(store, target, target2, roots);
+                    if (isInStore(target2)) foundRoot(target, target2);
                 }
             }
         }
 
         else if (type == DT_REG) {
             Path storePath = settings.nixStore + "/" + baseNameOf(path);
-            if (store.isValidPath(storePath))
+            if (isValidPath(storePath))
                 roots[path] = storePath;
         }
 
@@ -328,16 +325,16 @@ Roots LocalStore::findRoots()
     Roots roots;
 
     /* Process direct roots in {gcroots,manifests,profiles}. */
-    nix::findRoots(*this, settings.nixStateDir + "/" + gcRootsDir, DT_UNKNOWN, roots);
+    findRoots(settings.nixStateDir + "/" + gcRootsDir, DT_UNKNOWN, roots);
     if (pathExists(settings.nixStateDir + "/manifests"))
-        nix::findRoots(*this, settings.nixStateDir + "/manifests", DT_UNKNOWN, roots);
-    nix::findRoots(*this, settings.nixStateDir + "/profiles", DT_UNKNOWN, roots);
+        findRoots(settings.nixStateDir + "/manifests", DT_UNKNOWN, roots);
+    findRoots(settings.nixStateDir + "/profiles", DT_UNKNOWN, roots);
 
     return roots;
 }
 
 
-static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
+void LocalStore::findRuntimeRoots(PathSet & roots)
 {
     Path rootFinder = getEnv("NIX_ROOT_FINDER",
         settings.nixLibexecDir + "/nix/find-runtime-roots.pl");
@@ -353,7 +350,7 @@ static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
     for (auto & i : paths)
         if (isInStore(i)) {
             Path path = toStorePath(i);
-            if (roots.find(path) == roots.end() && store.isValidPath(path)) {
+            if (roots.find(path) == roots.end() && isValidPath(path)) {
                 debug(format("got additional root ‘%1%’") % path);
                 roots.insert(path);
             }
@@ -628,7 +625,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
        to add running programs to the set of roots (to prevent them
        from being garbage collected). */
     if (!options.ignoreLiveness)
-        addAdditionalRoots(*this, state.roots);
+        findRuntimeRoots(state.roots);
 
     /* Read the temporary roots.  This acquires read locks on all
        per-process temporary root files.  So after this point no paths
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index 978bca28d7fa..308aebd73bb4 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -23,16 +23,11 @@
 #include <time.h>
 #include <grp.h>
 
-#if HAVE_UNSHARE && HAVE_STATVFS && HAVE_SYS_MOUNT_H
+#if __linux__
 #include <sched.h>
 #include <sys/statvfs.h>
 #include <sys/mount.h>
-#endif
-
-#if HAVE_LINUX_FS_H
-#include <linux/fs.h>
 #include <sys/ioctl.h>
-#include <errno.h>
 #endif
 
 #include <sqlite3.h>
@@ -45,10 +40,7 @@ MakeError(SQLiteError, Error);
 MakeError(SQLiteBusy, SQLiteError);
 
 
-static void throwSQLiteError(sqlite3 * db, const format & f)
-    __attribute__ ((noreturn));
-
-static void throwSQLiteError(sqlite3 * db, const format & f)
+[[noreturn]] static void throwSQLiteError(sqlite3 * db, const format & f)
 {
     int err = sqlite3_errcode(db);
     if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
@@ -402,9 +394,15 @@ int LocalStore::getSchema()
 }
 
 
+bool LocalStore::haveWriteAccess()
+{
+    return access(settings.nixDBPath.c_str(), R_OK | W_OK) == 0;
+}
+
+
 void LocalStore::openDB(bool create)
 {
-    if (access(settings.nixDBPath.c_str(), R_OK | W_OK))
+    if (!haveWriteAccess())
         throw SysError(format("Nix database directory ‘%1%’ is not writable") % settings.nixDBPath);
 
     /* Open the Nix database. */
@@ -502,7 +500,7 @@ void LocalStore::openDB(bool create)
    bind mount.  So make the Nix store writable for this process. */
 void LocalStore::makeStoreWritable()
 {
-#if HAVE_UNSHARE && HAVE_STATVFS && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_REMOUNT)
+#if __linux__
     if (getuid() != 0) return;
     /* Check if /nix/store is on a read-only mount. */
     struct statvfs stat;
@@ -607,10 +605,10 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
        users group); we check for this case below. */
     if (st.st_uid != geteuid()) {
 #if HAVE_LCHOWN
-        if (lchown(path.c_str(), geteuid(), (gid_t) -1) == -1)
+        if (lchown(path.c_str(), geteuid(), getegid()) == -1)
 #else
         if (!S_ISLNK(st.st_mode) &&
-            chown(path.c_str(), geteuid(), (gid_t) -1) == -1)
+            chown(path.c_str(), geteuid(), getegid()) == -1)
 #endif
             throw SysError(format("changing owner of ‘%1%’ to %2%")
                 % path % geteuid());
@@ -654,7 +652,7 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation &
     assert(isDerivation(drvName));
     drvName = string(drvName, 0, drvName.size() - drvExtension.size());
 
-    if (isFixedOutputDrv(drv)) {
+    if (drv.isFixedOutput()) {
         DerivationOutputs::const_iterator out = drv.outputs.find("out");
         if (out == drv.outputs.end())
             throw Error(format("derivation ‘%1%’ does not have an output named ‘out’") % drvPath);
@@ -693,7 +691,7 @@ unsigned long long LocalStore::addValidPath(const ValidPathInfo & info, bool che
 {
     SQLiteStmtUse use(stmtRegisterValidPath);
     stmtRegisterValidPath.bind(info.path);
-    stmtRegisterValidPath.bind("sha256:" + printHash(info.hash));
+    stmtRegisterValidPath.bind("sha256:" + printHash(info.narHash));
     stmtRegisterValidPath.bind(info.registrationTime == 0 ? time(0) : info.registrationTime);
     if (info.deriver != "")
         stmtRegisterValidPath.bind(info.deriver);
@@ -844,7 +842,7 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
 
         const char * s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 1);
         assert(s);
-        info.hash = parseHashField(path, s);
+        info.narHash = parseHashField(path, s);
 
         info.registrationTime = sqlite3_column_int(stmtQueryPathInfo, 2);
 
@@ -882,7 +880,7 @@ void LocalStore::updatePathInfo(const ValidPathInfo & info)
         stmtUpdatePathInfo.bind64(info.narSize);
     else
         stmtUpdatePathInfo.bind(); // null
-    stmtUpdatePathInfo.bind("sha256:" + printHash(info.hash));
+    stmtUpdatePathInfo.bind("sha256:" + printHash(info.narHash));
     stmtUpdatePathInfo.bind(info.path);
     if (sqlite3_step(stmtUpdatePathInfo) != SQLITE_DONE)
         throwSQLiteError(db, format("updating info of path ‘%1%’ in database") % info.path);
@@ -952,14 +950,6 @@ PathSet LocalStore::queryAllValidPaths()
 }
 
 
-void LocalStore::queryReferences(const Path & path,
-    PathSet & references)
-{
-    ValidPathInfo info = queryPathInfo(path);
-    references.insert(info.references.begin(), info.references.end());
-}
-
-
 void LocalStore::queryReferrers_(const Path & path, PathSet & referrers)
 {
     SQLiteStmtUse use(stmtQueryReferrers);
@@ -1063,7 +1053,7 @@ StringSet LocalStore::queryDerivationOutputNames(const Path & path)
 
 Path LocalStore::queryPathFromHashPart(const string & hashPart)
 {
-    if (hashPart.size() != 32) throw Error("invalid hash part");
+    if (hashPart.size() != storePathHashLen) throw Error("invalid hash part");
 
     Path prefix = settings.nixStore + "/" + hashPart;
 
@@ -1281,7 +1271,7 @@ void LocalStore::querySubstitutablePathInfos(const PathSet & paths,
 
 Hash LocalStore::queryPathHash(const Path & path)
 {
-    return queryPathInfo(path).hash;
+    return queryPathInfo(path).narHash;
 }
 
 
@@ -1305,7 +1295,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
         PathSet paths;
 
         for (auto & i : infos) {
-            assert(i.hash.type == htSHA256);
+            assert(i.narHash.type == htSHA256);
             if (isValidPath_(i.path))
                 updatePathInfo(i);
             else
@@ -1334,7 +1324,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
            error if a cycle is detected and roll back the
            transaction.  Cycles can only occur when a derivation
            has multiple outputs. */
-        topoSortPaths(*this, paths);
+        topoSortPaths(paths);
 
         txn.commit();
     } end_retry_sqlite;
@@ -1404,7 +1394,7 @@ Path LocalStore::addToStoreFromDump(const string & dump, const string & name,
 
             ValidPathInfo info;
             info.path = dstPath;
-            info.hash = hash.first;
+            info.narHash = hash.first;
             info.narSize = hash.second;
             registerValidPath(info);
         }
@@ -1460,7 +1450,7 @@ Path LocalStore::addTextToStore(const string & name, const string & s,
 
             ValidPathInfo info;
             info.path = dstPath;
-            info.hash = hash.first;
+            info.narHash = hash.first;
             info.narSize = hash.second;
             info.references = references;
             registerValidPath(info);
@@ -1492,9 +1482,6 @@ struct HashAndWriteSink : Sink
 };
 
 
-#define EXPORT_MAGIC 0x4558494e
-
-
 static void checkSecrecy(const Path & path)
 {
     struct stat st;
@@ -1531,7 +1518,7 @@ void LocalStore::exportPath(const Path & path, bool sign,
     PathSet references;
     queryReferences(path, references);
 
-    hashAndWriteSink << EXPORT_MAGIC << path << references << queryDeriver(path);
+    hashAndWriteSink << exportMagic << path << references << queryDeriver(path);
 
     if (sign) {
         Hash hash = hashAndWriteSink.currentHash();
@@ -1607,8 +1594,8 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
 
     restorePath(unpacked, hashAndReadSource);
 
-    unsigned int magic = readInt(hashAndReadSource);
-    if (magic != EXPORT_MAGIC)
+    uint32_t magic = readInt(hashAndReadSource);
+    if (magic != exportMagic)
         throw Error("Nix archive cannot be imported; wrong format");
 
     Path dstPath = readStorePath(hashAndReadSource);
@@ -1690,7 +1677,7 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
 
             ValidPathInfo info;
             info.path = dstPath;
-            info.hash = hash.first;
+            info.narHash = hash.first;
             info.narSize = hash.second;
             info.references = references;
             info.deriver = deriver != "" && isValidPath(deriver) ? deriver : "";
@@ -1774,21 +1761,21 @@ bool LocalStore::verifyStore(bool checkContents, bool repair)
 
                 /* Check the content hash (optionally - slow). */
                 printMsg(lvlTalkative, format("checking contents of ‘%1%’") % i);
-                HashResult current = hashPath(info.hash.type, i);
+                HashResult current = hashPath(info.narHash.type, i);
 
-                if (info.hash != nullHash && info.hash != current.first) {
+                if (info.narHash != nullHash && info.narHash != current.first) {
                     printMsg(lvlError, format("path ‘%1%’ was modified! "
                             "expected hash ‘%2%’, got ‘%3%’")
-                        % i % printHash(info.hash) % printHash(current.first));
+                        % i % printHash(info.narHash) % printHash(current.first));
                     if (repair) repairPath(i); else errors = true;
                 } else {
 
                     bool update = false;
 
                     /* Fill in missing hashes. */
-                    if (info.hash == nullHash) {
+                    if (info.narHash == nullHash) {
                         printMsg(lvlError, format("fixing missing hash on ‘%1%’") % i);
-                        info.hash = current.first;
+                        info.narHash = current.first;
                         update = true;
                     }
 
@@ -1877,9 +1864,9 @@ bool LocalStore::pathContentsGood(const Path & path)
     if (!pathExists(path))
         res = false;
     else {
-        HashResult current = hashPath(info.hash.type, path);
+        HashResult current = hashPath(info.narHash.type, path);
         Hash nullHash(htSHA256);
-        res = info.hash == nullHash || info.hash == current.first;
+        res = info.narHash == nullHash || info.narHash == current.first;
     }
     pathContentsGoodCache[path] = res;
     if (!res) printMsg(lvlError, format("path ‘%1%’ is corrupted or missing!") % path);
@@ -1931,7 +1918,7 @@ ValidPathInfo LocalStore::queryPathInfoOld(const Path & path)
         } else if (name == "Deriver") {
             res.deriver = value;
         } else if (name == "Hash") {
-            res.hash = parseHashField(path, value);
+            res.narHash = parseHashField(path, value);
         } else if (name == "Registered-At") {
             int n = 0;
             string2Int(value, n);
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index ebdf19bf1359..a96000d9fbeb 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -80,7 +80,7 @@ struct SQLiteStmt
 };
 
 
-class LocalStore : public StoreAPI
+class LocalStore : public Store
 {
 private:
     typedef std::map<Path, RunningSubstituter> RunningSubstituters;
@@ -108,8 +108,6 @@ public:
 
     Hash queryPathHash(const Path & path) override;
 
-    void queryReferences(const Path & path, PathSet & references) override;
-
     void queryReferrers(const Path & path, PathSet & referrers) override;
 
     Path queryDeriver(const Path & path) override;
@@ -253,6 +251,12 @@ private:
 
     int getSchema();
 
+public:
+
+    static bool haveWriteAccess();
+
+private:
+
     void openDB(bool create);
 
     void makeStoreWritable();
@@ -297,6 +301,10 @@ private:
 
     int openGCLock(LockType lockType);
 
+    void findRoots(const Path & path, unsigned char type, Roots & roots);
+
+    void findRuntimeRoots(PathSet & roots);
+
     void removeUnusedLinks(const GCState & state);
 
     void startSubstituter(const Path & substituter,
diff --git a/src/libstore/local.mk b/src/libstore/local.mk
index e78f47949ad3..9a01596c36be 100644
--- a/src/libstore/local.mk
+++ b/src/libstore/local.mk
@@ -8,7 +8,7 @@ libstore_SOURCES := $(wildcard $(d)/*.cc)
 
 libstore_LIBS = libutil libformat
 
-libstore_LDFLAGS = $(SQLITE3_LIBS) -lbz2 $(LIBCURL_LIBS)
+libstore_LDFLAGS = $(SQLITE3_LIBS) -lbz2 $(LIBCURL_LIBS) $(SODIUM_LIBS)
 
 ifeq ($(OS), SunOS)
 	libstore_LDFLAGS += -lsocket
diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc
index 61a976c02f5c..12472f017ce4 100644
--- a/src/libstore/misc.cc
+++ b/src/libstore/misc.cc
@@ -1,21 +1,21 @@
-#include "misc.hh"
-#include "store-api.hh"
-#include "local-store.hh"
+#include "derivations.hh"
 #include "globals.hh"
+#include "local-store.hh"
+#include "store-api.hh"
 
 
 namespace nix {
 
 
-Derivation derivationFromPath(StoreAPI & store, const Path & drvPath)
+Derivation Store::derivationFromPath(const Path & drvPath)
 {
     assertStorePath(drvPath);
-    store.ensurePath(drvPath);
+    ensurePath(drvPath);
     return readDerivation(drvPath);
 }
 
 
-void computeFSClosure(StoreAPI & store, const Path & path,
+void Store::computeFSClosure(const Path & path,
     PathSet & paths, bool flipDirection, bool includeOutputs, bool includeDerivers)
 {
     if (paths.find(path) != paths.end()) return;
@@ -24,50 +24,42 @@ void computeFSClosure(StoreAPI & store, const Path & path,
     PathSet edges;
 
     if (flipDirection) {
-        store.queryReferrers(path, edges);
+        queryReferrers(path, edges);
 
         if (includeOutputs) {
-            PathSet derivers = store.queryValidDerivers(path);
+            PathSet derivers = queryValidDerivers(path);
             for (auto & i : derivers)
                 edges.insert(i);
         }
 
         if (includeDerivers && isDerivation(path)) {
-            PathSet outputs = store.queryDerivationOutputs(path);
+            PathSet outputs = queryDerivationOutputs(path);
             for (auto & i : outputs)
-                if (store.isValidPath(i) && store.queryDeriver(i) == path)
+                if (isValidPath(i) && queryDeriver(i) == path)
                     edges.insert(i);
         }
 
     } else {
-        store.queryReferences(path, edges);
+        queryReferences(path, edges);
 
         if (includeOutputs && isDerivation(path)) {
-            PathSet outputs = store.queryDerivationOutputs(path);
+            PathSet outputs = queryDerivationOutputs(path);
             for (auto & i : outputs)
-                if (store.isValidPath(i)) edges.insert(i);
+                if (isValidPath(i)) edges.insert(i);
         }
 
         if (includeDerivers) {
-            Path deriver = store.queryDeriver(path);
-            if (store.isValidPath(deriver)) edges.insert(deriver);
+            Path deriver = queryDeriver(path);
+            if (isValidPath(deriver)) edges.insert(deriver);
         }
     }
 
     for (auto & i : edges)
-        computeFSClosure(store, i, paths, flipDirection, includeOutputs, includeDerivers);
-}
-
-
-Path findOutput(const Derivation & drv, string id)
-{
-    for (auto & i : drv.outputs)
-        if (i.first == id) return i.second.path;
-    throw Error(format("derivation has no output ‘%1%’") % id);
+        computeFSClosure(i, paths, flipDirection, includeOutputs, includeDerivers);
 }
 
 
-void queryMissing(StoreAPI & store, const PathSet & targets,
+void Store::queryMissing(const PathSet & targets,
     PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,
     unsigned long long & downloadSize, unsigned long long & narSize)
 {
@@ -105,27 +97,27 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
             DrvPathWithOutputs i2 = parseDrvPathWithOutputs(i);
 
             if (isDerivation(i2.first)) {
-                if (!store.isValidPath(i2.first)) {
+                if (!isValidPath(i2.first)) {
                     // FIXME: we could try to substitute p.
                     unknown.insert(i);
                     continue;
                 }
-                Derivation drv = derivationFromPath(store, i2.first);
+                Derivation drv = derivationFromPath(i2.first);
 
                 PathSet invalid;
                 for (auto & j : drv.outputs)
                     if (wantOutput(j.first, i2.second)
-                        && !store.isValidPath(j.second.path))
+                        && !isValidPath(j.second.path))
                         invalid.insert(j.second.path);
                 if (invalid.empty()) continue;
 
                 todoDrv.insert(i);
-                if (settings.useSubstitutes && substitutesAllowed(drv))
+                if (settings.useSubstitutes && drv.substitutesAllowed())
                     query.insert(invalid.begin(), invalid.end());
             }
 
             else {
-                if (store.isValidPath(i)) continue;
+                if (isValidPath(i)) continue;
                 query.insert(i);
                 todoNonDrv.insert(i);
             }
@@ -134,20 +126,20 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
         todo.clear();
 
         SubstitutablePathInfos infos;
-        store.querySubstitutablePathInfos(query, infos);
+        querySubstitutablePathInfos(query, infos);
 
         for (auto & i : todoDrv) {
             DrvPathWithOutputs i2 = parseDrvPathWithOutputs(i);
 
             // FIXME: cache this
-            Derivation drv = derivationFromPath(store, i2.first);
+            Derivation drv = derivationFromPath(i2.first);
 
             PathSet outputs;
             bool mustBuild = false;
-            if (settings.useSubstitutes && substitutesAllowed(drv)) {
+            if (settings.useSubstitutes && drv.substitutesAllowed()) {
                 for (auto & j : drv.outputs) {
                     if (!wantOutput(j.first, i2.second)) continue;
-                    if (!store.isValidPath(j.second.path)) {
+                    if (!isValidPath(j.second.path)) {
                         if (infos.find(j.second.path) == infos.end())
                             mustBuild = true;
                         else
@@ -181,38 +173,38 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
 }
 
 
-static void dfsVisit(StoreAPI & store, const PathSet & paths,
-    const Path & path, PathSet & visited, Paths & sorted,
-    PathSet & parents)
+Paths Store::topoSortPaths(const PathSet & paths)
 {
-    if (parents.find(path) != parents.end())
-        throw BuildError(format("cycle detected in the references of ‘%1%’") % path);
+    Paths sorted;
+    PathSet visited, parents;
 
-    if (visited.find(path) != visited.end()) return;
-    visited.insert(path);
-    parents.insert(path);
+    std::function<void(const Path & path)> dfsVisit;
 
-    PathSet references;
-    if (store.isValidPath(path))
-        store.queryReferences(path, references);
+    dfsVisit = [&](const Path & path) {
+        if (parents.find(path) != parents.end())
+            throw BuildError(format("cycle detected in the references of ‘%1%’") % path);
 
-    for (auto & i : references)
-        /* Don't traverse into paths that don't exist.  That can
-           happen due to substitutes for non-existent paths. */
-        if (i != path && paths.find(i) != paths.end())
-            dfsVisit(store, paths, i, visited, sorted, parents);
+        if (visited.find(path) != visited.end()) return;
+        visited.insert(path);
+        parents.insert(path);
 
-    sorted.push_front(path);
-    parents.erase(path);
-}
+        PathSet references;
+        if (isValidPath(path))
+            queryReferences(path, references);
 
+        for (auto & i : references)
+            /* Don't traverse into paths that don't exist.  That can
+               happen due to substitutes for non-existent paths. */
+            if (i != path && paths.find(i) != paths.end())
+                dfsVisit(i);
+
+        sorted.push_front(path);
+        parents.erase(path);
+    };
 
-Paths topoSortPaths(StoreAPI & store, const PathSet & paths)
-{
-    Paths sorted;
-    PathSet visited, parents;
     for (auto & i : paths)
-        dfsVisit(store, paths, i, visited, sorted, parents);
+        dfsVisit(i);
+
     return sorted;
 }
 
diff --git a/src/libstore/misc.hh b/src/libstore/misc.hh
deleted file mode 100644
index 495c528750fb..000000000000
--- a/src/libstore/misc.hh
+++ /dev/null
@@ -1,40 +0,0 @@
-#pragma once
-
-#include "derivations.hh"
-
-
-namespace nix {
-
-
-/* Read a derivation, after ensuring its existence through
-   ensurePath(). */
-Derivation derivationFromPath(StoreAPI & store, const Path & drvPath);
-
-/* Place in `paths' the set of all store paths in the file system
-   closure of `storePath'; that is, all paths than can be directly or
-   indirectly reached from it.  `paths' is not cleared.  If
-   `flipDirection' is true, the set of paths that can reach
-   `storePath' is returned; that is, the closures under the
-   `referrers' relation instead of the `references' relation is
-   returned. */
-void computeFSClosure(StoreAPI & store, const Path & path,
-    PathSet & paths, bool flipDirection = false,
-    bool includeOutputs = false, bool includeDerivers = false);
-
-/* Return the path corresponding to the output identifier `id' in the
-   given derivation. */
-Path findOutput(const Derivation & drv, string id);
-
-/* Given a set of paths that are to be built, return the set of
-   derivations that will be built, and the set of output paths that
-   will be substituted. */
-void queryMissing(StoreAPI & store, const PathSet & targets,
-    PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,
-    unsigned long long & downloadSize, unsigned long long & narSize);
-
-bool willBuildLocally(const BasicDerivation & drv);
-
-bool substitutesAllowed(const BasicDerivation & drv);
-
-
-}
diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc
new file mode 100644
index 000000000000..e9260a09bf5a
--- /dev/null
+++ b/src/libstore/nar-info.cc
@@ -0,0 +1,134 @@
+#include "crypto.hh"
+#include "globals.hh"
+#include "nar-info.hh"
+
+namespace nix {
+
+NarInfo::NarInfo(const std::string & s, const std::string & whence)
+{
+    auto corrupt = [&]() {
+        throw Error("NAR info file ‘%1%’ is corrupt");
+    };
+
+    auto parseHashField = [&](const string & s) {
+        string::size_type colon = s.find(':');
+        if (colon == string::npos) corrupt();
+        HashType ht = parseHashType(string(s, 0, colon));
+        if (ht == htUnknown) corrupt();
+        return parseHash16or32(ht, string(s, colon + 1));
+    };
+
+    size_t pos = 0;
+    while (pos < s.size()) {
+
+        size_t colon = s.find(':', pos);
+        if (colon == std::string::npos) corrupt();
+
+        std::string name(s, pos, colon - pos);
+
+        size_t eol = s.find('\n', colon + 2);
+        if (eol == std::string::npos) corrupt();
+
+        std::string value(s, colon + 2, eol - colon - 2);
+
+        if (name == "StorePath") {
+            if (!isStorePath(value)) corrupt();
+            path = value;
+        }
+        else if (name == "URL")
+            url = value;
+        else if (name == "Compression")
+            compression = value;
+        else if (name == "FileHash")
+            fileHash = parseHashField(value);
+        else if (name == "FileSize") {
+            if (!string2Int(value, fileSize)) corrupt();
+        }
+        else if (name == "NarHash")
+            narHash = parseHashField(value);
+        else if (name == "NarSize") {
+            if (!string2Int(value, narSize)) corrupt();
+        }
+        else if (name == "References") {
+            auto refs = tokenizeString<Strings>(value, " ");
+            if (!references.empty()) corrupt();
+            for (auto & r : refs) {
+                auto r2 = settings.nixStore + "/" + r;
+                if (!isStorePath(r2)) corrupt();
+                references.insert(r2);
+            }
+        }
+        else if (name == "Deriver") {
+            auto p = settings.nixStore + "/" + value;
+            if (!isStorePath(p)) corrupt();
+            deriver = p;
+        }
+        else if (name == "System")
+            system = value;
+        else if (name == "Sig")
+            sig = value;
+
+        pos = eol + 1;
+    }
+
+    if (compression == "") compression = "bzip2";
+
+    if (path.empty() || url.empty()) corrupt();
+}
+
+std::string NarInfo::to_string() const
+{
+    std::string res;
+    res += "StorePath: " + path + "\n";
+    res += "URL: " + url + "\n";
+    assert(compression != "");
+    res += "Compression: " + compression + "\n";
+    assert(fileHash.type == htSHA256);
+    res += "FileHash: sha256:" + printHash32(fileHash) + "\n";
+    res += "FileSize: " + std::to_string(fileSize) + "\n";
+    assert(narHash.type == htSHA256);
+    res += "NarHash: sha256:" + printHash32(narHash) + "\n";
+    res += "NarSize: " + std::to_string(narSize) + "\n";
+
+    res += "References: " + concatStringsSep(" ", shortRefs()) + "\n";
+
+    if (!deriver.empty())
+        res += "Deriver: " + baseNameOf(deriver) + "\n";
+
+    if (!system.empty())
+        res += "System: " + system + "\n";
+
+    if (!sig.empty())
+        res += "Sig: " + sig + "\n";
+
+    return res;
+}
+
+std::string NarInfo::fingerprint() const
+{
+    return
+        "1;" + path + ";"
+        + printHashType(narHash.type) + ":" + printHash32(narHash) + ";"
+        + std::to_string(narSize) + ";"
+        + concatStringsSep(",", references);
+}
+
+Strings NarInfo::shortRefs() const
+{
+    Strings refs;
+    for (auto & r : references)
+        refs.push_back(baseNameOf(r));
+    return refs;
+}
+
+void NarInfo::sign(const SecretKey & secretKey)
+{
+    sig = secretKey.signDetached(fingerprint());
+}
+
+bool NarInfo::checkSignature(const PublicKeys & publicKeys) const
+{
+    return sig != "" && verifyDetached(fingerprint(), sig, publicKeys);
+}
+
+}
diff --git a/src/libstore/nar-info.hh b/src/libstore/nar-info.hh
new file mode 100644
index 000000000000..22e27cb42ebf
--- /dev/null
+++ b/src/libstore/nar-info.hh
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "types.hh"
+#include "hash.hh"
+#include "store-api.hh"
+
+namespace nix {
+
+struct NarInfo : ValidPathInfo
+{
+    std::string url;
+    std::string compression;
+    Hash fileHash;
+    uint64_t fileSize = 0;
+    std::string system;
+    std::string sig; // FIXME: support multiple signatures
+
+    NarInfo() { }
+    NarInfo(const ValidPathInfo & info) : ValidPathInfo(info) { }
+    NarInfo(const std::string & s, const std::string & whence);
+
+    std::string to_string() const;
+
+    /*  Return a fingerprint of the store path to be used in binary
+        cache signatures. It contains the store path, the base-32
+        SHA-256 hash of the NAR serialisation of the path, the size of
+        the NAR, and the sorted references. The size field is strictly
+        speaking superfluous, but might prevent endless/excessive data
+        attacks. */
+    std::string fingerprint() const;
+
+    void sign(const SecretKey & secretKey);
+
+    /* Return true iff this .narinfo is signed by one of the specified
+       keys. */
+    bool checkSignature(const PublicKeys & publicKeys) const;
+
+private:
+
+    Strings shortRefs() const;
+};
+
+}
diff --git a/src/libstore/pathlocks.cc b/src/libstore/pathlocks.cc
index 1c87034f8e1a..eddf5bcbda65 100644
--- a/src/libstore/pathlocks.cc
+++ b/src/libstore/pathlocks.cc
@@ -161,7 +161,11 @@ bool PathLocks::lockPaths(const PathSet & _paths,
 
 PathLocks::~PathLocks()
 {
-    unlock();
+    try {
+        unlock();
+    } catch (...) {
+        ignoreException();
+    }
 }
 
 
diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc
index da3f7da9d19d..cc83a838eddc 100644
--- a/src/libstore/profiles.cc
+++ b/src/libstore/profiles.cc
@@ -74,7 +74,7 @@ static void makeName(const Path & profile, unsigned int num,
 }
 
 
-Path createGeneration(Path profile, Path outPath)
+Path createGeneration(ref<Store> store, Path profile, Path outPath)
 {
     /* The new generation number should be higher than old the
        previous ones. */
@@ -108,7 +108,7 @@ Path createGeneration(Path profile, Path outPath)
        user environment etc. we've just built. */
     Path generation;
     makeName(profile, num + 1, generation);
-    addPermRoot(*store, outPath, generation, false, true);
+    store->addPermRoot(outPath, generation, false, true);
 
     return generation;
 }
diff --git a/src/libstore/profiles.hh b/src/libstore/profiles.hh
index e99bbf398a86..d758d94b603c 100644
--- a/src/libstore/profiles.hh
+++ b/src/libstore/profiles.hh
@@ -31,7 +31,9 @@ typedef list<Generation> Generations;
    profile, sorted by generation number. */
 Generations findGenerations(Path profile, int & curGen);
 
-Path createGeneration(Path profile, Path outPath);
+class Store;
+
+Path createGeneration(ref<Store> store, Path profile, Path outPath);
 
 void deleteGeneration(const Path & profile, unsigned int gen);
 
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index 262e4650bfb5..ab2ebb9aecc3 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -50,14 +50,8 @@ void RemoteStore::openConnection(bool reserveSpace)
     if (initialised) return;
     initialised = true;
 
-    string remoteMode = getEnv("NIX_REMOTE");
-
-    if (remoteMode == "daemon")
-        /* Connect to a daemon that does the privileged work for
-           us. */
-        connectToDaemon();
-    else
-        throw Error(format("invalid setting for NIX_REMOTE, ‘%1%’") % remoteMode);
+    /* Connect to a daemon that does the privileged work for us. */
+    connectToDaemon();
 
     from.fd = fdSocket;
     to.fd = fdSocket;
@@ -279,7 +273,7 @@ ValidPathInfo RemoteStore::queryPathInfo(const Path & path)
     info.path = path;
     info.deriver = readString(from);
     if (info.deriver != "") assertStorePath(info.deriver);
-    info.hash = parseHash(htSHA256, readString(from));
+    info.narHash = parseHash(htSHA256, readString(from));
     info.references = readStorePaths<PathSet>(from);
     info.registrationTime = readInt(from);
     info.narSize = readLongLong(from);
diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh
index b0787c072904..f15182285e8c 100644
--- a/src/libstore/remote-store.hh
+++ b/src/libstore/remote-store.hh
@@ -14,7 +14,7 @@ struct FdSink;
 struct FdSource;
 
 
-class RemoteStore : public StoreAPI
+class RemoteStore : public Store
 {
 public:
 
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index a73ebd824264..82872cc335e3 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -61,7 +61,14 @@ Path followLinksToStorePath(const Path & path)
 string storePathToName(const Path & path)
 {
     assertStorePath(path);
-    return string(path, settings.nixStore.size() + 34);
+    return string(path, settings.nixStore.size() + storePathHashLen + 2);
+}
+
+
+string storePathToHash(const Path & path)
+{
+    assertStorePath(path);
+    return string(path, settings.nixStore.size() + 1, storePathHashLen);
 }
 
 
@@ -217,10 +224,17 @@ Path computeStorePathForText(const string & name, const string & s,
 }
 
 
+void Store::queryReferences(const Path & path, PathSet & references)
+{
+    ValidPathInfo info = queryPathInfo(path);
+    references.insert(info.references.begin(), info.references.end());
+}
+
+
 /* Return a string accepted by decodeValidPathInfo() that
    registers the specified paths as valid.  Note: it's the
    responsibility of the caller to provide a closure. */
-string StoreAPI::makeValidityRegistration(const PathSet & paths,
+string Store::makeValidityRegistration(const PathSet & paths,
     bool showDerivers, bool showHash)
 {
     string s = "";
@@ -231,7 +245,7 @@ string StoreAPI::makeValidityRegistration(const PathSet & paths,
         ValidPathInfo info = queryPathInfo(i);
 
         if (showHash) {
-            s += printHash(info.hash) + "\n";
+            s += printHash(info.narHash) + "\n";
             s += (format("%1%\n") % info.narSize).str();
         }
 
@@ -256,7 +270,7 @@ ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven)
     if (hashGiven) {
         string s;
         getline(str, s);
-        info.hash = parseHash(htSHA256, s);
+        info.narHash = parseHash(htSHA256, s);
         getline(str, s);
         if (!string2Int(s, info.narSize)) throw Error("number expected");
     }
@@ -284,12 +298,12 @@ string showPaths(const PathSet & paths)
 }
 
 
-void exportPaths(StoreAPI & store, const Paths & paths,
+void Store::exportPaths(const Paths & paths,
     bool sign, Sink & sink)
 {
     for (auto & i : paths) {
         sink << 1;
-        store.exportPath(i, sign, sink);
+        exportPath(i, sign, sink);
     }
     sink << 0;
 }
@@ -306,15 +320,24 @@ void exportPaths(StoreAPI & store, const Paths & paths,
 namespace nix {
 
 
-std::shared_ptr<StoreAPI> store;
+ref<Store> openStore(bool reserveSpace)
+{
+    enum { mDaemon, mLocal, mAuto } mode;
 
+    mode = getEnv("NIX_REMOTE") == "daemon" ? mDaemon : mAuto;
 
-std::shared_ptr<StoreAPI> openStore(bool reserveSpace)
-{
-    if (getEnv("NIX_REMOTE") == "")
-        return std::shared_ptr<StoreAPI>(new LocalStore(reserveSpace));
-    else
-        return std::shared_ptr<StoreAPI>(new RemoteStore());
+    if (mode == mAuto) {
+        if (LocalStore::haveWriteAccess())
+            mode = mLocal;
+        else if (pathExists(settings.nixDaemonSocketFile))
+            mode = mDaemon;
+        else
+            mode = mLocal;
+    }
+
+    return mode == mDaemon
+        ? (ref<Store>) make_ref<RemoteStore>()
+        : (ref<Store>) make_ref<LocalStore>(reserveSpace);
 }
 
 
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 9cc5fd45b7c4..6f50e3c55aba 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -12,6 +12,13 @@
 namespace nix {
 
 
+/* Size of the hash part of store paths, in base-32 characters. */
+const size_t storePathHashLen = 32; // i.e. 160 bits
+
+/* Magic header of exportPath() output. */
+const uint32_t exportMagic = 0x4558494e;
+
+
 typedef std::map<Path, Path> Roots;
 
 
@@ -85,7 +92,7 @@ struct ValidPathInfo
 {
     Path path;
     Path deriver;
-    Hash hash;
+    Hash narHash;
     PathSet references;
     time_t registrationTime = 0;
     unsigned long long narSize = 0; // 0 = unknown
@@ -95,7 +102,7 @@ struct ValidPathInfo
     {
         return
             path == i.path
-            && hash == i.hash
+            && narHash == i.narHash
             && references == i.references;
     }
 };
@@ -103,7 +110,7 @@ struct ValidPathInfo
 typedef list<ValidPathInfo> ValidPathInfos;
 
 
-enum BuildMode { bmNormal, bmRepair, bmCheck };
+enum BuildMode { bmNormal, bmRepair, bmCheck, bmHash };
 
 
 struct BuildResult
@@ -132,13 +139,14 @@ struct BuildResult
 
 
 struct BasicDerivation;
+struct Derivation;
 
 
-class StoreAPI
+class Store
 {
 public:
 
-    virtual ~StoreAPI() { }
+    virtual ~Store() { }
 
     /* Check whether a path is valid. */
     virtual bool isValidPath(const Path & path) = 0;
@@ -155,10 +163,9 @@ public:
     /* Query the hash of a valid path. */
     virtual Hash queryPathHash(const Path & path) = 0;
 
-    /* Query the set of outgoing FS references for a store path.  The
+    /* Query the set of outgoing FS references for a store path. The
        result is not cleared. */
-    virtual void queryReferences(const Path & path,
-        PathSet & references) = 0;
+    virtual void queryReferences(const Path & path, PathSet & references);
 
     /* Queries the set of incoming FS references for a store path.
        The result is not cleared. */
@@ -214,6 +221,10 @@ public:
     virtual void exportPath(const Path & path, bool sign,
         Sink & sink) = 0;
 
+    /* Export multiple paths in the format expected by ‘nix-store
+       --import’. */
+    void exportPaths(const Paths & paths, bool sign, Sink & sink);
+
     /* Import a sequence of NAR dumps created by exportPaths() into
        the Nix store. */
     virtual Paths importPaths(bool requireSignature, Source & source) = 0;
@@ -250,6 +261,10 @@ public:
        `path' has disappeared. */
     virtual void addIndirectRoot(const Path & path) = 0;
 
+    /* Register a permanent GC root. */
+    Path addPermRoot(const Path & storePath,
+        const Path & gcRoot, bool indirect, bool allowOutsideRootsDir = false);
+
     /* Acquire the global GC lock, then immediately release it.  This
        function must be called after registering a new permanent root,
        but before exiting.  Otherwise, it is possible that a running
@@ -298,6 +313,35 @@ public:
     /* Check the integrity of the Nix store.  Returns true if errors
        remain. */
     virtual bool verifyStore(bool checkContents, bool repair) = 0;
+
+    /* Utility functions. */
+
+    /* Read a derivation, after ensuring its existence through
+       ensurePath(). */
+    Derivation derivationFromPath(const Path & drvPath);
+
+    /* Place in `paths' the set of all store paths in the file system
+       closure of `storePath'; that is, all paths than can be directly
+       or indirectly reached from it.  `paths' is not cleared.  If
+       `flipDirection' is true, the set of paths that can reach
+       `storePath' is returned; that is, the closures under the
+       `referrers' relation instead of the `references' relation is
+       returned. */
+    void computeFSClosure(const Path & path,
+        PathSet & paths, bool flipDirection = false,
+        bool includeOutputs = false, bool includeDerivers = false);
+
+    /* Given a set of paths that are to be built, return the set of
+       derivations that will be built, and the set of output paths
+       that will be substituted. */
+    void queryMissing(const PathSet & targets,
+        PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,
+        unsigned long long & downloadSize, unsigned long long & narSize);
+
+    /* Sort a set of paths topologically under the references
+       relation.  If p refers to q, then p preceeds q in this list. */
+    Paths topoSortPaths(const PathSet & paths);
+
 };
 
 
@@ -312,6 +356,9 @@ bool isStorePath(const Path & path);
 /* Extract the name part of the given store path. */
 string storePathToName(const Path & path);
 
+/* Extract the hash part of the given store path. */
+string storePathToHash(const Path & path);
+
 void checkStoreName(const string & name);
 
 
@@ -372,24 +419,9 @@ Path computeStorePathForText(const string & name, const string & s,
 void removeTempRoots();
 
 
-/* Register a permanent GC root. */
-Path addPermRoot(StoreAPI & store, const Path & storePath,
-    const Path & gcRoot, bool indirect, bool allowOutsideRootsDir = false);
-
-
-/* Sort a set of paths topologically under the references relation.
-   If p refers to q, then p preceeds q in this list. */
-Paths topoSortPaths(StoreAPI & store, const PathSet & paths);
-
-
-/* For now, there is a single global store API object, but we'll
-   purify that in the future. */
-extern std::shared_ptr<StoreAPI> store;
-
-
 /* Factory method: open the Nix database, either through the local or
    remote implementation. */
-std::shared_ptr<StoreAPI> openStore(bool reserveSpace = true);
+ref<Store> openStore(bool reserveSpace = true);
 
 
 /* Display a set of paths in human-readable form (i.e., between quotes
@@ -401,12 +433,6 @@ ValidPathInfo decodeValidPathInfo(std::istream & str,
     bool hashGiven = false);
 
 
-/* Export multiple paths in the format expected by ‘nix-store
-   --import’. */
-void exportPaths(StoreAPI & store, const Paths & paths,
-    bool sign, Sink & sink);
-
-
 MakeError(SubstError, Error)
 MakeError(BuildError, Error) /* denotes a permanent build failure */
 
diff --git a/src/libutil/affinity.cc b/src/libutil/affinity.cc
index 3e21f43a2e9d..3cbdf878617a 100644
--- a/src/libutil/affinity.cc
+++ b/src/libutil/affinity.cc
@@ -2,14 +2,14 @@
 #include "util.hh"
 #include "affinity.hh"
 
-#if HAVE_SCHED_H
+#if __linux__
 #include <sched.h>
 #endif
 
 namespace nix {
 
 
-#if HAVE_SCHED_SETAFFINITY
+#if __linux__
 static bool didSaveAffinity = false;
 static cpu_set_t savedAffinity;
 #endif
@@ -17,7 +17,7 @@ static cpu_set_t savedAffinity;
 
 void setAffinityTo(int cpu)
 {
-#if HAVE_SCHED_SETAFFINITY
+#if __linux__
     if (sched_getaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) return;
     didSaveAffinity = true;
     printMsg(lvlDebug, format("locking this thread to CPU %1%") % cpu);
@@ -32,7 +32,7 @@ void setAffinityTo(int cpu)
 
 int lockToCurrentCPU()
 {
-#if HAVE_SCHED_SETAFFINITY
+#if __linux__
     int cpu = sched_getcpu();
     if (cpu != -1) setAffinityTo(cpu);
     return cpu;
@@ -44,7 +44,7 @@ int lockToCurrentCPU()
 
 void restoreAffinity()
 {
-#if HAVE_SCHED_SETAFFINITY
+#if __linux__
     if (!didSaveAffinity) return;
     if (sched_setaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1)
         printMsg(lvlError, "failed to restore affinity %1%");
diff --git a/src/libutil/compression.cc b/src/libutil/compression.cc
index 446fcb781564..a3fa0dab737b 100644
--- a/src/libutil/compression.cc
+++ b/src/libutil/compression.cc
@@ -2,37 +2,87 @@
 #include "types.hh"
 
 #include <lzma.h>
+#include <cstdio>
 
 namespace nix {
 
+/* RAII wrapper around lzma_stream. */
+struct LzmaStream
+{
+    lzma_stream strm;
+    LzmaStream() : strm(LZMA_STREAM_INIT) { };
+    ~LzmaStream() { lzma_end(&strm); };
+    lzma_stream & operator()() { return strm; }
+};
+
+std::string compressXZ(const std::string & in)
+{
+    LzmaStream strm;
+
+    // FIXME: apply the x86 BCJ filter?
+
+    lzma_ret ret = lzma_easy_encoder(
+        &strm(), 6, LZMA_CHECK_CRC64);
+    if (ret != LZMA_OK)
+        throw Error("unable to initialise lzma encoder");
+
+    lzma_action action = LZMA_RUN;
+    uint8_t outbuf[BUFSIZ];
+    string res;
+    strm().next_in = (uint8_t *) in.c_str();
+    strm().avail_in = in.size();
+    strm().next_out = outbuf;
+    strm().avail_out = sizeof(outbuf);
+
+    while (true) {
+
+        if (strm().avail_in == 0)
+            action = LZMA_FINISH;
+
+        lzma_ret ret = lzma_code(&strm(), action);
+
+        if (strm().avail_out == 0 || ret == LZMA_STREAM_END) {
+            res.append((char *) outbuf, sizeof(outbuf) - strm().avail_out);
+            strm().next_out = outbuf;
+            strm().avail_out = sizeof(outbuf);
+        }
+
+        if (ret == LZMA_STREAM_END)
+            return res;
+
+        if (ret != LZMA_OK)
+            throw Error("error while decompressing xz file");
+    }
+}
+
 std::string decompressXZ(const std::string & in)
 {
-    lzma_stream strm = LZMA_STREAM_INIT;
+    LzmaStream strm;
 
     lzma_ret ret = lzma_stream_decoder(
-        &strm, UINT64_MAX, LZMA_CONCATENATED);
+        &strm(), UINT64_MAX, LZMA_CONCATENATED);
     if (ret != LZMA_OK)
         throw Error("unable to initialise lzma decoder");
 
     lzma_action action = LZMA_RUN;
     uint8_t outbuf[BUFSIZ];
     string res;
-    strm.next_in = (uint8_t *) in.c_str();
-    strm.avail_in = in.size();
-    strm.next_out = outbuf;
-    strm.avail_out = sizeof(outbuf);
+    strm().next_in = (uint8_t *) in.c_str();
+    strm().avail_in = in.size();
+    strm().next_out = outbuf;
+    strm().avail_out = sizeof(outbuf);
 
     while (true) {
 
-        if (strm.avail_in == 0)
+        if (strm().avail_in == 0)
             action = LZMA_FINISH;
 
-        lzma_ret ret = lzma_code(&strm, action);
+        lzma_ret ret = lzma_code(&strm(), action);
 
-        if (strm.avail_out == 0 || ret == LZMA_STREAM_END) {
-            res.append((char *) outbuf, sizeof(outbuf) - strm.avail_out);
-            strm.next_out = outbuf;
-            strm.avail_out = sizeof(outbuf);
+        if (strm().avail_out == 0 || ret == LZMA_STREAM_END) {
+            res.append((char *) outbuf, sizeof(outbuf) - strm().avail_out);
+            strm().next_out = outbuf;
+            strm().avail_out = sizeof(outbuf);
         }
 
         if (ret == LZMA_STREAM_END)
diff --git a/src/libutil/compression.hh b/src/libutil/compression.hh
index 962ce5ac7767..eb1697fc4aa4 100644
--- a/src/libutil/compression.hh
+++ b/src/libutil/compression.hh
@@ -4,6 +4,8 @@
 
 namespace nix {
 
+std::string compressXZ(const std::string & in);
+
 std::string decompressXZ(const std::string & in);
 
 }
diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc
index 2d97c5e6b6a7..64739300302b 100644
--- a/src/libutil/hash.cc
+++ b/src/libutil/hash.cc
@@ -96,19 +96,13 @@ Hash parseHash(HashType ht, const string & s)
 }
 
 
-unsigned int hashLength32(const Hash & hash)
-{
-    return (hash.hashSize * 8 - 1) / 5 + 1;
-}
-
-
 // omitted: E O U T
 const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz";
 
 
 string printHash32(const Hash & hash)
 {
-    unsigned int len = hashLength32(hash);
+    size_t len = hash.base32Len();
 
     string s;
     s.reserve(len);
@@ -136,7 +130,7 @@ string printHash16or32(const Hash & hash)
 Hash parseHash32(HashType ht, const string & s)
 {
     Hash hash(ht);
-    unsigned int len = hashLength32(ht);
+    size_t len = hash.base32Len();
     assert(s.size() == len);
 
     for (unsigned int n = 0; n < len; ++n) {
@@ -163,7 +157,7 @@ Hash parseHash16or32(HashType ht, const string & s)
     if (s.size() == hash.hashSize * 2)
         /* hexadecimal representation */
         hash = parseHash(ht, s);
-    else if (s.size() == hashLength32(hash))
+    else if (s.size() == hash.base32Len())
         /* base-32 representation */
         hash = parseHash32(ht, s);
     else
diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh
index 841b4cb2936c..bac2ebf2dcfa 100644
--- a/src/libutil/hash.hh
+++ b/src/libutil/hash.hh
@@ -7,7 +7,7 @@
 namespace nix {
 
 
-typedef enum { htUnknown, htMD5, htSHA1, htSHA256, htSHA512 } HashType;
+enum HashType : char { htUnknown, htMD5, htSHA1, htSHA256, htSHA512 };
 
 
 const int md5HashSize = 16;
@@ -40,6 +40,18 @@ struct Hash
 
     /* For sorting. */
     bool operator < (const Hash & h) const;
+
+    /* Returns the length of a base-16 representation of this hash. */
+    size_t base16Len() const
+    {
+        return hashSize * 2;
+    }
+
+    /* Returns the length of a base-32 representation of this hash. */
+    size_t base32Len() const
+    {
+        return (hashSize * 8 - 1) / 5 + 1;
+    }
 };
 
 
@@ -49,9 +61,6 @@ string printHash(const Hash & hash);
 /* Parse a hexadecimal representation of a hash code. */
 Hash parseHash(HashType ht, const string & s);
 
-/* Returns the length of a base-32 hash representation. */
-unsigned int hashLength32(const Hash & hash);
-
 /* Convert a hash to a base-32 representation. */
 string printHash32(const Hash & hash);
 
diff --git a/src/libutil/types.hh b/src/libutil/types.hh
index 160884ee1ad7..0eae46c5fe93 100644
--- a/src/libutil/types.hh
+++ b/src/libutil/types.hh
@@ -5,6 +5,7 @@
 #include <string>
 #include <list>
 #include <set>
+#include <memory>
 
 #include <boost/format.hpp>
 
@@ -96,4 +97,63 @@ typedef enum {
 } Verbosity;
 
 
+/* A simple non-nullable reference-counted pointer. Actually a wrapper
+   around std::shared_ptr that prevents non-null constructions. */
+template<typename T>
+class ref
+{
+private:
+
+    std::shared_ptr<T> p;
+
+public:
+
+    ref<T>(const ref<T> & r)
+        : p(r.p)
+    { }
+
+    explicit ref<T>(const std::shared_ptr<T> & p)
+        : p(p)
+    {
+        if (!p)
+            throw std::invalid_argument("null pointer cast to ref");
+    }
+
+    T* operator ->() const
+    {
+        return &*p;
+    }
+
+    T& operator *() const
+    {
+        return *p;
+    }
+
+    operator std::shared_ptr<T> ()
+    {
+        return p;
+    }
+
+    template<typename T2>
+    operator ref<T2> ()
+    {
+        return ref<T2>((std::shared_ptr<T2>) p);
+    }
+
+private:
+
+    template<typename T2, typename... Args>
+    friend ref<T2>
+    make_ref(Args&&... args);
+
+};
+
+template<typename T, typename... Args>
+inline ref<T>
+make_ref(Args&&... args)
+{
+    auto p = std::make_shared<T>(std::forward<Args>(args)...);
+    return ref<T>(p);
+}
+
 }
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 75032bf90d0b..def0525abc18 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -150,7 +150,7 @@ Path dirOf(const Path & path)
 string baseNameOf(const Path & path)
 {
     if (path.empty())
-        return string("");
+        return "";
 
     Path::size_type last = path.length() - 1;
     if (path[last] == '/' && last > 0)
@@ -161,6 +161,7 @@ string baseNameOf(const Path & path)
         pos = 0;
     else
         pos += 1;
+
     return string(path, pos, last - pos + 1);
 }
 
@@ -232,7 +233,13 @@ DirEntries readDirectory(const Path & path)
         checkInterrupt();
         string name = dirent->d_name;
         if (name == "." || name == "..") continue;
-        entries.emplace_back(name, dirent->d_ino, dirent->d_type);
+        entries.emplace_back(name, dirent->d_ino,
+#ifdef HAVE_STRUCT_DIRENT_D_TYPE
+            dirent->d_type
+#else
+            DT_UNKNOWN
+#endif
+        );
     }
     if (errno) throw SysError(format("reading directory ‘%1%’") % path);
 
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index f4026a0a884b..9eebb67fdf3a 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -8,9 +8,15 @@
 #include <unistd.h>
 #include <signal.h>
 #include <functional>
-
+#include <limits>
 #include <cstdio>
 
+#ifndef HAVE_STRUCT_DIRENT_D_TYPE
+#define DT_UNKNOWN 0
+#define DT_REG 1
+#define DT_LNK 2
+#define DT_DIR 3
+#endif
 
 namespace nix {
 
@@ -353,6 +359,16 @@ bool statusOk(int status);
 /* Parse a string into an integer. */
 template<class N> bool string2Int(const string & s, N & n)
 {
+    if (string(s, 0, 1) == "-" && !std::numeric_limits<N>::is_signed)
+        return false;
+    std::istringstream str(s);
+    str >> n;
+    return str && str.get() == EOF;
+}
+
+/* Parse a string into a float. */
+template<class N> bool string2Float(const string & s, N & n)
+{
     std::istringstream str(s);
     str >> n;
     return str && str.get() == EOF;
@@ -405,4 +421,14 @@ string base64Encode(const string & s);
 string base64Decode(const string & s);
 
 
+/* Get a value for the specified key from an associate container, or a
+   default value if the key doesn't exist. */
+template <class T>
+string get(const T & map, const string & key, const string & def = "")
+{
+    auto i = map.find(key);
+    return i == map.end() ? def : i->second;
+}
+
+
 }
diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc
index 5a72cb712014..b9ccafb751c1 100644
--- a/src/nix-collect-garbage/nix-collect-garbage.cc
+++ b/src/nix-collect-garbage/nix-collect-garbage.cc
@@ -82,7 +82,7 @@ int main(int argc, char * * argv)
 
         // Run the actual garbage collector.
         if (!dryRun) {
-            store = openStore(false);
+            auto store = openStore(false);
             options.action = GCOptions::gcDeleteDead;
             GCResults results;
             PrintFreed freed(true, results);
diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc
index c5e11afa1553..27694eac1a8c 100644
--- a/src/nix-daemon/nix-daemon.cc
+++ b/src/nix-daemon/nix-daemon.cc
@@ -149,7 +149,7 @@ struct SavingSourceAdapter : Source
 };
 
 
-static void performOp(bool trusted, unsigned int clientVersion,
+static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVersion,
     Source & from, Sink & to, unsigned int op)
 {
     switch (op) {
@@ -278,8 +278,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
 
         startWork();
         if (!savedRegular.regular) throw Error("regular file expected");
-        Path path = dynamic_cast<LocalStore *>(store.get())
-            ->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo);
+        Path path = store->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo);
         stopWork();
 
         to << path;
@@ -516,7 +515,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
         startWork();
         ValidPathInfo info = store->queryPathInfo(path);
         stopWork();
-        to << info.deriver << printHash(info.hash) << info.references
+        to << info.deriver << printHash(info.narHash) << info.references
            << info.registrationTime << info.narSize;
         break;
     }
@@ -583,56 +582,56 @@ static void processConnection(bool trusted)
 #endif
 
         /* Open the store. */
-        store = std::shared_ptr<StoreAPI>(new LocalStore(reserveSpace));
+        auto store = make_ref<LocalStore>(reserveSpace);
 
         stopWork();
         to.flush();
 
-    } catch (Error & e) {
-        stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
-        to.flush();
-        return;
-    }
+        /* Process client requests. */
+        unsigned int opCount = 0;
+
+        while (true) {
+            WorkerOp op;
+            try {
+                op = (WorkerOp) readInt(from);
+            } catch (Interrupted & e) {
+                break;
+            } catch (EndOfFile & e) {
+                break;
+            }
 
-    /* Process client requests. */
-    unsigned int opCount = 0;
+            opCount++;
+
+            try {
+                performOp(store, trusted, clientVersion, from, to, op);
+            } catch (Error & e) {
+                /* If we're not in a state where we can send replies, then
+                   something went wrong processing the input of the
+                   client.  This can happen especially if I/O errors occur
+                   during addTextToStore() / importPath().  If that
+                   happens, just send the error message and exit. */
+                bool errorAllowed = canSendStderr;
+                stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0);
+                if (!errorAllowed) throw;
+            } catch (std::bad_alloc & e) {
+                stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
+                throw;
+            }
 
-    while (true) {
-        WorkerOp op;
-        try {
-            op = (WorkerOp) readInt(from);
-        } catch (Interrupted & e) {
-            break;
-        } catch (EndOfFile & e) {
-            break;
-        }
+            to.flush();
 
-        opCount++;
+            assert(!canSendStderr);
+        };
 
-        try {
-            performOp(trusted, clientVersion, from, to, op);
-        } catch (Error & e) {
-            /* If we're not in a state where we can send replies, then
-               something went wrong processing the input of the
-               client.  This can happen especially if I/O errors occur
-               during addTextToStore() / importPath().  If that
-               happens, just send the error message and exit. */
-            bool errorAllowed = canSendStderr;
-            stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0);
-            if (!errorAllowed) throw;
-        } catch (std::bad_alloc & e) {
-            stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
-            throw;
-        }
+        canSendStderr = false;
+        _isInterrupted = false;
+        printMsg(lvlDebug, format("%1% operations") % opCount);
 
+    } catch (Error & e) {
+        stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
         to.flush();
-
-        assert(!canSendStderr);
-    };
-
-    canSendStderr = false;
-    _isInterrupted = false;
-    printMsg(lvlDebug, format("%1% operations") % opCount);
+        return;
+    }
 }
 
 
@@ -787,10 +786,6 @@ static void daemonLoop(char * * argv)
     while (1) {
 
         try {
-            /* Important: the server process *cannot* open the SQLite
-               database, because it doesn't like forks very much. */
-            assert(!store);
-
             /* Accept a connection. */
             struct sockaddr_un remoteAddr;
             socklen_t remoteAddrLen = sizeof(remoteAddr);
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index 02a9f25a7a4e..a9d1ed024dd3 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -1,17 +1,17 @@
-#include "profiles.hh"
-#include "names.hh"
-#include "globals.hh"
-#include "misc.hh"
-#include "shared.hh"
-#include "eval.hh"
-#include "get-drvs.hh"
 #include "attr-path.hh"
 #include "common-opts.hh"
-#include "xml-writer.hh"
+#include "derivations.hh"
+#include "eval.hh"
+#include "get-drvs.hh"
+#include "globals.hh"
+#include "names.hh"
+#include "profiles.hh"
+#include "shared.hh"
 #include "store-api.hh"
 #include "user-env.hh"
 #include "util.hh"
 #include "value-to-json.hh"
+#include "xml-writer.hh"
 
 #include <cerrno>
 #include <ctime>
@@ -223,8 +223,8 @@ static int comparePriorities(EvalState & state, DrvInfo & drv1, DrvInfo & drv2)
 static bool isPrebuilt(EvalState & state, DrvInfo & elem)
 {
     Path path = elem.queryOutPath();
-    if (store->isValidPath(path)) return true;
-    PathSet ps = store->querySubstitutablePaths(singleton<PathSet>(path));
+    if (state.store->isValidPath(path)) return true;
+    PathSet ps = state.store->querySubstitutablePaths(singleton<PathSet>(path));
     return ps.find(path) != ps.end();
 }
 
@@ -398,7 +398,7 @@ static void queryInstSources(EvalState & state,
 
                 if (isDerivation(path)) {
                     elem.setDrvPath(path);
-                    elem.setOutPath(findOutput(derivationFromPath(*store, path), "out"));
+                    elem.setOutPath(state.store->derivationFromPath(path).findOutput("out"));
                     if (name.size() >= drvExtension.size() &&
                         string(name, name.size() - drvExtension.size()) == drvExtension)
                         name = string(name, 0, name.size() - drvExtension.size());
@@ -445,7 +445,7 @@ static void printMissing(EvalState & state, DrvInfos & elems)
             targets.insert(i.queryOutPath());
     }
 
-    printMissing(*store, targets);
+    printMissing(state.store, targets);
 }
 
 
@@ -711,18 +711,18 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
 
     if (drv.queryDrvPath() != "") {
         PathSet paths = singleton<PathSet>(drv.queryDrvPath());
-        printMissing(*store, paths);
+        printMissing(globals.state->store, paths);
         if (globals.dryRun) return;
-        store->buildPaths(paths, globals.state->repair ? bmRepair : bmNormal);
+        globals.state->store->buildPaths(paths, globals.state->repair ? bmRepair : bmNormal);
     }
     else {
-        printMissing(*store, singleton<PathSet>(drv.queryOutPath()));
+        printMissing(globals.state->store, singleton<PathSet>(drv.queryOutPath()));
         if (globals.dryRun) return;
-        store->ensurePath(drv.queryOutPath());
+        globals.state->store->ensurePath(drv.queryOutPath());
     }
 
     debug(format("switching to new user environment"));
-    Path generation = createGeneration(globals.profile, drv.queryOutPath());
+    Path generation = createGeneration(globals.state->store, globals.profile, drv.queryOutPath());
     switchLink(globals.profile, generation);
 }
 
@@ -973,8 +973,8 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
                 printMsg(lvlTalkative, format("skipping derivation named ‘%1%’ which gives an assertion failure") % i.name);
                 i.setFailed();
             }
-        validPaths = store->queryValidPaths(paths);
-        substitutablePaths = store->querySubstitutablePaths(paths);
+        validPaths = globals.state->store->queryValidPaths(paths);
+        substitutablePaths = globals.state->store->querySubstitutablePaths(paths);
     }
 
 
@@ -1127,6 +1127,10 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
                                     attrs2["type"] = "int";
                                     attrs2["value"] = (format("%1%") % v->integer).str();
                                     xml.writeEmptyElement("meta", attrs2);
+                                } else if (v->type == tFloat) {
+                                    attrs2["type"] = "float";
+                                    attrs2["value"] = (format("%1%") % v->fpoint).str();
+                                    xml.writeEmptyElement("meta", attrs2);
                                 } else if (v->type == tBool) {
                                     attrs2["type"] = "bool";
                                     attrs2["value"] = v->boolean ? "true" : "false";
@@ -1394,9 +1398,9 @@ int main(int argc, char * * argv)
 
         if (!op) throw UsageError("no operation specified");
 
-        store = openStore();
+        auto store = openStore();
 
-        globals.state = std::shared_ptr<EvalState>(new EvalState(searchPath));
+        globals.state = std::shared_ptr<EvalState>(new EvalState(searchPath, store));
         globals.state->repair = repair;
 
         if (file != "")
diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc
index 9a20b94334f2..4e0e28c1158c 100644
--- a/src/nix-env/user-env.cc
+++ b/src/nix-env/user-env.cc
@@ -38,7 +38,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
             drvsToBuild.insert(i.queryDrvPath());
 
     debug(format("building user environment dependencies"));
-    store->buildPaths(drvsToBuild, state.repair ? bmRepair : bmNormal);
+    state.store->buildPaths(drvsToBuild, state.repair ? bmRepair : bmNormal);
 
     /* Construct the whole top level derivation. */
     PathSet references;
@@ -76,8 +76,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
 
             /* This is only necessary when installing store paths, e.g.,
                `nix-env -i /nix/store/abcd...-foo'. */
-            store->addTempRoot(j.second);
-            store->ensurePath(j.second);
+            state.store->addTempRoot(j.second);
+            state.store->ensurePath(j.second);
 
             references.insert(j.second);
         }
@@ -100,7 +100,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
     /* Also write a copy of the list of user environment elements to
        the store; we need it for future modifications of the
        environment. */
-    Path manifestFile = store->addTextToStore("env-manifest.nix",
+    Path manifestFile = state.store->addTextToStore("env-manifest.nix",
         (format("%1%") % manifest).str(), references);
 
     /* Get the environment builder expression. */
@@ -128,7 +128,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
 
     /* Realise the resulting store expression. */
     debug("building user environment");
-    store->buildPaths(singleton<PathSet>(topLevelDrv), state.repair ? bmRepair : bmNormal);
+    state.store->buildPaths(singleton<PathSet>(topLevelDrv), state.repair ? bmRepair : bmNormal);
 
     /* Switch the current user environment to the output path. */
     PathLocks lock;
@@ -141,7 +141,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
     }
 
     debug(format("switching to new user environment"));
-    Path generation = createGeneration(profile, topLevelOut);
+    Path generation = createGeneration(state.store, profile, topLevelOut);
     switchLink(profile, generation);
 
     return true;
diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc
index 13a145a3b53e..81c1c8d5637c 100644
--- a/src/nix-instantiate/nix-instantiate.cc
+++ b/src/nix-instantiate/nix-instantiate.cc
@@ -9,7 +9,6 @@
 #include "util.hh"
 #include "store-api.hh"
 #include "common-opts.hh"
-#include "misc.hh"
 
 #include <map>
 #include <iostream>
@@ -80,7 +79,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
                 else {
                     Path rootName = gcRoot;
                     if (++rootNr > 1) rootName += "-" + std::to_string(rootNr);
-                    drvPath = addPermRoot(*store, drvPath, rootName, indirectRoot);
+                    drvPath = state.store->addPermRoot(drvPath, rootName, indirectRoot);
                 }
                 std::cout << format("%1%%2%\n") % drvPath % (outputName != "out" ? "!" + outputName : "");
             }
@@ -158,9 +157,9 @@ int main(int argc, char * * argv)
         if (evalOnly && !wantsReadWrite)
             settings.readOnlyMode = true;
 
-        store = openStore();
+        auto store = openStore();
 
-        EvalState state(searchPath);
+        EvalState state(searchPath, store);
         state.repair = repair;
 
         Bindings & autoArgs(*evalAutoArgs(state, autoArgs_));
diff --git a/src/nix-prefetch-url/nix-prefetch-url.cc b/src/nix-prefetch-url/nix-prefetch-url.cc
index 73a2845e07a5..c0c05a60bd78 100644
--- a/src/nix-prefetch-url/nix-prefetch-url.cc
+++ b/src/nix-prefetch-url/nix-prefetch-url.cc
@@ -91,8 +91,8 @@ int main(int argc, char * * argv)
         if (args.size() > 2)
             throw UsageError("too many arguments");
 
-        store = openStore();
-        EvalState state(searchPath);
+        auto store = openStore();
+        EvalState state(searchPath, store);
 
         Bindings & autoArgs(*evalAutoArgs(state, autoArgs_));
 
diff --git a/src/nix-store/dotgraph.cc b/src/nix-store/dotgraph.cc
index a333d7351010..8735cf9b667b 100644
--- a/src/nix-store/dotgraph.cc
+++ b/src/nix-store/dotgraph.cc
@@ -20,8 +20,8 @@ static string nextColour()
 {
     static int n = 0;
     static string colours[] =
-	{ "black", "red", "green", "blue"
-	, "magenta", "burlywood" };
+        { "black", "red", "green", "blue"
+        , "magenta", "burlywood" };
     return colours[n++ % (sizeof(colours) / sizeof(string))];
 }
 
@@ -29,7 +29,7 @@ static string nextColour()
 static string makeEdge(const string & src, const string & dst)
 {
     format f = format("%1% -> %2% [color = %3%];\n")
-	% dotQuote(src) % dotQuote(dst) % dotQuote(nextColour());
+        % dotQuote(src) % dotQuote(dst) % dotQuote(nextColour());
     return f.str();
 }
 
@@ -38,8 +38,8 @@ static string makeNode(const string & id, const string & label,
     const string & colour)
 {
     format f = format("%1% [label = %2%, shape = box, "
-	"style = filled, fillcolor = %3%];\n")
-	% dotQuote(id) % dotQuote(label) % dotQuote(colour);
+        "style = filled, fillcolor = %3%];\n")
+        % dotQuote(id) % dotQuote(label) % dotQuote(colour);
     return f.str();
 }
 
@@ -65,51 +65,51 @@ void printClosure(const Path & nePath, const StoreExpr & fs)
     PathSet doneSet;
 
     for (PathSet::iterator i = workList.begin(); i != workList.end(); ++i) {
-	cout << makeEdge(pathLabel(nePath, *i), nePath);
+        cout << makeEdge(pathLabel(nePath, *i), nePath);
     }
 
     while (!workList.empty()) {
-	Path path = *(workList.begin());
-	workList.erase(path);
-
-	if (doneSet.find(path) == doneSet.end()) {
-	    doneSet.insert(path);
-
-	    ClosureElems::const_iterator elem = fs.closure.elems.find(path);
-	    if (elem == fs.closure.elems.end())
-		throw Error(format("bad closure, missing path ‘%1%’") % path);
-
-	    for (StringSet::const_iterator i = elem->second.refs.begin();
-		 i != elem->second.refs.end(); ++i)
-	    {
-		workList.insert(*i);
-		cout << makeEdge(pathLabel(nePath, *i), pathLabel(nePath, path));
-	    }
-
-	    cout << makeNode(pathLabel(nePath, path), 
-		symbolicName(path), "#ff0000");
-	}
+        Path path = *(workList.begin());
+        workList.erase(path);
+
+        if (doneSet.find(path) == doneSet.end()) {
+            doneSet.insert(path);
+
+            ClosureElems::const_iterator elem = fs.closure.elems.find(path);
+            if (elem == fs.closure.elems.end())
+                throw Error(format("bad closure, missing path ‘%1%’") % path);
+
+            for (StringSet::const_iterator i = elem->second.refs.begin();
+                 i != elem->second.refs.end(); ++i)
+            {
+                workList.insert(*i);
+                cout << makeEdge(pathLabel(nePath, *i), pathLabel(nePath, path));
+            }
+
+            cout << makeNode(pathLabel(nePath, path),
+                symbolicName(path), "#ff0000");
+        }
     }
 }
 #endif
 
 
-void printDotGraph(const PathSet & roots)
+void printDotGraph(ref<Store> store, const PathSet & roots)
 {
     PathSet workList(roots);
     PathSet doneSet;
-            
+
     cout << "digraph G {\n";
 
     while (!workList.empty()) {
-	Path path = *(workList.begin());
-	workList.erase(path);
+        Path path = *(workList.begin());
+        workList.erase(path);
 
-	if (doneSet.find(path) != doneSet.end()) continue;
+        if (doneSet.find(path) != doneSet.end()) continue;
         doneSet.insert(path);
 
         cout << makeNode(path, symbolicName(path), "#ff0000");
-        
+
         PathSet references;
         store->queryReferences(path, references);
 
@@ -121,42 +121,42 @@ void printDotGraph(const PathSet & roots)
                 cout << makeEdge(*i, path);
             }
         }
-            
-
-#if 0        
-	    StoreExpr ne = storeExprFromPath(path);
-
-	    string label, colour;
-                    
-	    if (ne.type == StoreExpr::neDerivation) {
-		for (PathSet::iterator i = ne.derivation.inputs.begin();
-		     i != ne.derivation.inputs.end(); ++i)
-		{
-		    workList.insert(*i);
-		    cout << makeEdge(*i, path);
-		}
-
-		label = "derivation";
-		colour = "#00ff00";
-		for (StringPairs::iterator i = ne.derivation.env.begin();
-		     i != ne.derivation.env.end(); ++i)
-		    if (i->first == "name") label = i->second;
-	    }
-
-	    else if (ne.type == StoreExpr::neClosure) {
-		label = "<closure>";
-		colour = "#00ffff";
-		printClosure(path, ne);
-	    }
-
-	    else abort();
-
-	    cout << makeNode(path, label, colour);
+
+
+#if 0
+        StoreExpr ne = storeExprFromPath(path);
+
+        string label, colour;
+
+        if (ne.type == StoreExpr::neDerivation) {
+            for (PathSet::iterator i = ne.derivation.inputs.begin();
+                 i != ne.derivation.inputs.end(); ++i)
+            {
+                workList.insert(*i);
+                cout << makeEdge(*i, path);
+            }
+
+            label = "derivation";
+            colour = "#00ff00";
+            for (StringPairs::iterator i = ne.derivation.env.begin();
+                 i != ne.derivation.env.end(); ++i)
+                if (i->first == "name") label = i->second;
+        }
+
+        else if (ne.type == StoreExpr::neClosure) {
+            label = "<closure>";
+            colour = "#00ffff";
+            printClosure(path, ne);
+        }
+
+        else abort();
+
+        cout << makeNode(path, label, colour);
 #endif
     }
 
     cout << "}\n";
 }
 
- 
+
 }
diff --git a/src/nix-store/dotgraph.hh b/src/nix-store/dotgraph.hh
index 68410d84156d..e2b5fc72fbe1 100644
--- a/src/nix-store/dotgraph.hh
+++ b/src/nix-store/dotgraph.hh
@@ -4,6 +4,8 @@
 
 namespace nix {
 
-void printDotGraph(const PathSet & roots);
+class Store;
+
+void printDotGraph(ref<Store> store, const PathSet & roots);
 
 }
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index 14354f86e228..7ec61eb625b0 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -1,14 +1,14 @@
-#include "globals.hh"
-#include "misc.hh"
 #include "archive.hh"
-#include "shared.hh"
+#include "derivations.hh"
 #include "dotgraph.hh"
-#include "xmlgraph.hh"
+#include "globals.hh"
 #include "local-store.hh"
-#include "util.hh"
+#include "monitor-fd.hh"
 #include "serve-protocol.hh"
+#include "shared.hh"
+#include "util.hh"
 #include "worker-protocol.hh"
-#include "monitor-fd.hh"
+#include "xmlgraph.hh"
 
 #include <iostream>
 #include <algorithm>
@@ -37,13 +37,14 @@ static Path gcRoot;
 static int rootNr = 0;
 static bool indirectRoot = false;
 static bool noOutput = false;
+static std::shared_ptr<Store> store;
 
 
-LocalStore & ensureLocalStore()
+ref<LocalStore> ensureLocalStore()
 {
-    LocalStore * store2(dynamic_cast<LocalStore *>(store.get()));
+    auto store2 = std::dynamic_pointer_cast<LocalStore>(store);
     if (!store2) throw Error("you don't have sufficient rights to use this command");
-    return *store2;
+    return ref<LocalStore>(store2);
 }
 
 
@@ -65,7 +66,7 @@ static PathSet realisePath(Path path, bool build = true)
 
     if (isDerivation(p.first)) {
         if (build) store->buildPaths(singleton<PathSet>(path));
-        Derivation drv = derivationFromPath(*store, p.first);
+        Derivation drv = store->derivationFromPath(p.first);
         rootNr++;
 
         if (p.second.empty())
@@ -83,7 +84,7 @@ static PathSet realisePath(Path path, bool build = true)
                 Path rootName = gcRoot;
                 if (rootNr > 1) rootName += "-" + std::to_string(rootNr);
                 if (i->first != "out") rootName += "-" + i->first;
-                outPath = addPermRoot(*store, outPath, rootName, indirectRoot);
+                outPath = store->addPermRoot(outPath, rootName, indirectRoot);
             }
             outputs.insert(outPath);
         }
@@ -99,7 +100,7 @@ static PathSet realisePath(Path path, bool build = true)
             Path rootName = gcRoot;
             rootNr++;
             if (rootNr > 1) rootName += "-" + std::to_string(rootNr);
-            path = addPermRoot(*store, path, rootName, indirectRoot);
+            path = store->addPermRoot(path, rootName, indirectRoot);
         }
         return singleton<PathSet>(path);
     }
@@ -117,6 +118,7 @@ static void opRealise(Strings opFlags, Strings opArgs)
         if (i == "--dry-run") dryRun = true;
         else if (i == "--repair") buildMode = bmRepair;
         else if (i == "--check") buildMode = bmCheck;
+        else if (i == "--hash") buildMode = bmHash;
         else if (i == "--ignore-unknown") ignoreUnknown = true;
         else throw UsageError(format("unknown flag ‘%1%’") % i);
 
@@ -128,7 +130,7 @@ static void opRealise(Strings opFlags, Strings opArgs)
 
     unsigned long long downloadSize, narSize;
     PathSet willBuild, willSubstitute, unknown;
-    queryMissing(*store, PathSet(paths.begin(), paths.end()),
+    store->queryMissing(PathSet(paths.begin(), paths.end()),
         willBuild, willSubstitute, unknown, downloadSize, narSize);
 
     if (ignoreUnknown) {
@@ -140,7 +142,7 @@ static void opRealise(Strings opFlags, Strings opArgs)
     }
 
     if (settings.get("print-missing", true))
-        printMissing(willBuild, willSubstitute, unknown, downloadSize, narSize);
+        printMissing(ref<Store>(store), willBuild, willSubstitute, unknown, downloadSize, narSize);
 
     if (dryRun) return;
 
@@ -215,7 +217,7 @@ static PathSet maybeUseOutputs(const Path & storePath, bool useOutput, bool forc
 {
     if (forceRealise) realisePath(storePath);
     if (useOutput && isDerivation(storePath)) {
-        Derivation drv = derivationFromPath(*store, storePath);
+        Derivation drv = store->derivationFromPath(storePath);
         PathSet outputs;
         for (auto & i : drv.outputs)
             outputs.insert(i.second.path);
@@ -252,7 +254,7 @@ static void printTree(const Path & path,
        closure(B).  That is, if derivation A is an (possibly indirect)
        input of B, then A is printed first.  This has the effect of
        flattening the tree, preventing deeply nested structures.  */
-    Paths sorted = topoSortPaths(*store, references);
+    Paths sorted = store->topoSortPaths(references);
     reverse(sorted.begin(), sorted.end());
 
     for (auto i = sorted.begin(); i != sorted.end(); ++i) {
@@ -317,7 +319,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
             for (auto & i : opArgs) {
                 i = followLinksToStorePath(i);
                 if (forceRealise) realisePath(i);
-                Derivation drv = derivationFromPath(*store, i);
+                Derivation drv = store->derivationFromPath(i);
                 for (auto & j : drv.outputs)
                     cout << format("%1%\n") % j.second.path;
             }
@@ -332,13 +334,13 @@ static void opQuery(Strings opFlags, Strings opArgs)
             for (auto & i : opArgs) {
                 PathSet ps = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise);
                 for (auto & j : ps) {
-                    if (query == qRequisites) computeFSClosure(*store, j, paths, false, includeOutputs);
+                    if (query == qRequisites) store->computeFSClosure(j, paths, false, includeOutputs);
                     else if (query == qReferences) store->queryReferences(j, paths);
                     else if (query == qReferrers) store->queryReferrers(j, paths);
-                    else if (query == qReferrersClosure) computeFSClosure(*store, j, paths, true);
+                    else if (query == qReferrersClosure) store->computeFSClosure(j, paths, true);
                 }
             }
-            Paths sorted = topoSortPaths(*store, paths);
+            Paths sorted = store->topoSortPaths(paths);
             for (Paths::reverse_iterator i = sorted.rbegin();
                  i != sorted.rend(); ++i)
                 cout << format("%s\n") % *i;
@@ -356,7 +358,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
         case qBinding:
             for (auto & i : opArgs) {
                 Path path = useDeriver(followLinksToStorePath(i));
-                Derivation drv = derivationFromPath(*store, path);
+                Derivation drv = store->derivationFromPath(path);
                 StringPairs::iterator j = drv.env.find(bindingName);
                 if (j == drv.env.end())
                     throw Error(format("derivation ‘%1%’ has no environment binding named ‘%2%’")
@@ -372,8 +374,8 @@ static void opQuery(Strings opFlags, Strings opArgs)
                 for (auto & j : paths) {
                     ValidPathInfo info = store->queryPathInfo(j);
                     if (query == qHash) {
-                        assert(info.hash.type == htSHA256);
-                        cout << format("sha256:%1%\n") % printHash32(info.hash);
+                        assert(info.narHash.type == htSHA256);
+                        cout << format("sha256:%1%\n") % printHash32(info.narHash);
                     } else if (query == qSize)
                         cout << format("%1%\n") % info.narSize;
                 }
@@ -393,7 +395,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
                 PathSet paths = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise);
                 roots.insert(paths.begin(), paths.end());
             }
-            printDotGraph(roots);
+            printDotGraph(ref<Store>(store), roots);
             break;
         }
 
@@ -403,7 +405,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
                 PathSet paths = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise);
                 roots.insert(paths.begin(), paths.end());
             }
-            printXmlGraph(roots);
+            printXmlGraph(ref<Store>(store), roots);
             break;
         }
 
@@ -418,7 +420,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
             for (auto & i : opArgs) {
                 PathSet paths = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise);
                 for (auto & j : paths)
-                    computeFSClosure(*store, j, referrers, true,
+                    store->computeFSClosure(j, referrers, true,
                         settings.gcKeepOutputs, settings.gcKeepDerivations);
             }
             Roots roots = store->findRoots();
@@ -449,7 +451,7 @@ static void opPrintEnv(Strings opFlags, Strings opArgs)
     if (opArgs.size() != 1) throw UsageError("‘--print-env’ requires one derivation store path");
 
     Path drvPath = opArgs.front();
-    Derivation drv = derivationFromPath(*store, drvPath);
+    Derivation drv = store->derivationFromPath(drvPath);
 
     /* Print each environment variable in the derivation in a format
        that can be sourced by the shell. */
@@ -565,14 +567,14 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise)
                 canonicalisePathMetaData(info.path, -1);
             if (!hashGiven) {
                 HashResult hash = hashPath(htSHA256, info.path);
-                info.hash = hash.first;
+                info.narHash = hash.first;
                 info.narSize = hash.second;
             }
             infos.push_back(info);
         }
     }
 
-    ensureLocalStore().registerValidPaths(infos);
+    ensureLocalStore()->registerValidPaths(infos);
 }
 
 
@@ -714,9 +716,9 @@ static void opExport(Strings opFlags, Strings opArgs)
         else throw UsageError(format("unknown flag ‘%1%’") % i);
 
     FdSink sink(STDOUT_FILENO);
-    Paths sorted = topoSortPaths(*store, PathSet(opArgs.begin(), opArgs.end()));
+    Paths sorted = store->topoSortPaths(PathSet(opArgs.begin(), opArgs.end()));
     reverse(sorted.begin(), sorted.end());
-    exportPaths(*store, sorted, sign, sink);
+    store->exportPaths(sorted, sign, sink);
 }
 
 
@@ -781,11 +783,11 @@ static void opVerifyPath(Strings opFlags, Strings opArgs)
         Path path = followLinksToStorePath(i);
         printMsg(lvlTalkative, format("checking path ‘%1%’...") % path);
         ValidPathInfo info = store->queryPathInfo(path);
-        HashResult current = hashPath(info.hash.type, path);
-        if (current.first != info.hash) {
+        HashResult current = hashPath(info.narHash.type, path);
+        if (current.first != info.narHash) {
             printMsg(lvlError,
                 format("path ‘%1%’ was modified! expected hash ‘%2%’, got ‘%3%’")
-                % path % printHash(info.hash) % printHash(current.first));
+                % path % printHash(info.narHash) % printHash(current.first));
             status = 1;
         }
     }
@@ -803,7 +805,7 @@ static void opRepairPath(Strings opFlags, Strings opArgs)
 
     for (auto & i : opArgs) {
         Path path = followLinksToStorePath(i);
-        ensureLocalStore().repairPath(path);
+        ensureLocalStore()->repairPath(path);
     }
 }
 
@@ -895,7 +897,7 @@ static void opServe(Strings opFlags, Strings opArgs)
                         if (!isDerivation(path)) paths2.insert(path);
                     unsigned long long downloadSize, narSize;
                     PathSet willBuild, willSubstitute, unknown;
-                    queryMissing(*store, PathSet(paths2.begin(), paths2.end()),
+                    store->queryMissing(PathSet(paths2.begin(), paths2.end()),
                         willBuild, willSubstitute, unknown, downloadSize, narSize);
                     /* FIXME: should use ensurePath(), but it only
                        does one path at a time. */
@@ -940,9 +942,9 @@ static void opServe(Strings opFlags, Strings opArgs)
 
             case cmdExportPaths: {
                 bool sign = readInt(in);
-                Paths sorted = topoSortPaths(*store, readStorePaths<PathSet>(in));
+                Paths sorted = store->topoSortPaths(readStorePaths<PathSet>(in));
                 reverse(sorted.begin(), sorted.end());
-                exportPaths(*store, sorted, sign, out);
+                store->exportPaths(sorted, sign, out);
                 break;
             }
 
@@ -987,7 +989,7 @@ static void opServe(Strings opFlags, Strings opArgs)
                 PathSet paths = readStorePaths<PathSet>(in);
                 PathSet closure;
                 for (auto & i : paths)
-                    computeFSClosure(*store, i, closure, false, includeOutputs);
+                    store->computeFSClosure(i, closure, false, includeOutputs);
                 out << closure;
                 break;
             }
@@ -1013,7 +1015,8 @@ static void opGenerateBinaryCacheKey(Strings opFlags, Strings opArgs)
     string publicKeyFile = *i++;
 
 #if HAVE_SODIUM
-    sodium_init();
+    if (sodium_init() == -1)
+        throw Error("could not initialise libsodium");
 
     unsigned char pk[crypto_sign_PUBLICKEYBYTES];
     unsigned char sk[crypto_sign_SECRETKEYBYTES];
diff --git a/src/nix-store/xmlgraph.cc b/src/nix-store/xmlgraph.cc
index 1b3ad3d28ad4..b6e1c1c4b873 100644
--- a/src/nix-store/xmlgraph.cc
+++ b/src/nix-store/xmlgraph.cc
@@ -33,34 +33,34 @@ static string makeNode(const string & id)
 }
 
 
-void printXmlGraph(const PathSet & roots)
+void printXmlGraph(ref<Store> store, const PathSet & roots)
 {
     PathSet workList(roots);
     PathSet doneSet;
 
     cout << "<?xml version='1.0' encoding='utf-8'?>\n"
-	 << "<nix>\n";
+         << "<nix>\n";
 
     while (!workList.empty()) {
-	Path path = *(workList.begin());
-	workList.erase(path);
+        Path path = *(workList.begin());
+        workList.erase(path);
 
-	if (doneSet.find(path) != doneSet.end()) continue;
-	doneSet.insert(path);
+        if (doneSet.find(path) != doneSet.end()) continue;
+        doneSet.insert(path);
 
-	cout << makeNode(path);
+        cout << makeNode(path);
 
-	PathSet references;
-	store->queryReferences(path, references);
+        PathSet references;
+        store->queryReferences(path, references);
 
-	for (PathSet::iterator i = references.begin();
-	     i != references.end(); ++i)
-	{
-	    if (*i != path) {
-		workList.insert(*i);
-		cout << makeEdge(*i, path);
-	    }
-	}
+        for (PathSet::iterator i = references.begin();
+             i != references.end(); ++i)
+        {
+            if (*i != path) {
+                workList.insert(*i);
+                cout << makeEdge(*i, path);
+            }
+        }
 
     }
 
diff --git a/src/nix-store/xmlgraph.hh b/src/nix-store/xmlgraph.hh
index c2216c5a4627..a6e7d4e2805a 100644
--- a/src/nix-store/xmlgraph.hh
+++ b/src/nix-store/xmlgraph.hh
@@ -4,6 +4,8 @@
 
 namespace nix {
 
-void printXmlGraph(const PathSet & roots);
+class Store;
+
+void printXmlGraph(ref<Store> store, const PathSet & roots);
 
 }
diff --git a/tests/dump-db.sh b/tests/dump-db.sh
index 234b7ac02680..57c8c401600d 100644
--- a/tests/dump-db.sh
+++ b/tests/dump-db.sh
@@ -9,6 +9,7 @@ deps="$(nix-store -qR $TEST_ROOT/result)"
 nix-store --dump-db > $TEST_ROOT/dump
 
 rm -rf $NIX_DB_DIR
+mkdir $NIX_DB_DIR
 
 nix-store --load-db < $TEST_ROOT/dump
 
diff --git a/tests/lang/eval-okay-attrs5.nix b/tests/lang/eval-okay-attrs5.nix
index 0a98b8fdffa6..a4584cd3b398 100644
--- a/tests/lang/eval-okay-attrs5.nix
+++ b/tests/lang/eval-okay-attrs5.nix
@@ -15,7 +15,7 @@ in
     as.a.b.c or as.x.y.z
     as.x.y.bla or bs.f-o-o.bar or "xyzzy"
     as.x.y.bla or bs.bar.foo or "xyzzy"
-    123.bla or null.foo or "xyzzy"
+    (123).bla or null.foo or "xyzzy"
     # Backwards compatibility test.
     (fold or [] [true false false])
   ]
diff --git a/tests/lang/eval-okay-fromjson.nix b/tests/lang/eval-okay-fromjson.nix
index 5ed0c1c4395d..102ee82b5e6b 100644
--- a/tests/lang/eval-okay-fromjson.nix
+++ b/tests/lang/eval-okay-fromjson.nix
@@ -12,7 +12,9 @@ builtins.fromJSON
               "Width":  100
           },
           "Animated" : false,
-          "IDs": [116, 943, 234, 38793, true  ,false,null, -100]
+          "IDs": [116, 943, 234, 38793, true  ,false,null, -100],
+          "Latitude":  37.7668,
+          "Longitude": -122.3959
         }
     }
   ''
@@ -28,5 +30,7 @@ builtins.fromJSON
         };
       Animated = false;
       IDs = [ 116 943 234 38793 true false null (0-100) ];
+      Latitude = 37.7668;
+      Longitude = -122.3959;
     };
   }
diff --git a/tests/lang/eval-okay-tojson.exp b/tests/lang/eval-okay-tojson.exp
index e8164af2b66e..33588493f75c 100644
--- a/tests/lang/eval-okay-tojson.exp
+++ b/tests/lang/eval-okay-tojson.exp
@@ -1 +1 @@
-"{\"a\":123,\"b\":-456,\"c\":\"foo\",\"d\":\"foo\\n\\\"bar\\\"\",\"e\":true,\"f\":false,\"g\":[1,2,3],\"h\":[\"a\",[\"b\",{\"foo\\nbar\":{}}]],\"i\":3}"
+"{\"a\":123,\"b\":-456,\"c\":\"foo\",\"d\":\"foo\\n\\\"bar\\\"\",\"e\":true,\"f\":false,\"g\":[1,2,3],\"h\":[\"a\",[\"b\",{\"foo\\nbar\":{}}]],\"i\":3,\"j\":1.44}"
diff --git a/tests/lang/eval-okay-tojson.nix b/tests/lang/eval-okay-tojson.nix
index 0d4e55b3d367..c046ba4ae59b 100644
--- a/tests/lang/eval-okay-tojson.nix
+++ b/tests/lang/eval-okay-tojson.nix
@@ -8,4 +8,5 @@ builtins.toJSON
     g = [ 1 2 3 ];
     h = [ "a" [ "b" { "foo\nbar" = {}; } ] ];
     i = 1 + 2;
+    j = 1.44;
   }
diff --git a/tests/lang/eval-okay-types.exp b/tests/lang/eval-okay-types.exp
index 82487f7100e2..9a8ea0bcbd8a 100644
--- a/tests/lang/eval-okay-types.exp
+++ b/tests/lang/eval-okay-types.exp
@@ -1 +1 @@
-[ true false true false true false true false true false true false "int" "bool" "string" "null" "set" "list" "lambda" "lambda" "lambda" "lambda" ]
+[ true false true false true false true false true true true true true true true true true true true false true false "int" "bool" "string" "null" "set" "list" "lambda" "lambda" "lambda" "lambda" ]
diff --git a/tests/lang/eval-okay-types.nix b/tests/lang/eval-okay-types.nix
index 8cb225e247fb..a34775f5e602 100644
--- a/tests/lang/eval-okay-types.nix
+++ b/tests/lang/eval-okay-types.nix
@@ -8,6 +8,16 @@ with builtins;
   (isString [ "x" ])
   (isInt (1 + 2))
   (isInt { x = 123; })
+  (isInt (1 / 2))
+  (isInt (1 + 1))
+  (isInt (1 / 2))
+  (isInt (1 * 2))
+  (isInt (1 - 2))
+  (isFloat (1.2))
+  (isFloat (1 + 1.0))
+  (isFloat (1 / 2.0))
+  (isFloat (1 * 2.0))
+  (isFloat (1 - 2.0))
   (isBool (true && false))
   (isBool null)
   (isAttrs { x = 123; })
diff --git a/tests/lang/eval-okay-xml.exp.xml b/tests/lang/eval-okay-xml.exp.xml
index f124f939ed48..92b75e0b8b17 100644
--- a/tests/lang/eval-okay-xml.exp.xml
+++ b/tests/lang/eval-okay-xml.exp.xml
@@ -45,5 +45,8 @@
     <attr name="x">
       <int value="123" />
     </attr>
+    <attr name="y">
+      <float value="567.89" />
+    </attr>
   </attrs>
 </expr>
diff --git a/tests/lang/eval-okay-xml.nix b/tests/lang/eval-okay-xml.nix
index b9389bfae759..9ee9f8a0b4f5 100644
--- a/tests/lang/eval-okay-xml.nix
+++ b/tests/lang/eval-okay-xml.nix
@@ -2,6 +2,8 @@ rec {
 
   x = 123;
 
+  y = 567.890;
+
   a = "foo";
 
   b = "bar";
diff --git a/tests/lang/parse-okay-regression-751.nix b/tests/lang/parse-okay-regression-751.nix
new file mode 100644
index 000000000000..05c78b3016d3
--- /dev/null
+++ b/tests/lang/parse-okay-regression-751.nix
@@ -0,0 +1,2 @@
+let const = a: "const"; in
+''${ const { x = "q"; }}''
diff --git a/tests/repair.sh b/tests/repair.sh
index ae82b649c6ac..92f2f8fe60a5 100644
--- a/tests/repair.sh
+++ b/tests/repair.sh
@@ -18,6 +18,18 @@ if nix-store --verify --check-contents -v; then
     exit 1
 fi
 
+# The path can be repaired by rebuilding the derivation.
+nix-store --verify --check-contents --repair
+
+nix-store --verify-path $path2
+
+# Re-corrupt and delete the deriver. Now --verify --repair should
+# not work.
+chmod u+w $path2
+touch $path2/bad
+
+nix-store --delete $(nix-store -qd $path2)
+
 if nix-store --verify --check-contents --repair; then
     echo "nix-store --verify --repair succeeded unexpectedly" >&2
     exit 1
diff --git a/version b/version
index b8162070734f..35d51f33b34f 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-1.11
\ No newline at end of file
+1.12
\ No newline at end of file