diff --git a/.gitignore b/.gitignore
index dd46dfdae0074708e49f18cbac3f8e71bdd81f47..d3d77504cc8a66eafe9c94ecf187cd414d0f7dd0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -62,8 +62,4 @@ third_party/luajit/src/lj_libdef.h
 third_party/luajit/src/lj_recdef.h
 third_party/luajit/src/lj_vm.s
 VERSION
-debian/*.debhelper
-debian/*.log
-debian/*.substvars
-*.cdbs-orig
 src/00000000000000000001.snap
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 424d296f848ee6302457e797b3c7a36e28d69b6c..dc197d86d0b8a4b2f0041da472d3611d1cd864e0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -98,7 +98,7 @@ add_custom_target(tags COMMAND ctags -R -f tags
 #
 set (CPACK_PACKAGE_VERSION_MAJOR "1")
 set (CPACK_PACKAGE_VERSION_MINOR "6")
-set (CPACK_PACKAGE_VERSION_PATCH "2")
+set (CPACK_PACKAGE_VERSION_PATCH "3")
 
 set (PACKAGE_VERSION "")
 
diff --git a/debian/control b/debian/control
index c54bb43cb8fb2355f1167b3300ef5544458bd119..596a94722777c76d0988573bfc2bee0b02be5f4e 100644
--- a/debian/control
+++ b/debian/control
@@ -14,29 +14,30 @@ Homepage: http://tarantool.org/
 VCS-Browser: https://github.com/tarantool/tarantool
 VCS-Git: git://github.com/tarantool/tarantool.git
 
-Package: tarantool-common
-Architecture: all
-Priority: optional
-Depends: ${misc:Depends}, adduser
-Description: Tarantool in-memory database - common files
- Tarantool is an in-memory database and Lua application server.
- This package provides scripts to work with tarantool configuration
- and log files.
+# Package: tarantool-common
+# Architecture: all
+# Priority: optional
+# Depends: ${misc:Depends}, adduser
+# Description: Tarantool in-memory database - common files
+#  Tarantool is an in-memory database and Lua application server.
+#  This package provides scripts to work with tarantool configuration
+#  and log files.
 
 Package: tarantool
 Architecture: i386 amd64 kfreebsd-i386 kfreebsd-amd64 hurd-i386
 Priority: optional
 Depends: ${shlibs:Depends}, ${misc:Depends}
 Conflicts: tarantool-dbg (<< 1.5.2),
- tarantool-common (<< 1.6~),
+#  tarantool-common (<< 1.6~),
  tarantool-client (<< 1.6~),
  tarantool-client-dbg (<< 1.6~),
  tarantool-plugins (<< 1.6~),
  tarantool-mysql-plugin (<< 1.6~),
  tarantool-postgresql-plugin (<< 1.6~),
  libtarantool-dev (<< 1.6~)
-Recommends: tarantool-common (>= 1.6),
- tarantool-modules
+# Recommends: tarantool-common (>= 1.6),
+#  tarantool-modules
+Recommends: tarantool-modules
 Description: In-memory database with Lua application server
  Tarantool is an in-memory database and Lua application server.
  Its key properties include:
diff --git a/debian/rules b/debian/rules
index 5e8c510daea2b8e99a197e1ca1aa3ca1227ce147..4415b71d73686d13b7ec167f2518c9000ec54f19 100755
--- a/debian/rules
+++ b/debian/rules
@@ -67,14 +67,14 @@ install/tarantool::
 #         make -C build-area test-force || /bin/true
 
 
-install/tarantool-common::
-	pod2man -c 'tarantool instances control' \
-	    debian/tarantool_instance.pod > build-area/tarantool_instance.1
-	pod2man -c 'tarantool log rotation' \
-	    debian/scripts/tarantool_logrotate \
-	    	 > build-area/tarantool_logrotate.1
-	pod2man -c 'snapshot rotate' \
-	    debian/scripts/tarantool_snapshot_rotate \
-	    	> build-area/tarantool_snapshot_rotate.1
-	install -m0755 extra/logger.pl \
-	    debian/tarantool-common/usr/lib/tarantool/logger
+# install/tarantool-common::
+# 	pod2man -c 'tarantool instances control' \
+# 	    debian/tarantool_instance.pod > build-area/tarantool_instance.1
+# 	pod2man -c 'tarantool log rotation' \
+# 	    debian/scripts/tarantool_logrotate \
+# 	    	 > build-area/tarantool_logrotate.1
+# 	pod2man -c 'snapshot rotate' \
+# 	    debian/scripts/tarantool_snapshot_rotate \
+# 	    	> build-area/tarantool_snapshot_rotate.1
+# 	install -m0755 extra/logger.pl \
+# 	    debian/tarantool-common/usr/lib/tarantool/logger
diff --git a/doc/user/configuration-reference.xml b/doc/user/configuration-reference.xml
index ca291be9745290de728b7c81c88f3626f8682242..1b8f07d47709f447c64c6cddbb0a1e4f9aebe27e 100644
--- a/doc/user/configuration-reference.xml
+++ b/doc/user/configuration-reference.xml
@@ -95,8 +95,8 @@ The Lua program almost always begins by invoking <code>box.cfg()</code>,
 if the database server will be used or if ports need to be opened.
 For example, suppose <filename>script.lua</filename> contains the lines<programlisting>#!/usr/bin/env tarantool
 box.cfg{
-    primary_port        = os.getenv("PRIMARY_PORT"),
-    admin_port          = os.getenv("ADMIN_PORT"),
+    listen              = os.getenv("LISTEN"),
+    admin               = os.getenv("ADMIN"),
     slab_alloc_arena    = 0.1,
     pid_file            = "tarantool.pid",
     rows_per_wal        = 50
@@ -104,15 +104,15 @@ box.cfg{
 print('Starting ',arg[1])</programlisting>
 and suppose the command line is <code>~/tarantool/src/tarantool script.lua ARG</code>.
 Then the screen might look like this:<programlisting>
-<prompt>$</prompt> <userinput>export PRIMARY_PORT=3303</userinput>
+<prompt>$</prompt> <userinput>export PRIMARY_PORT=3301</userinput>
 <prompt>$</prompt> <userinput>~/tarantool/src/tarantool script.lua ARG</userinput>
 2014-07-02 10:19:29.078 ... version 1.6.0-1471-g663694c
 2014-07-02 10:19:29.078 ... log level 5
 2014-07-02 10:19:29.078 ... mapping 107374184 bytes for a shared arena...
 2014-07-02 10:19:29.086 ... initialized
 2014-07-02 10:19:29.101 ... recovery start
-2014-07-02 10:19:29.107 ...  bound to primary port tcp://0.0.0.0:3303
-2014-07-02 10:19:29.108 ... I am primary
+2014-07-02 10:19:29.107 ...  bound to primary port tcp://0.0.0.0:3301
+2014-07-02 10:19:29.108 ... ready to accept requests
 Starting  ARG
 2014-07-02 10:19:29.108 ... entering the event loop</programlisting>
 </para>
@@ -138,7 +138,7 @@ Starting  ARG
   </para>
   <para>
     To see all the non-null parameters, say <code>box.cfg</code> (no parentheses).
-    To see a particular parameter, for example the primary port, say <code>box.cfg.primary_port</code>.
+    To see a particular parameter, for example the listen address, say <code>box.cfg.listen</code>.
   </para>
   <para>
     The following tables describe all parameters for basic operation,
@@ -212,7 +212,7 @@ Starting  ARG
           <entry>Deprecated. Do not use.</entry>
         </row>
         <row>
-          <entry xml:id="primary_port" xreflabel="primary_port">primary_port</entry>
+          <entry xml:id="primary_port" xreflabel="primary_port">listen</entry>
           <entry>integer or string</entry>
           <entry>null</entry>
           <entry>no</entry>
@@ -220,15 +220,15 @@ Starting  ARG
           Has no default value, so <emphasis
           role="strong">must be specified</emphasis>
           if connections will occur from remote clients
-          that do not use admin_port.
+          that do not use "admin address" (the administrative host and port).
           Note: a replica also binds to this port, and accepts
           connections, but these connections can only serve
           reads until the replica becomes a master.
-          A typical value is 3303.</entry>
+          A typical value is 3301.</entry>
         </row>
 
         <row>
-          <entry xml:id="admin_port" xreflabel="admin_port">admin_port</entry>
+          <entry xml:id="admin_port" xreflabel="admin_port">admin</entry>
           <entry>integer or string</entry>
           <entry>null</entry>
           <entry>no</entry>
@@ -266,11 +266,11 @@ Starting  ARG
              ordinarily <command>ps</command> shows the Tarantool server process thus:
             </para>
 <programlisting>kostja@shmita:~$ ps -a -o command | grep box
-tarantool: primary pri: 3303 adm: 33135</programlisting>
+tarantool: primary pri: 3301 adm: 3313</programlisting>
           <para>But if the configuration file contains custom_proc_title=sessions then
           the output looks like:</para>
 <programlisting>kostja@shmita:~$ ps -a -o command | grep box
-tarantool: primary@sessions pri: 3303 adm: 3313</programlisting>
+tarantool: primary@sessions pri: 3301 adm: 3313</programlisting>
           </entry>
         </row>
 
@@ -472,7 +472,7 @@ tarantool: primary@sessions pri: 3303 adm: 3313</programlisting>
           For example, if replication_source = "1.2.3.4:55555" then
           the replica server tries to connect to 1.2.3.4 port 55555.
           A replica server does not accept updates
-          on <olink targetptr="primary_port"/>. This parameter is
+          on <olink targetptr="primary_port">listen</olink>. This parameter is
           dynamic, that is, to enter master mode, simply set
           replication_source to an empty string and issue
           "box.cfg{replication_source=new-value}".</entry>
@@ -643,8 +643,8 @@ tarantool: primary@sessions pri: 3303 adm: 3313</programlisting>
           instances of the server using the same configuration file.
           The first one to start will be the "primary" instance.
           The second one to start will be the "standby" instance.
-          The standby instance will initialize and will try to connect on primary_port
-          and admin_port, but will fail because the
+          The standby instance will initialize and will try to connect on listen
+          address and admin address, but will fail because the
           primary instance has already taken them.
           So the standby instance goes into a loop, reading the write
           ahead log which the primary instance is writing (so the
diff --git a/doc/user/connectors.xml b/doc/user/connectors.xml
index 44e2617814447bcbbe983ac3dbbf7b75caae1186..17031d023e3f6f7a780945da46ec6c6e1086cac6 100644
--- a/doc/user/connectors.xml
+++ b/doc/user/connectors.xml
@@ -113,10 +113,11 @@ And that is why APIs exist for drivers for C, Perl, Python, PHP, Ruby, and so on
     the necessary file <filename>tp.h</filename>, and the default library path contains
     the directory where Tarantool library files were placed at installation time.
     Before trying to run, check that the server
-    (tarantool) is running on localhost (127.0.0.1) and its primary port is the default (3303) and
+    (tarantool) is running on localhost (127.0.0.1) and its listen address is the default
+    (local host, port 3301) and
     space[513]'s primary key type is numeric (space[513].index[0].key_field[1].type = "NUM").
     To run, say <code>./example</code>.
-    The program will open a socket connection with the tarantool server at localhost:3303,
+    The program will open a socket connection with the tarantool server at localhost:3301,
     then format a buffer for sending an INSERT request, then send the request, then check if the
     server returned an error, then &mdash; if all is well &mdash; print "Insert succeeded". If the
     row already exists, the program will print <quote>Duplicate key exists in unique index 0</quote>.
@@ -136,10 +137,10 @@ int main()
   struct sockaddr_in sock;                                     /* the usual socket address info */
   if ((fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) &lt;= 0)   /* open the socket. abort if failure */
     exit(1);
-  memset(&amp;sock, 0, sizeof(sock));                              /* connect to localhost:3303 */
+  memset(&amp;sock, 0, sizeof(sock));                              /* connect to localhost:3301 */
   sock.sin_family = AF_INET;
   sock.sin_addr.s_addr = inet_addr("127.0.0.1");
-  sock.sin_port = htons(3303);
+  sock.sin_port = htons(3301);
   if (connect(fd, (struct sockaddr *)&amp;sock, sizeof(sock)) &lt; 0) /* connect, abort if failure */
     exit(1);
   const int greeting_buffer_size = 128;                        /* handle the server's greeting */
@@ -219,14 +220,15 @@ sudo cpan install DR::Tarantool
     <para>
     Here is a complete Perl program that inserts [99999,'BB'] into space[0] via the Perl API.
     Before trying to run, check that the server
-    (tarantool) is running on localhost (127.0.0.1) and its primary port is the default (3303) and
+    (tarantool) is running on localhost (127.0.0.1) and its listen address is the default
+    (local host, port 3301) and
     space[0]'s primary key type is numeric (space[0].index[0].key_field[1].type = "NUM" in configuration file).
     To run, paste the code into a file named example.pl and say <code>perl example.pl</code>.
     The program will connect using an application-specific definition of the space.
     The program will open a socket connection
-    with the tarantool server at localhost:3303, then send an INSERT request,
+    with the tarantool server at localhost:3301, then send an INSERT request,
     then &mdash; if all is well &mdash; end without displaying any messages.
-    If tarantool is not running on localhost with primary port = 3303, the program will print
+    If tarantool is not running on localhost with listen address = port 3301, the program will print
     <quote>Connection refused</quote>.
     </para>
     <para>
@@ -237,7 +239,7 @@ use DR::Tarantool ':all';
 
 my $tnt = tarantool
   host    =&gt; '127.0.0.1',                      # look for tarantool on localhost
-  port    =&gt; 3303,                             # assume tarantool primary port = default
+  port    =&gt; 3301,                             # assume tarantool listen address = default
   spaces  =&gt; {
     0 =&gt; {                                     # definition of space[0] ...
       name =&gt; 't0',                            #   space[0] name = 't0'
@@ -295,18 +297,19 @@ export PHP_INI_SCAN_DIR=~/tarantool-php/test/share
     <para>
     Here is a complete PHP program that inserts [99999,'BB'] into space[0] via the PHP API.
     Before trying to run, check that the server
-    (tarantool) is running on localhost (127.0.0.1) and its primary port is the default (3303) and
+    (tarantool) is running on localhost (127.0.0.1) and its listen address is the default
+    (local host, port 3301) and
     space[0]'s primary key type is numeric (space[0].index[0].key_field[1].type = "NUM" in configuration file).
     To run, paste the code into a file named example.php and say <code>php example.php</code>.
     The program will open a socket connection
-    with the tarantool server at localhost:3303, then send an INSERT request,
+    with the tarantool server at localhost:3301, then send an INSERT request,
     then &mdash; if all is well &mdash; print "Insert succeeded".
     If the tuple already exists, the program will print <quote>Duplicate key exists in unique index 0</quote>.
     </para>
     <para>
     <programlisting>
 &lt;?php
-$tarantool = new Tarantool("localhost", 3303, 33015);
+$tarantool = new Tarantool("localhost", 3301, 3313);
 try {
   $tarantool-&gt;insert(0, array(99999, "BB"), TARANTOOL_FLAGS_ADD);
   print "Insert succeeded\n";
@@ -333,11 +336,11 @@ catch (Exception $e) {
     <userinput><code>pip install tarantool</code></userinput> to install in <filename>/usr</filename> (requires root privilege)
     or
     <userinput><code>pip install tarantool --user</code></userinput> to install in <filename>~</filename> i.e. user's default directory.
-    The program is assuming that the server (tarantool) is running on localhost (127.0.0.1) and its primary port is
-    the default (3303) and space99's primary key type is string (box.space.space99.index['primary'].key_field[1].type = "STR")
+    The program is assuming that the server (tarantool) is running on localhost (127.0.0.1) and its listen address is
+    the default (local host, port 3301) and space99's primary key type is string (box.space.space99.index['primary'].key_field[1].type = "STR")
     and user 'guest' has permission to read and write on space99. An administrator could fulfill all those conditions by
     starting the tarantool server and executing these requests:<programlisting>
-box.cfg{primary_port = 3303}
+box.cfg{listen = 3301}
 box.schema.create_space('space99') 
 box.space.space99:create_index('primary',{parts = {1,'STR'}})
 box.schema.user.grant('guest', 'read', 'space', '_space')
@@ -350,7 +353,7 @@ box.schema.user.grant('guest', 'read,write', 'space', 'space99')</programlisting
 #!/usr/bin/python
 from tarantool import Connection
 
-c = Connection("127.0.0.1", 3303)
+c = Connection("127.0.0.1", 3301)
 result = c.insert("space99",('First Tuple','Value', 'Value'))
 print result
 </programlisting>
diff --git a/doc/user/databases.xml b/doc/user/databases.xml
index 57477de80f947a7de44b30630dc137a8e5fad50d..1911115ac7caa6d2ab7222c9d78cb2fc21860f1f 100644
--- a/doc/user/databases.xml
+++ b/doc/user/databases.xml
@@ -90,7 +90,7 @@
     The contents of the <code>box</code> library can be inspected at runtime with
     <code>box</code>, with no arguments.
     The packages inside the box library are: schema, box.tuple, box.space, box.index, 
-    box.net.box, box.cfg, box.info, box.slab, box.stat.
+    net.box, box.cfg, box.info, box.slab, box.stat.
     Every package contains one or more Lua functions. A few packages contain members as well as functions.
     The functions allow data definition (create alter drop), data manipulation (insert delete update select replace),
     and introspection (inspecting contents of spaces, accessing server configuration).
@@ -1188,7 +1188,7 @@ session.delimiter('')!
     <varlistentry>
         <term>
              <emphasis role="lua" xml:id="boxindexiterator" xreflabel="box.index.iterator(type, ...)">
-            box.space.<replaceable>space-name</replaceable>.index.<replaceable>index-name</replaceable>]:iterator(<replaceable>iterator-type, bitset-value | field-value...</replaceable>)</emphasis>
+            box.space.<replaceable>space-name</replaceable>.index[.<replaceable>index-name</replaceable>]:pairs(<replaceable>bitset-value | field-value..., iterator-type</replaceable>)</emphasis>
         </term>
         <listitem>
             <para>
@@ -1298,7 +1298,7 @@ session.delimiter('')!
     <varlistentry>
      <term>
              <emphasis role="lua" xml:id="box.index.iterator" xreflabel="box.index.select(type, ...)">
-            box.space.<replaceable>space-name</replaceable>.index.<replaceable>index-name</replaceable>]:select(<replaceable>{fields, {parameters}]</replaceable>)</emphasis>
+            box.space.<replaceable>space-name</replaceable>[.index.<replaceable>index-name</replaceable>]:select(<replaceable>{fields, {parameters}]</replaceable>)</emphasis>
         </term>
         <listitem>
             <para>
@@ -1334,9 +1334,15 @@ The result will be a table of tuples and will look like this:
 ...
 </programlisting>
             </para>
+            <para>
+            Note: <code>[.index.<replaceable>index-name</replaceable>]</code> is optional. If it is
+            omitted, then the assumed index is the first (primary-key) index. Therefore, for
+            the example above, <code>box.space.tester:select(1, {iterator = 'GT'})</code>
+            would have returned the same two rows, via the 'primary' index.
+            </para>
         </listitem>
     </varlistentry>
-    
+
     <varlistentry>
         <term>
             <emphasis role="lua">box.space.<replaceable>space-name</replaceable>.index.<replaceable>index-name</replaceable>:min([key])</emphasis>
@@ -2035,287 +2041,6 @@ session.delimiter('')!
 
 <!--   end of lib -->
 
-<section xml:id="sp-box-net-box">
-    <title>Package <code>box.net.box</code> &mdash; working with networked Tarantool peers</title>
-    <para>
-        The <code>box.net</code> package contains connectors to remote database systems.
-        One variant, <code>box.net.sql</code>, is for connecting to MySQL or MariaDB or PostgreSQL
-        &mdash; that variant is the subject of the <quote>SQL DBMS plugins</quote> appendix.
-        In this section the subject is the built-in variant, <code>box.net.box</code>.
-        This is for connecting to tarantool servers via a network.
-    </para>
-<variablelist xml:id="box.net.box">
-    <para>
-        Call <code>box.net.box.new()</code> to connect and get a connection object,
-        which will be called <code>conn</code> for examples in this section.
-        Call the other <code>box.net.box()</code> routines, passing <code>conn</code>,
-        to execute requests on the remote box.
-        Call <code>box.net.box.close(conn)</code> to disconnect.
-        Object-oriented and functional APIs are equivalent, so
-        <code>conn:close()</code> is the same as <code>box.net.box.close(conn)</code>.
-    </para>
-
-    <para>
-        All <code>box.net.box</code> methods are fiber-safe, that is, it is
-        safe to share and use the same connection object across
-        multiple concurrent fibers. In fact, it's perhaps the
-        best programming practice with Tarantool. When multiple
-        fibers use the same connection, all requests are pipelined
-        through the same network socket, but each fiber gets back a
-        correct response. Reducing the number of active sockets
-        lowers the overhead of system calls and increases the
-        overall server performance. There are, however, cases when
-        a single connection is not enough &mdash; for example when it's necessary to
-        prioritize requests or to use different authentication ids.
-    </para>
-
-    <varlistentry>
-        <term>
-            <emphasis role="lua" xml:id="box.net.box.new">
-             conn = box.net.box.new(<replaceable>host</replaceable>, <replaceable>port</replaceable> [, <replaceable>reconnect_interval</replaceable>])</emphasis>
-        </term>
-        <listitem>
-            <para>
-                Create a new connection. The connection is
-                established on demand, at the time of the first
-                request. It is re-established automatically after
-                a disconnect. The argument
-                <code>reconnect_interval</code> (in seconds)
-                specifies the amount of time the server
-                sleeps between failing attempts to reconnect.
-                The returned <code>conn</code> object supports methods for making remote
-                requests, such as select, update or delete.
-            </para>
-            <para>
-               For the local tarantool server there is a pre-created always-established
-               connection object named <code>box.net.self</code>.
-               Its purpose is to make polymorphic use of the
-               <code>box.net.box</code> API easier. Therefore
-               <code>conn = box.net.box.new('localhost', 3303)</code> can
-               be replaced by <code>conn = box.net.box.self</code>.
-               However, there is an important difference between the embedded
-               connection and a remote one. With the embedded connection,
-               requests which do not modify data do not yield. When using
-               a remote connection, any request can yield, and local database state may
-               have changed by the time it returns.
-            </para>
-            <para>
-               Parameters: <code>host</code>, <code>port</code>, <code>reconnect_interval</code>.
-            </para>
-            <para>
-               Returns: (type = userdata) conn object).
-            </para>
-            <para>
-               Example: <code>conn = box.net.box.new('localhost', 3303)</code>.
-            </para>
-        </listitem>
-    </varlistentry>
-
-    <varlistentry>
-        <term><emphasis role="lua" xml:id="box.net.box.ping">
-        conn:ping()</emphasis></term>
-        <listitem>
-            <para>
-                Execute a PING command.
-            </para>
-            <para>
-               Returns: (type = boolean) <code>true</code> on success,
-                 <code>false</code> on error. Example: <code>self:ping()</code>.
-            </para>
-        </listitem>
-    </varlistentry>
-
-    <varlistentry>
-        <term><emphasis role="lua" xml:id="box.net.box.close">
-        conn:close()</emphasis></term>
-        <listitem>
-            <para>
-            Close a connection.
-            </para>
-            <para>
-              Connection objects are garbage collected just like any other objects
-              in Lua, so an explicit destruction is not mandatory.
-              However, since <code>close()</code> is a system call, it
-              is good programming practice to close a connection
-              explicitly when it is no longer needed, to avoid lengthy
-              stalls of the garbage collector.
-            </para>
-            <para>
-              Example: <code>conn:close()</code>.
-            </para>
-        </listitem>
-    </varlistentry>
-
-    <varlistentry>
-        <term><emphasis role="lua" xml:id="box.net.box.select">
-         conn:select(<replaceable>space-number</replaceable>, ...)</emphasis></term>
-        <listitem>
-            <para>
-              <code>conn:select(<replaceable>space-number</replaceable>, ...)</code> is the remote-call equivalent of the local call
-              <code xlink:href="#box.select">box.space[<replaceable>space-number</replaceable>]:select(...)</code>.
-              Please note this difference: a local <code>box.space[<replaceable>space-number</replaceable>]:select(...)</code> does not yield,
-              but a remote <code>conn:select(<replaceable>space-number</replaceable>, ...)</code> call does yield,
-              so local data may change while a remote <code>conn:select(<replaceable>space-number</replaceable>, ...)</code> is running.
-            </para>
-        </listitem>
-    </varlistentry>
-
-    <varlistentry>
-        <term><emphasis role="lua" xml:id="box.net.box.insert">
-         conn:insert(<replaceable>space-number</replaceable>, ...)</emphasis></term>
-        <listitem>
-            <para>
-              <code>conn:insert(<replaceable>space-number</replaceable>, ...)</code> is the remote-call equivalent of the local call
-              <code xlink:href="#box.insert">box.space[<replaceable>space-number</replaceable>]:insert(...)</code>.
-            </para>
-        </listitem>
-    </varlistentry>
-
-    <varlistentry>
-        <term><emphasis role="lua" xml:id="box.net.box.replace">
-         conn:replace(<replaceable>space-number</replaceable>, ...)</emphasis></term>
-        <listitem>
-            <para>
-              <code>conn:replace(<replaceable>space-number</replaceable>, ...)</code> is the remote-call equivalent of the local call
-              <code xlink:href="#box.replace">box.space[<replaceable>space-number</replaceable>]:replace(...)</code>.
-            </para>
-        </listitem>
-    </varlistentry>
-
-    <varlistentry>
-        <term><emphasis role="lua" xml:id="box.net.box.update">
-         conn:update(<replaceable>space-number</replaceable>, <replaceable>key</replaceable>, <replaceable>format</replaceable>, ...)</emphasis></term>
-        <listitem>
-            <para>
-              <code>conn:update(<replaceable>space-number</replaceable>, ...)</code> is the remote-call equivalent of the local call
-              <code xlink:href="#box.update">box.space[<replaceable>space-number</replaceable>]:update(...)</code>.
-            </para>
-        </listitem>
-    </varlistentry>
-
-    <varlistentry>
-        <term><emphasis role="lua" xml:id="box.net.box.delete">
-         conn:delete(<replaceable>space-number</replaceable>, ...)</emphasis></term>
-        <listitem>
-            <para>
-              <code>conn:delete(<replaceable>space-number</replaceable>, ...)</code> is the remote-call equivalent of the local call
-              <code xlink:href="#box.delete">box.space[<replaceable>space-number</replaceable>]:delete(...)</code>.
-            </para>
-        </listitem>
-    </varlistentry>
-
-    <varlistentry>
-        <term><emphasis role="lua" xml:id="box.net.box.call">
-         conn:call(<replaceable>function-name</replaceable> [, <replaceable>arguments</replaceable>])</emphasis></term>
-        <listitem>
-            <para>
-              <code>conn:call('func', '1', '2', '3')</code> is the remote-call equivalent of <code>func('1', '2', '3')</code>.
-              That is, box.net.box.call is a remote stored-procedure call.
-              Please keep in mind that the call is using
-              the binary protocol to pack procedure arguments,
-              and the binary protocol is type agnostic, so it's recommended
-              to pass all arguments of remote stored procedure calls as
-              strings.
-            </para>
-            <para>
-              Example: <code>conn:call('box.space.tester:insert',{2})</code>.
-            </para>
-        </listitem>
-    </varlistentry>
-
-    <varlistentry>
-        <term><emphasis role="lua" xml:id="box.net.box.timeout">
-         conn:timeout(<replaceable>timeout</replaceable>)</emphasis></term>
-        <listitem>
-            <para>
-              <code>timeout(...)</code> is a wrapper which sets a timeout for the request that follows it.
-              Example: <code>conn:timeout(0.5):update({'1'}, {{'=p', 1, 15}})</code>.
-            </para>
-            <para>
-              All remote calls support execution timeouts.
-              Using a wrapper object makes the remote
-              connection API compatible with the local one, removing
-              the need for a separate <code>timeout</code> argument, which
-              the local version would ignore. Once a request is sent,
-              it cannot be revoked from the remote server even if
-              a timeout expires: the timeout expiration only aborts the
-              wait for the remote server response, not the request itself.
-            </para>
-        </listitem>
-    </varlistentry>
-</variablelist>
-
- <bridgehead renderas="sect4">Example showing use of most of the box.net.box methods</bridgehead>
- <para>
- This example will work with the sandbox configuration described in the preface.
- That is, there is a space named tester with a numeric primary key.
- Assume that the database is nearly empty.
- Assume that the tarantool server is running on localhost 127.0.0.1:3303.
-<programlisting>
-<prompt>tarantool&gt;</prompt><userinput> ta = {}</userinput>
----
-...
-<prompt>tarantool&gt;</prompt><userinput> box.schema.user.grant('guest', 'read,write,execute', 'space', 'tester')</userinput>
-<prompt>tarantool&gt;</prompt><userinput> session = require('session'); session.delimiter('!')</userinput>
-<prompt>tarantool&gt;</prompt><userinput> function example()</userinput>
-<prompt>        -&gt;</prompt><userinput>     if box.net.self:ping() then</userinput>
-<prompt>        -&gt;</prompt><userinput>       table.insert(ta, 'self:ping() succeeded')</userinput>
-<prompt>        -&gt;</prompt><userinput>       table.insert(ta, '  (no surprise -- self connection is pre-established)')</userinput>
-<prompt>        -&gt;</prompt><userinput>       end</userinput>
-<prompt>        -&gt;</prompt><userinput>     if box.cfg.primary_port == 3303 then</userinput>
-<prompt>        -&gt;</prompt><userinput>       table.insert(ta,'The local server primary port number = 3303')</userinput>
-<prompt>        -&gt;</prompt><userinput>     else</userinput>
-<prompt>        -&gt;</prompt><userinput>       table.insert(ta, 'The local server primary port number is not 3303')</userinput>
-<prompt>        -&gt;</prompt><userinput>       table.insert(ta, '(  (maybe box.cfg{...primary_port=3303...} was not stated)')</userinput>
-<prompt>        -&gt;</prompt><userinput>       table.insert(ta, '(  (so connect will fail)')</userinput>
-<prompt>        -&gt;</prompt><userinput>       end</userinput>
-<prompt>        -&gt;</prompt><userinput>     conn = box.net.box.new('127.0.0.1', 3303)</userinput>
-<prompt>        -&gt;</prompt><userinput>     conn:delete(box.space.tester.id, 800)</userinput>
-<prompt>        -&gt;</prompt><userinput>     table.insert(ta, 'conn:delete done on tester.')</userinput>
-<prompt>        -&gt;</prompt><userinput>     conn:insert(box.space.tester.id, {800, 'data'})</userinput>
-<prompt>        -&gt;</prompt><userinput>     table.insert(ta, 'conn:insert done on tester, index 0')</userinput>
-<prompt>        -&gt;</prompt><userinput>     table.insert(ta, '  primary key value = 800.')</userinput>
-<prompt>        -&gt;</prompt><userinput>     wtuple = conn:select(box.space.tester.id, 800)</userinput>
-<prompt>        -&gt;</prompt><userinput>     table.insert(ta, 'conn:select done on tester, index 0')</userinput>
-<prompt>        -&gt;</prompt><userinput>     table.insert(ta, '  number of fields = ' .. #wtuple)</userinput>
-<prompt>        -&gt;</prompt><userinput>     conn:delete(box.space.tester.id, 800)</userinput>
-<prompt>        -&gt;</prompt><userinput>     table.insert(ta, 'conn:delete done on tester')</userinput>
-<prompt>        -&gt;</prompt><userinput>     conn:replace(box.space.tester.id, {800, 'New data', 'Extra data'})</userinput>
-<prompt>        -&gt;</prompt><userinput>     table.insert(ta, 'conn:replace done on tester')</userinput>
-<prompt>        -&gt;</prompt><userinput>     conn:timeout(1):update(box.space.tester.id, {800}, {{'=p', 1, 'Fld#1'}})</userinput>
-<prompt>        -&gt;</prompt><userinput>     table.insert(ta, 'conn:update done on tester')</userinput>
-<prompt>        -&gt;</prompt><userinput>     conn:close()</userinput>
-<prompt>        -&gt;</prompt><userinput>     table.insert(ta, 'conn:close done')</userinput>
-<prompt>        -&gt;</prompt><userinput>   end!</userinput>
----
-...
-<prompt>tarantool&gt;</prompt><userinput> session.delimiter('')!</userinput>
-<prompt>tarantool&gt;</prompt><userinput> example()</userinput>
----
-...
-<prompt>tarantool&gt;</prompt><userinput> ta</userinput>
----
-- - self:ping() succeeded
-  - '  (no surprise -- self connection is pre-established)'
-  - The local server primary port number = 3303 = default
-  - conn:delete done on tester.
-  - conn:insert done on tester, index 0
-  - '  primary key value = 800.'
-  - conn:select done on tester, index 0
-  - '  number of fields = 2'
-  - conn:delete done on tester
-  - conn:replace done on tester
-  - conn:update done on tester
-  - conn:close done
-...
-<prompt>tarantool&gt;</prompt><userinput> box.space.tester:select(800) -- Prove that the update succeeded.</userinput>
----
-- [800, 'Fld#1', 'Extra data']
-...
-</programlisting>
-</para>
-</section>
-
 <section xml:id="sp-box-cfg">
     <title>Packages <code>box.cfg</code>,
     <code>box.info</code>, <code>box.slab</code> and
@@ -2567,6 +2292,288 @@ tarantool> <userinput>box.stat().DELETE -- a selected item of the table</userinp
 </para>
 </section>
 
+<section xml:id="sp-net-box">
+    <title>Package <code>net.box</code> &mdash; working with networked Tarantool peers</title>
+    <para>
+        The <code>net.box</code> package contains connectors to remote database systems.
+        One variant, <code>box.net.sql</code>, is for connecting to MySQL or MariaDB or PostgreSQL
+        &mdash; that variant is the subject of the <olink targetptr="plugins"><quote>SQL DBMS plugins</quote></olink> appendix.
+        In this section the subject is the built-in variant, <code>box.net</code>.
+        This is for connecting to tarantool servers via a network.
+    </para>
+<variablelist xml:id="net.box">
+    <para>
+        Call <code>require('net.box')</code> to get a net.box object, which will be called <code>net_box</code>
+        for examples in this section.
+        Call <code><replaceable>net_box</replaceable>:new()</code> to connect and get a connection object,
+        which will be called <code>conn</code> for examples in this section.
+        Call the other <code>net.box()</code> routines, passing <code>conn:</code>,
+        to execute requests on the remote box.
+        Call <code>conn:close</code> to disconnect.
+    </para>
+
+    <para>
+        All <code>net.box</code> methods are fiber-safe, that is, it is
+        safe to share and use the same connection object across
+        multiple concurrent fibers. In fact, it's perhaps the
+        best programming practice with Tarantool. When multiple
+        fibers use the same connection, all requests are pipelined
+        through the same network socket, but each fiber gets back a
+        correct response. Reducing the number of active sockets
+        lowers the overhead of system calls and increases the
+        overall server performance. There are, however, cases when
+        a single connection is not enough &mdash; for example when it's necessary to
+        prioritize requests or to use different authentication ids.
+    </para>
+
+    <varlistentry>
+        <term>
+            <emphasis role="lua" xml:id="net.box.new">
+             conn = <replaceable>net_box</replaceable>:new(<replaceable>host</replaceable>, <replaceable>port</replaceable> [, {<replaceable>other parameter[s]</replaceable>}])</emphasis>
+        </term>
+        <listitem>
+            <para>
+                Create a new connection. The connection is
+                established on demand, at the time of the first
+                request. It is re-established automatically after
+                a disconnect.
+                The returned <code>conn</code> object supports methods for making remote
+                requests, such as select, update or delete.
+            </para>
+            <para>
+               For the local tarantool server there is a pre-created always-established
+               connection object named <code><replaceable>net_box</replaceable>.self</code>.
+               Its purpose is to make polymorphic use of the
+               <code>net.box</code> API easier. Therefore
+               <code>conn = <replaceable>net_box</replaceable>:new('localhost', 3301)</code> can
+               be replaced by <code>conn = <replaceable>net.box</replaceable>.self</code>.
+               However, there is an important difference between the embedded
+               connection and a remote one. With the embedded connection,
+               requests which do not modify data do not yield. When using
+               a remote connection, any request can yield, and local database state may
+               have changed by the time it returns.
+            </para>
+            <para>
+             Parameters: <code>host</code>, <code>port</code>, <code>wait_connect</code>, <code>user</code>, <code>password</code>.
+            </para>
+            <para>
+               Returns: (type = userdata) conn object).
+            </para>
+            <para>
+               Examples: <code>conn = net_box:new('localhost', 3301)</code>,
+               <code>conn = net_box:new('127.0.0.1', box.cfg.listen, {wait_connect = false, user = 'guest', password = ''})</code>.
+            </para>
+        </listitem>
+    </varlistentry>
+
+    <varlistentry>
+        <term><emphasis role="lua" xml:id="net.box.ping">
+        conn:ping()</emphasis></term>
+        <listitem>
+            <para>
+                Execute a PING command.
+            </para>
+            <para>
+               Returns: (type = boolean) <code>true</code> on success,
+               <code>false</code> on error. Example: <code>net_box.self:ping()</code>.
+            </para>
+        </listitem>
+    </varlistentry>
+
+    <varlistentry>
+        <term><emphasis role="lua" xml:id="net.box.close">
+        conn:close()</emphasis></term>
+        <listitem>
+            <para>
+            Close a connection.
+            </para>
+            <para>
+              Connection objects are garbage collected just like any other objects
+              in Lua, so an explicit destruction is not mandatory.
+              However, since <code>close()</code> is a system call, it
+              is good programming practice to close a connection
+              explicitly when it is no longer needed, to avoid lengthy
+              stalls of the garbage collector.
+            </para>
+            <para>
+              Example: <code>conn:close()</code>.
+            </para>
+        </listitem>
+    </varlistentry>
+
+    <varlistentry>
+        <term><emphasis role="lua" xml:id="net.box.select">
+         conn.space.<replaceable>space-name</replaceable>:select(<replaceable>field-value</replaceable>, ...)</emphasis></term>
+        <listitem>
+            <para>
+              <code>conn.space.<replaceable>space-name</replaceable>:select(...)</code> is the remote-call equivalent of the local call
+              <code xlink:href="#box.select">box.space.<replaceable>space-name</replaceable>:select(...)</code>.
+              Please note this difference: a local <code>box.space.<replaceable>space-name</replaceable>:select(...)</code> does not yield,
+              but a remote <code>conn.space.<replaceable>space-name</replaceable>:select(...)</code> call does yield,
+              so local data may change while a remote <code>conn.space.<replaceable>space-name</replaceable>:select(...)</code> is running.
+            </para>
+        </listitem>
+    </varlistentry>
+
+    <varlistentry>
+        <term><emphasis role="lua" xml:id="net.box.insert">
+         conn.space.<replaceable>space-name</replaceable>:insert{field-value, ...}</emphasis></term>
+        <listitem>
+            <para>
+             <code>conn.space.<replaceable>space-name</replaceable>:insert(...)</code> is the remote-call equivalent of the local call
+             <code xlink:href="#box.insert">box.space.<replaceable>space-name</replaceable>:insert(...)</code>.
+            </para>
+        </listitem>
+    </varlistentry>
+
+    <varlistentry>
+        <term><emphasis role="lua" xml:id="net.box.replace">
+         conn.space.<replaceable>space-name</replaceable>:replace{field-value, ...}</emphasis></term>
+        <listitem>
+            <para>
+              <code>conn.space.<replaceable>space-name</replaceable>:replace(...)</code> is the remote-call equivalent of the local call
+              <code xlink:href="#box.replace">box.space.<replaceable>space-name</replaceable>:replace(...)</code>.
+            </para>
+        </listitem>
+    </varlistentry>
+
+    <varlistentry>
+        <term><emphasis role="lua" xml:id="net.box.update">
+         conn.space.<replaceable>space-name</replaceable>:update(<replaceable>key</replaceable>, <replaceable>format</replaceable>, ...)</emphasis></term>
+        <listitem>
+            <para>
+             <code>conn.space.<replaceable>space-name</replaceable>:update(...)</code> is the remote-call equivalent of the local call
+              <code xlink:href="#box.update">box.space.<replaceable>space-name</replaceable>:update(...)</code>.
+            </para>
+        </listitem>
+    </varlistentry>
+
+    <varlistentry>
+        <term><emphasis role="lua" xml:id="net.box.delete">
+         conn.space.<replaceable>space-name</replaceable>:delete{key}</emphasis></term>
+        <listitem>
+            <para>
+              <code>conn.space.<replaceable>space-name</replaceable>:delete{...}</code> is the remote-call equivalent of the local call
+              <code xlink:href="#box.delete">box.space.<replaceable>space-name</replaceable>:delete{...}</code>.
+            </para>
+        </listitem>
+    </varlistentry>
+
+    <varlistentry>
+        <term><emphasis role="lua" xml:id="net.box.call">
+         conn:call(<replaceable>function-name</replaceable> [, <replaceable>arguments</replaceable>])</emphasis></term>
+        <listitem>
+            <para>
+              <code>conn:call('func', '1', '2', '3')</code> is the remote-call equivalent of <code>func('1', '2', '3')</code>.
+              That is, conn:call is a remote stored-procedure call.
+            </para>
+            <para>
+              Example: <code>conn:call('box.space.tester:insert',{2})</code>.
+            </para>
+        </listitem>
+    </varlistentry>
+
+    <varlistentry>
+        <term><emphasis role="lua" xml:id="net.box.timeout">
+         conn:timeout(<replaceable>timeout</replaceable>)</emphasis></term>
+        <listitem>
+            <para>
+              <code>timeout(...)</code> is a wrapper which sets a timeout for the request that follows it.
+              Example: <code>conn:timeout(0.5).space.tester:update({1}, {{'=', 2, 15}})</code>.
+            </para>
+            <para>
+              All remote calls support execution timeouts.
+              Using a wrapper object makes the remote
+              connection API compatible with the local one, removing
+              the need for a separate <code>timeout</code> argument, which
+              the local version would ignore. Once a request is sent,
+              it cannot be revoked from the remote server even if
+              a timeout expires: the timeout expiration only aborts the
+              wait for the remote server response, not the request itself.
+            </para>
+        </listitem>
+    </varlistentry>
+</variablelist>
+
+ <bridgehead renderas="sect4">Example showing use of most of the net.box methods</bridgehead>
+ <para>
+ This example will work with the sandbox configuration described in the preface.
+ That is, there is a space named tester with a numeric primary key.
+ Assume that the database is nearly empty.
+ Assume that the tarantool server is running on localhost 127.0.0.1:3301.
+<programlisting>
+<prompt>tarantool&gt;</prompt><userinput> box.schema.user.grant('guest', 'read,write,execute', 'universe')</userinput>
+---
+...
+<prompt>tarantool&gt;</prompt><userinput> session = require('session'); session.delimiter('!')</userinput>
+---
+...
+<prompt>tarantool&gt;</prompt><userinput> net_box = require('net.box')!</userinput>
+---
+...
+<prompt>tarantool&gt;</prompt><userinput> function example()</userinput>
+<prompt>         &gt;</prompt><userinput> if net_box.self:ping() then</userinput>
+<prompt>         &gt;</prompt><userinput>   table.insert(ta, 'self:ping() succeeded')</userinput>
+<prompt>         &gt;</prompt><userinput>   table.insert(ta, '  (no surprise -- self connection is pre-established)')</userinput>
+<prompt>         &gt;</prompt><userinput> end</userinput>
+<prompt>         &gt;</prompt><userinput> if box.cfg.listen == '3301' then</userinput>
+<prompt>         &gt;</prompt><userinput>   table.insert(ta,'The local server listen address = 3301')</userinput>
+<prompt>         &gt;</prompt><userinput> else</userinput>
+<prompt>         &gt;</prompt><userinput>   table.insert(ta, 'The local server listen address is not 3301')</userinput>
+<prompt>         &gt;</prompt><userinput>   table.insert(ta, '(  (maybe box.cfg{...listen="3301"...} was not stated)')</userinput>
+<prompt>         &gt;</prompt><userinput>   table.insert(ta, '(  (so connect will fail)')</userinput>
+<prompt>         &gt;</prompt><userinput> end</userinput>
+<prompt>         &gt;</prompt><userinput> conn = net_box:new('127.0.0.1', 3301)</userinput>
+<prompt>         &gt;</prompt><userinput> conn.space.tester:delete{800}</userinput>
+<prompt>         &gt;</prompt><userinput> table.insert(ta, 'conn delete done on tester.')</userinput>
+<prompt>         &gt;</prompt><userinput> conn.space.tester:insert{800, 'data'}</userinput>
+<prompt>         &gt;</prompt><userinput> table.insert(ta, 'conn insert done on tester, index 0')</userinput>
+<prompt>         &gt;</prompt><userinput> table.insert(ta, '  primary key value = 800.')</userinput>
+<prompt>         &gt;</prompt><userinput> wtuple = conn.space.tester:select(800)</userinput>
+<prompt>         &gt;</prompt><userinput> table.insert(ta, 'conn select done on tester, index 0')</userinput>
+<prompt>         &gt;</prompt><userinput> table.insert(ta, '  number of fields = ' .. #wtuple)</userinput>
+<prompt>         &gt;</prompt><userinput> conn.space.tester:delete{800}</userinput>
+<prompt>         &gt;</prompt><userinput> table.insert(ta, 'conn delete done on tester')</userinput>
+<prompt>         &gt;</prompt><userinput> conn.space.tester:replace{800, 'New data', 'Extra data'}</userinput>
+<prompt>         &gt;</prompt><userinput> table.insert(ta, 'conn:replace done on tester')</userinput>
+<prompt>         &gt;</prompt><userinput> conn:timeout(0.5).space.tester:update({800}, {{'=', 2, 'Fld#1'}})</userinput>
+<prompt>         &gt;</prompt><userinput> table.insert(ta, 'conn update done on tester')</userinput>
+<prompt>         &gt;</prompt><userinput> conn:close()</userinput>
+<prompt>         &gt;</prompt><userinput> table.insert(ta, 'conn close done')</userinput>
+<prompt>         &gt;</prompt><userinput> end!</userinput>
+---
+...
+<prompt>tarantool&gt;</prompt><userinput> session.delimiter('')!</userinput>
+---
+...
+<prompt>tarantool&gt;</prompt><userinput> ta = {}</userinput>
+---
+...
+<prompt>tarantool&gt;</prompt><userinput> example()</userinput>
+---
+...
+<prompt>tarantool&gt;</prompt><userinput> ta</userinput>
+---
+- - self:ping() succeeded
+  - '  (no surprise -- self connection is pre-established)'
+  - The local server listen address = 3301
+  - conn delete done on tester.
+  - conn insert done on tester, index 0
+  - '  primary key value = 800.'
+  - conn select done on tester, index 0
+  - '  number of fields = 1'
+  - conn delete done on tester
+  - conn:replace done on tester
+  - conn update done on tester
+  - conn close done
+...
+<prompt>tarantool&gt;</prompt><userinput> box.space.tester:select(800) -- Prove that the update succeeded.</userinput>
+---
+- [800, 'Fld#1', 'Extra data']
+...
+</programlisting>
+</para>
+</section>
 
 <section xml:id="sp-shard">
     <title>Package <code>shard</code></title>
@@ -2653,8 +2660,8 @@ https://github.com/tarantool/shard/blob/master/README.md</link>.
 Get the space number and the primary-key value from the function's parameters.
 Pass the space number and the primary-key value to [curr] or [prev] to get a Shard Identification Number.
 If the number is equal to "me", perform the function directly because this is the responsible node.
-Otherwise, use the box.net.box package to pass the function to the host and port of
-  the node that, according to the Shard List, is responsible for handling this tuple.
+Otherwise, use the box.net package to pass the function to the host and port of
+the node that, according to the Shard List, is responsible for handling this tuple.
 </programlisting>
         </para>
         </listitem>
@@ -3013,12 +3020,13 @@ error: can't save snapshot, errno 17 (File exists)
 </variablelist>
 </section>
 
-<section xml:id="sp-limitations">
-<title>A limitation that affects long-running Lua functions</title>
+<section xml:id="multitasking">
+<title>Atomic execution</title>
 
 <para>
-    There is a limitation in stored procedures support one should
-    be aware of: execution atomicity.
+    In several places it's been noted that Lua processes occur in
+    fibers on a single thread. That is why there
+    can be a guarantee of execution atomicity. That requires emphasis.
 </para>
 <bridgehead renderas="sect4">Cooperative multitasking environment</bridgehead>
 <para>
@@ -3059,10 +3067,67 @@ error: can't save snapshot, errno 17 (File exists)
     database state. Effectively, it's only possible
     to have CAS (compare-and-swap) -like atomic stored
     procedures: i.e. functions which select and then modify a record.
-
     Multiple data change requests always run through a built-in
     yield point.
 </para>
+<para>
+    At this point an objection could arise: "It's good that a single
+    data-change request will commit and yield, but surely there are
+    times when multiple data-change requests must happen without
+    yielding." The standard example is the money-transfer, where
+    $1 is withdrawn from account #1 and deposited into account #2.
+    If something interrupted after the withdrawal, then the
+    institution would be out of balance. For such cases, the
+    <code>begin ... commit|rollback</code> block was designed.
+</para>
+  <variablelist>
+    <varlistentry>
+      <term xml:id="begin" xreflabel="begin()">
+        <emphasis role="lua">box.begin()</emphasis>
+      </term>
+      <listitem><para>
+        From this point, implicit yields are suspended.
+        In effect the fiber which executes <code>box.begin()</code>
+        is starting an "active multi-request transaction", blocking all
+        other fibers until the transaction ends.
+      </para></listitem>
+    </varlistentry>
+
+    <varlistentry>
+      <term xml:id="commit" xreflabel="commit()">
+        <emphasis role="lua">box.commit()</emphasis>
+      </term>
+      <listitem><para>
+        End the currently active transaction, and make
+        all its data-change operations permanent.
+       </para></listitem>
+    </varlistentry>
+    
+    <varlistentry>
+      <term xml:id="rollback" xreflabel="rollback()">
+        <emphasis role="lua">box.rollback()</emphasis>
+      </term>
+      <listitem><para>
+        End the currently active transaction, but cancel
+        all its data-change operations. An explicit call
+        to fiber.yield() will have the same effect.
+       </para></listitem>
+    </varlistentry>
+    
+  </variablelist>
+
+<para>
+Example:
+Assuming that in tuple set 'tester' there are tuples
+in which the third field represents a positive dollar amount ...
+Start a transaction, withdraw from tuple#1, deposit in tuple#2,
+and end the transaction, making its effects permanent.<programlisting>
+box.begin()
+amount_of_money = 1.00
+box.space.tester:update({999}, {{'-', 3, amount_of_money}})
+box.space.tester:update({1000}, {{'+', 3, amount_of_money}})
+box.commit()</programlisting>
+</para>
 </section>
 
 <section xml:id="errcode">
@@ -3388,7 +3453,7 @@ session.su(<replaceable>user-name</replaceable>)   #allows changing current user
 <para>
 If a user types requests directly on the Tarantool server in its interactive mode,
 or if a user connects via telnet to the administrative port (using
-<olink targetptr="admin_port"/> instead of primary_port), then the user by default is 'admin' and has
+<olink targetptr="admin_port">admin</olink> instead of listen), then the user by default is 'admin' and has
 many privileges. If a user connects from an application program via one of the
 <olink targetptr="connectors">connectors</olink>, then the user by default is 'guest' and has few
 privileges. Typically an admin user will set up and configure objects, then
diff --git a/doc/user/language-reference.xml b/doc/user/language-reference.xml
index d1050ca013d65ebdbd63b6b13bb3e2fb5903efe5..c7832dca4806af3ad10299df556acd4fbe81bbfe 100644
--- a/doc/user/language-reference.xml
+++ b/doc/user/language-reference.xml
@@ -22,7 +22,7 @@
       (also called the "binary protocol"), and provides full data access.
       </para>
       <para>
-      The default value of the port is <literal>3303</literal>,
+      The default value of the port is <literal>3301</literal>,
       as defined in the <olink targetptr="primary_port"/>
       configuration option.
     </para></listitem>
diff --git a/doc/user/lua-and-packages.xml b/doc/user/lua-and-packages.xml
index 889d6551e2b4c147903dcff058a2770b052bbcad..3cdee6a4ef5e84e95a97f8e84c3ffcaaedeb1e05 100644
--- a/doc/user/lua-and-packages.xml
+++ b/doc/user/lua-and-packages.xml
@@ -5,7 +5,7 @@
 <chapter xmlns="http://docbook.org/ns/docbook" version="5.0"
          xmlns:xlink="http://www.w3.org/1999/xlink"
          xmlns:xi="http://www.w3.org/2001/XInclude"
-         xml:id="language-reference">
+         xml:id="lua-and-packages">
 
 <title>Lua and the Tarantool Lua Packages</title>
 
diff --git a/doc/user/plugins.xml b/doc/user/plugins.xml
index fa9756cfb76e5b3cf527226caa96acf8752f509a..a8ca7a431176973d62639211ed7736a7f45285b8 100644
--- a/doc/user/plugins.xml
+++ b/doc/user/plugins.xml
@@ -46,7 +46,7 @@ can work on both SQL and Tarantool inside the same Lua routine.
 <para>
 The connection method is
 <code>box.net.sql.connect('mysql'|'pg', <replaceable>host</replaceable>, <replaceable>port</replaceable>, <replaceable>user</replaceable>, <replaceable>password</replaceable>, <replaceable>database</replaceable>)</code>.
-The methods for select/insert/etc. are the same as the ones in <olink targetptr="sp-box-net-box">the box.net library</olink>.
+The methods for select/insert/etc. are the same as the ones in <link linkend="sp-net-box">the net.box package</link>.
 </para>
 
 <para xml:id="plugin-mysql-example">
diff --git a/doc/user/proctitle.xml b/doc/user/proctitle.xml
index 5476dce8bc6aa0aa518c07ee5ef855b091996db7..fc2c90c1c9c5bc88badf1369b7755126d6561328 100644
--- a/doc/user/proctitle.xml
+++ b/doc/user/proctitle.xml
@@ -42,18 +42,17 @@
     </para></listitem>
   </itemizedlist>
   Possible port names are: <quote>pri</quote> for
-  <olink targetptr="primary_port"/>, <quote>sec</quote> for <olink
-  targetptr="secondary_port"/> and <quote>adm</quote> for <olink
+  <olink targetptr="primary_port">listen</olink>, and <quote>adm</quote> for <olink
   targetptr="admin_port"/>.
 </para>
 <para>
   For example:
   <itemizedlist>
     <listitem><para>
-      <command>tarantool: primary pri: 50000 sec: 50001 adm: 50002</command>
+      <command>tarantool: primary pri: 50000 adm: 50002</command>
     </para></listitem>
     <listitem><para>
-      <command>tarantool: primary@infobox pri: 15013 sec: 15523 adm: 10012</command>
+      <command>tarantool: primary@infobox pri: 15013 adm: 10012</command>
     </para></listitem>
   </itemizedlist>
 </para>
diff --git a/doc/user/replication.xml b/doc/user/replication.xml
index 2c0dd16531d39f4b90abad86dc48a3d5dc53a4ee..fd914269583fb1b181cce9b5d96c5599c5abe7c0 100644
--- a/doc/user/replication.xml
+++ b/doc/user/replication.xml
@@ -54,7 +54,7 @@
     updates from the master having identical LSNs would
     not be applied. In fact, if replication is ON, Tarantool
     does not accept updates, even on its <olink
-    targetptr="primary_port"/>.
+    targetptr="primary_port">"listen" address</olink>.
   </para>
 </section>
 
diff --git a/doc/user/server-administration.xml b/doc/user/server-administration.xml
index e65cf2744569b7916137199e04bfe3ac6164078b..64b395764652f279e216e4d6470997d4e9e3ed19 100644
--- a/doc/user/server-administration.xml
+++ b/doc/user/server-administration.xml
@@ -61,7 +61,7 @@ File-name can be any script containing code for initializing.
 Effect: The code in the file is executed during startup.
 Example: <code>init.lua</code>.
 Notes: If a script is used, there will be no prompt. The script should contain
-configuration information including "admin_port=..." or "primary_port=..." so
+configuration information including "admin=..." or "listen=..." so
 that a separate program can connect to the server via one of the ports.
 </para>
 <para>
@@ -161,7 +161,7 @@ Here is an example of an interactive-mode tarantool client session:
 <programlisting>
 <prompt>$ </prompt>tarantool
                 [ tarantool will display an introductory message including version number here ]
-tarantool> box.cfg{admin_port=3313, primary_port=3303}
+tarantool> box.cfg{admin=3313, listen=3301}
                 [ tarantool will display configuration information here ]
 tarantool> s = box.schema.create_space('tester')
                 [ tarantool may display an in-progress message here ]
@@ -189,10 +189,10 @@ Explanatory notes about what tarantool displayed in the above example:
 </para>
 <para>
 * Many requests return typed objects.
-    In the case of "box.cfg{admin_port=3313, primary_port=3303}",
+    In the case of "box.cfg{admin=3313, listen=3301}",
     this result is displayed on the screen.
     If the request had assigned the result to a variable, for example
-    "c = box.cfg{admin_port=3313, primary_port=3303}", then
+    "c = box.cfg{admin=3313, listen=3301}", then
     the result would not have been displayed on the screen.
 </para>
 <para>
diff --git a/doc/user/stored-procedures.xml b/doc/user/stored-procedures.xml
index 7ec2ed0fefdf3ad833bf17a2b3da95c87e832821..7df0bb4226160c08f2165a6659a5a568db76c77b 100644
--- a/doc/user/stored-procedures.xml
+++ b/doc/user/stored-procedures.xml
@@ -451,8 +451,8 @@ tarantool> <userinput>box.space.tester:update({0}, {{'^', 2, 4}})</userinput>
     (<link xlink:href="https://en.wikipedia.org/wiki/Cyclic_redundancy_check">CRC32</link>).
     The functions in <code>digest</code> are:<programlisting><code>    digest.crc32(<replaceable>string</replaceable>)                  Returns 32-bit checksum made with CRC32.
     digest.crc32_update(<replaceable>number</replaceable>,<replaceable>string</replaceable>)    Returns update of a checksum calculated with crc32.
-    digest.sha0(<replaceable>string</replaceable>)                   Returns 160-bit digest made with SHA-0. Not recommended.
-    digest.sha0_hex(<replaceable>string</replaceable>)               Returns hexadecimal of a digest calculated with sha0.
+    digest.sha(<replaceable>string</replaceable>)                    Returns 160-bit digest made with SHA-0. Not recommended.
+    digest.sha_hex(<replaceable>string</replaceable>)                Returns hexadecimal of a digest calculated with sha.
     digest.sha1(<replaceable>string</replaceable>)                   Returns 160-bit digest made with SHA-1.
     digest.sha1_hex(<replaceable>string</replaceable>)               Returns hexadecimal of a digest calculated with sha1.
     digest.sha224(<replaceable>string</replaceable>)                 Returns 224-bit digest made with SHA-2.
diff --git a/doc/user/tutorial.xml b/doc/user/tutorial.xml
index 184c5d3fbd40a040a82d63c682deedc1a5dec16b..e900c30508ab9f9d967474f891ed3424b1795ce6 100644
--- a/doc/user/tutorial.xml
+++ b/doc/user/tutorial.xml
@@ -490,7 +490,7 @@ The server name is <computeroutput><filename>tarantool</filename></computeroutpu
 <para>
     The server starts in interactive mode and outputs a command prompt.
     To turn on the database, configure it:
-<programlisting><prompt>tarantool></prompt> <userinput>box.cfg{admin_port=3313}</userinput></programlisting>
+<programlisting><prompt>tarantool></prompt> <userinput>box.cfg{admin=3313}</userinput></programlisting>
     (this minimal example is sufficient).
 </para>
 
@@ -508,7 +508,7 @@ If all goes well, you will see the server displaying progress as it initializes,
 2014-06-10 11:53:41.095 ... 0.0M rows written
 2014-06-10 11:53:41.126 ... done
 2014-06-10 11:53:41.126 ... bound to admin port tcp://0.0.0.0:3313
-2014-06-10 11:53:41.127 ... I am primary
+2014-06-10 11:53:41.127 ... ready to accept requests
 </programlisting>
 </para>
 
@@ -637,8 +637,8 @@ inserted and selected tuples.
     "script_dir" no longer exists, use #! instead (see below re "Tarantool = a shell script processor").
   </para>
   <para>CHANGES IN PORT CONFIGURATION.
-    "secondary_port" no longer exists; use "primary_port".
-    "replication_port" no longer exists; use "primary_port".
+    "secondary_port" no longer exists; use "listen".
+    "replication_port" no longer exists; use "listen".
     "memcached" no longer exists (memcached is not supported).
     The admin port is Lua-only now, and the lua prefix is gone. 
   </para>
diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt
index f0c48389ea71011d9f5ec6f3643bd7051c552305..b6cdf165e3b40f4d5ef2496efa657771c5fed398 100644
--- a/extra/CMakeLists.txt
+++ b/extra/CMakeLists.txt
@@ -3,23 +3,23 @@
 #
 if (ENABLE_RPM)
 	# chmod +x 655
-	install (FILES tarantool DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/init.d
-		 PERMISSIONS
-		 OWNER_READ OWNER_WRITE
-		 GROUP_READ GROUP_EXECUTE
-		 WORLD_READ WORLD_EXECUTE)
-	# chmod +x 755
-	install (FILES tarantool_multi.sh DESTINATION bin
-		 PERMISSIONS
-		 OWNER_READ OWNER_WRITE OWNER_EXECUTE
-		 GROUP_READ GROUP_EXECUTE
-		 WORLD_READ WORLD_EXECUTE)
-	# chmod +x 755
-	install (FILES tarantool_deploy.sh DESTINATION bin
-		 PERMISSIONS
-		 OWNER_READ OWNER_WRITE OWNER_EXECUTE
-		 GROUP_READ GROUP_EXECUTE
-		 WORLD_READ WORLD_EXECUTE)
+# 	install (FILES tarantool DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/init.d
+# 		 PERMISSIONS
+# 		 OWNER_READ OWNER_WRITE
+# 		 GROUP_READ GROUP_EXECUTE
+# 		 WORLD_READ WORLD_EXECUTE)
+# 	# chmod +x 755
+# 	install (FILES tarantool_multi.sh DESTINATION bin
+# 		 PERMISSIONS
+# 		 OWNER_READ OWNER_WRITE OWNER_EXECUTE
+# 		 GROUP_READ GROUP_EXECUTE
+# 		 WORLD_READ WORLD_EXECUTE)
+# 	# chmod +x 755
+# 	install (FILES tarantool_deploy.sh DESTINATION bin
+# 		 PERMISSIONS
+# 		 OWNER_READ OWNER_WRITE OWNER_EXECUTE
+# 		 GROUP_READ GROUP_EXECUTE
+# 		 WORLD_READ WORLD_EXECUTE)
 endif()
 
 configure_file(rpm/tarantool.rpm.spec.in rpm/tarantool.rpm.spec @ONLY)
diff --git a/extra/rpm/tarantool.rpm.spec.in b/extra/rpm/tarantool.rpm.spec.in
index b241071d92c97f844c075078ae061a30778965f3..828cd7bb0d7ea748aff42959e01f01cc1b8e5193 100644
--- a/extra/rpm/tarantool.rpm.spec.in
+++ b/extra/rpm/tarantool.rpm.spec.in
@@ -8,6 +8,11 @@
 %define _source_filedigest_algorithm 0
 %define _binary_filedigest_algorithm 0
 
+%global debug_package %{nil}
+%global _enable_debug_package %{nil}
+%global __debug_install_post %{nil}
+%global __debug_package %{nil}
+
 %bcond_without postgresql
 %bcond_without mysql
 
@@ -27,7 +32,8 @@ BuildRequires: binutils-devel
 BuildRequires: perl-podlators
 %endif
 
-# Strange bug. Fix according to http://www.jethrocarr.com/2012/05/23/bad-packaging-habits/
+# Strange bug.
+# Fix according to http://www.jethrocarr.com/2012/05/23/bad-packaging-habits/
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
 Name: %{?scl_prefix}tarantool
@@ -37,8 +43,9 @@ Group: Applications/Databases
 Summary: Tarantool - an efficient in-memory data store
 Vendor: tarantool.org
 License: BSD
-Requires: %{?scl_prefix}tarantool-debuginfo = @RPM_PACKAGE_VERSION@-@RPM_PACKAGE_RELEASE@
 Requires: readline
+Provides: %{?scl_prefix}tarantool-debuginfo
+Provides: %{?scl_prefix}tarantool-debug
 URL: http://tarantool.org
 Source0: @RPM_PACKAGE_SOURCE_FILE_NAME@
 %description
@@ -153,7 +160,7 @@ tarantool-sql-module.
         .. cmake_key_value('CMAKE_INSTALL_INFODIR', '%{_infodir}')
         .. cmake_key_value('CMAKE_INSTALL_MANDIR', '%{_mandir}')
         .. cmake_key_value('CMAKE_INSTALL_LOCALSTATEDIR', '%{_localstatedir}')
-	.. ' %{!?scl:-DCMAKE_INSTALL_SYSCONFDIR=%{_sysconfdir}}'
+        .. ' %{!?scl:-DCMAKE_INSTALL_SYSCONFDIR=%{_sysconfdir}}'
         .. ' %{!?scl:-DENABLE_RPM=ON}'
         .. ' %{?scl:-DENABLE_RPM_SCL=ON}'
         .. dev_with('postgresql', 'WITH_POSTGRESQL')
@@ -169,18 +176,13 @@ tarantool-sql-module.
 make DESTDIR=%{buildroot} install
 
 %post
-groupadd tarantool > /dev/null 2>&1
-useradd -r -g tarantool tarantool > /dev/null 2>&1
-# Performe a single instance setup
-/usr/bin/tarantool_deploy.sh --yes --quiet 1.1
 
 %preun
 
 %files
 %defattr(-,root,root,-)
 
-%dir "%{_datadir}/tarantool"
-%{!?scl:"%{_datadir}/tarantool/00000000000000000001.snap"}
+"%{_bindir}/tarantool"
 
 %dir "%{_datadir}/doc/tarantool"
 "%{_datadir}/doc/tarantool/README.md"
@@ -188,11 +190,6 @@ useradd -r -g tarantool tarantool > /dev/null 2>&1
 "%{_datadir}/doc/tarantool/box-protocol.txt"
 "%{_mandir}/man1/tarantool.1.gz"
 
-"%{_bindir}/tarantool"
-%{!?scl:"%{_bindir}/tarantool_multi.sh"}
-%{!?scl:"%{_bindir}/tarantool_deploy.sh"}
-%{!?scl:"%{_sysconfdir}/init.d/tarantool"}
-
 %files sql-module
 %defattr(-,root,root,-)
 %dir "%{_datadir}/tarantool"
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 082de681c698f1dad91f0d957051b56a542f93b7..9804d41ffe1c8461ca2b7667df3d92bdd88fb050 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -27,6 +27,7 @@ lua_source(lua_sources lua/errno.lua)
 lua_source(lua_sources lua/log.lua)
 lua_source(lua_sources lua/box_net_box.lua)
 lua_source(lua_sources lua/help.lua)
+lua_source(lua_sources lua/tap.lua)
 file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/third_party/luafun)
 lua_source(lua_sources ../third_party/luafun/fun.lua)
 
@@ -85,7 +86,6 @@ set (common_sources
      lua/init.cc
      lua/fiber.cc
      lua/trigger.cc
-     lua/errinj.cc
      lua/ipc.cc
      lua/socket.cc
      lua/session.cc
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 31067cc95a445324da0755bc3282a113b562664c..12177aceb0bec7c7db4db5c5d89535f63b76f282 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -50,4 +50,5 @@ add_library(box
     lua/space.cc
     lua/info.cc
     lua/stat.cc
+    lua/error.cc
     ${bin_sources})
diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc
index 3c6929f8ece83f63c7284a7fbed3242a63a24c5c..7fed303683ba76dee760ce2407def2cc189d7487 100644
--- a/src/box/lua/call.cc
+++ b/src/box/lua/call.cc
@@ -29,6 +29,7 @@
 #include "box/lua/call.h"
 #include "pickle.h"
 
+#include "box/lua/error.h"
 #include "box/lua/tuple.h"
 #include "box/lua/index.h"
 #include "box/lua/space.h"
@@ -304,7 +305,7 @@ boxffi_select(struct port *port, uint32_t space_id, uint32_t index_id,
 		box_process(port, &request);
 		return 0;
 	} catch (Exception *e) {
-		/* will be hanled by box.raise() in Lua */
+		/* will be hanled by box.error() in Lua */
 		return -1;
 	}
 }
@@ -368,27 +369,6 @@ lbox_delete(lua_State *L)
 	return lua_gettop(L) - 3;
 }
 
-static int
-lbox_raise(lua_State *L)
-{
-	if (lua_gettop(L) == 0) {
-		/* re-throw saved exceptions (if any) */
-		if (cord()->exception == NULL)
-			return 0;
-		cord()->exception->raise();
-		return 0;
-	}
-
-	if (lua_gettop(L) < 2)
-		luaL_error(L, "box.raise(): bad arguments");
-	uint32_t code = lua_tointeger(L, 1);
-	if (code >= tnt_error_codes_enum_MAX)
-		luaL_error(L, "box.raise(): unknown error code");
-	const char *str = lua_tostring(L, 2);
-	tnt_raise(ClientError, str, code);
-	return 0;
-}
-
 /**
  * A helper to find a Lua function by name and put it
  * on top of the stack.
@@ -537,7 +517,6 @@ lbox_snapshot(struct lua_State *L)
 }
 
 static const struct luaL_reg boxlib[] = {
-	{"raise", lbox_raise},
 	{"snapshot", lbox_snapshot},
 	{NULL, NULL}
 };
@@ -560,6 +539,7 @@ box_lua_init(struct lua_State *L)
 	luaL_register_module(L, "box.internal", boxlib_internal);
 	lua_pop(L, 1);
 
+	box_lua_error_init(L);
 	box_lua_tuple_init(L);
 	box_lua_index_init(L);
 	box_lua_space_init(L);
diff --git a/src/box/lua/error.cc b/src/box/lua/error.cc
new file mode 100644
index 0000000000000000000000000000000000000000..00d3baabef31e9ec42db683a600fc6b1e81eb643
--- /dev/null
+++ b/src/box/lua/error.cc
@@ -0,0 +1,172 @@
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "box/lua/error.h"
+
+extern "C" {
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+} /* extern "C" */
+
+#include <fiber.h>
+#include <errcode.h>
+#include <errinj.h>
+
+#include "lua/utils.h"
+
+static int
+lbox_raise(lua_State *L)
+{
+	uint32_t code = 0;
+	const char *reason = NULL;
+	const char *file = "";
+	unsigned line = 0;
+	lua_Debug info;
+
+	/* lua_type(L, 1) == LUA_TTABLE - box.error table */
+	int top = lua_gettop(L);
+	if (top <= 1) {
+		/* re-throw saved exceptions (if any) */
+		if (cord()->exception == NULL)
+			return 0;
+		cord()->exception->raise();
+		return 0;
+	} else if (top >= 2 && lua_type(L, 2) == LUA_TNUMBER) {
+		code = lua_tointeger(L, 2);
+		reason = tnt_errcode_desc(code);
+		if (top > 2) {
+			/* Call string.format(reason, ...) to format message */
+			lua_getglobal(L, "string");
+			if (lua_isnil(L, -1))
+				goto raise;
+			lua_getfield(L, -1, "format");
+			if (lua_isnil(L, -1))
+				goto raise;
+			lua_pushstring(L, reason);
+			for (int i = 3; i <= top; i++)
+				lua_pushvalue(L, i);
+			lua_call(L, top - 1, 1);
+			reason = lua_tostring(L, -1);
+		}
+	} else if (top == 2 && lua_istable(L, 2)) {
+		/* A special case that rethrows raw error (used by net.box) */
+		lua_getfield(L, 2, "code");
+		code = lua_tointeger(L, -1);
+		lua_pop(L, 1);
+		lua_getfield(L, 2, "reason");
+		reason = lua_tostring(L, -1);
+		if (reason == NULL)
+			reason = "";
+		lua_pop(L, 1);
+	} else {
+		luaL_error(L, "box.error(): bad arguments");
+	}
+
+raise:
+	if (lua_getstack(L, 1, &info) && lua_getinfo(L, "Sl", &info)) {
+		if (*info.short_src) {
+			file = info.short_src;
+		} else if (*info.source) {
+			file = info.source;
+		} else {
+			file = "eval";
+		}
+		line = info.currentline;
+	}
+
+	/* see tnt_raise() */
+	say_debug("ClientError at %s:%i", file, line);
+	throw new ClientError(file, line, reason, code);
+	return 0;
+}
+
+static int
+lbox_errinj_set(struct lua_State *L)
+{
+	char *name = (char*)luaL_checkstring(L, 1);
+	bool state = lua_toboolean(L, 2);
+	if (errinj_set_byname(name, state)) {
+		lua_pushfstring(L, "error: can't find error injection '%s'", name);
+		return 1;
+	}
+	lua_pushstring(L, "ok");
+	return 1;
+}
+
+static inline int
+lbox_errinj_cb(struct errinj *e, void *cb_ctx)
+{
+	struct lua_State *L = (struct lua_State*)cb_ctx;
+	lua_pushstring(L, e->name);
+	lua_newtable(L);
+	lua_pushstring(L, "state");
+	lua_pushboolean(L, e->state);
+	lua_settable(L, -3);
+	lua_settable(L, -3);
+	return 0;
+}
+
+static int
+lbox_errinj_info(struct lua_State *L)
+{
+	lua_newtable(L);
+	errinj_foreach(lbox_errinj_cb, L);
+	return 1;
+}
+
+void
+box_lua_error_init(struct lua_State *L) {
+	static const struct luaL_reg errorlib[] = {
+		{NULL, NULL}
+	};
+	luaL_register(L, "box.error", errorlib);
+	for (int i = 0; i < tnt_error_codes_enum_MAX; i++) {
+		const char *name = tnt_error_codes[i].errstr;
+		if (strstr(name, "UNUSED") || strstr(name, "RESERVED"))
+			continue;
+		assert(strncmp(name, "ER_", 3) == 0);
+		lua_pushnumber(L, i);
+		/* cut ER_ prefix from constant */
+		lua_setfield(L, -2, name + 3);
+	}
+	lua_newtable(L);
+	lua_pushcfunction(L, lbox_raise);
+	lua_setfield(L, -2, "__call");
+	lua_setmetatable(L, -2);
+	lua_pop(L, 1);
+
+	static const struct luaL_reg errinjlib[] = {
+		{"info", lbox_errinj_info},
+		{"set", lbox_errinj_set},
+		{NULL, NULL}
+	};
+	/* box.error.injection is not set by register_module */
+	luaL_register_module(L, "box.error.injection", errinjlib);
+	lua_pop(L, 1);
+}
diff --git a/src/lua/errinj.h b/src/box/lua/error.h
similarity index 88%
rename from src/lua/errinj.h
rename to src/box/lua/error.h
index dce6b11fa3c6083235a61c1d397966429f2a1769..f0dc5895f9834700a901211f522b3ec62afbd12c 100644
--- a/src/lua/errinj.h
+++ b/src/box/lua/error.h
@@ -1,6 +1,5 @@
-#ifndef INCLUDES_TARANTOOL_LUA_ERRINJ_H
-#define INCLUDES_TARANTOOL_LUA_ERRINJ_H
-
+#ifndef INCLUDES_TARANTOOL_LUA_ERROR_H
+#define INCLUDES_TARANTOOL_LUA_ERROR_H
 /*
  * Redistribution and use in source and binary forms, with or
  * without modification, are permitted provided that the following
@@ -31,6 +30,8 @@
  */
 
 struct lua_State;
-void tarantool_lua_errinj_init(struct lua_State *L);
 
-#endif /* INCLUDES_TARANTOOL_LUA_ERRINJ_H */
+void
+box_lua_error_init(struct lua_State *L);
+
+#endif /* INCLUDES_TARANTOOL_LUA_ERROR_H */
diff --git a/src/box/lua/index.cc b/src/box/lua/index.cc
index 44341362e890f0b77e4540e009ae7191381febc0..0371fd0990de3d0659b61b0a3292cc32d9e467b9 100644
--- a/src/box/lua/index.cc
+++ b/src/box/lua/index.cc
@@ -53,7 +53,7 @@ boxffi_index_len(uint32_t space_id, uint32_t index_id)
 	try {
 		return check_index(space_id, index_id)->size();
 	} catch (Exception *) {
-		return (size_t) -1; /* handled by box.raise() in Lua */
+		return (size_t) -1; /* handled by box.error() in Lua */
 	}
 }
 
@@ -63,7 +63,7 @@ boxffi_index_random(uint32_t space_id, uint32_t index_id, uint32_t rnd)
 	try {
 		return check_index(space_id, index_id)->random(rnd);
 	}  catch (Exception *) {
-		return (struct tuple *) -1; /* handled by box.raise() in Lua */
+		return (struct tuple *) -1; /* handled by box.error() in Lua */
 	}
 }
 
@@ -99,7 +99,7 @@ boxffi_index_iterator(uint32_t space_id, uint32_t index_id, int type,
 	} catch (Exception *) {
 		if (it)
 			it->free(it);
-		/* will be hanled by box.raise() in Lua */
+		/* will be hanled by box.error() in Lua */
 		return NULL;
 	}
 }
diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua
index 6780aea771bf923c8c0c5fe1e69dd477e5a4d267..4d8c73e3a27a8b10b6d5dff00ca00b4f3f557b02 100644
--- a/src/box/lua/load_cfg.lua
+++ b/src/box/lua/load_cfg.lua
@@ -70,8 +70,7 @@ local function reload_cfg(oldcfg, newcfg)
     end
     for key, val in pairs(newcfg) do
         if dynamic_cfg[key] == nil then
-            box.raise(box.error.RELOAD_CFG,
-            "Can't set option '"..key.."' dynamically");
+            box.error(box.error.RELOAD_CFG, key);
         end
         if val == "" then
             val = nil
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index cebfe88ad7f03b2daa3a7ff472dbb2b31545fb0f..ad6f5190f1cdfeab1536d7d1d5736366aea7e7c3 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -69,8 +69,8 @@ local function user_resolve(user)
     return tuple[1]
 end
 
-box.begin = function() if ffi.C.boxffi_txn_begin() == -1 then box.raise() end end
-box.commit = function() if ffi.C.boxffi_txn_commit() == -1 then box.raise() end end
+box.begin = function() if ffi.C.boxffi_txn_begin() == -1 then box.error() end end
+box.commit = function() if ffi.C.boxffi_txn_commit() == -1 then box.error() end end
 box.rollback = ffi.C.boxffi_txn_rollback;
 
 box.schema.space = {}
@@ -89,8 +89,7 @@ box.schema.space.create = function(name, options)
         if options.if_not_exists then
             return box.space[name], "not created"
         else
-            box.raise(box.error.SPACE_EXISTS,
-                     "Space '"..name.."' already exists")
+            box.error(box.error.SPACE_EXISTS, name)
         end
     end
     local id
@@ -134,8 +133,7 @@ box.schema.space.drop = function(space_id)
         end
     end
     if _space:delete{space_id} == nil then
-        box.raise(box.error.NO_SUCH_SPACE,
-                  "Space "..space_id.." does not exist")
+        box.error(box.error.NO_SUCH_SPACE, '#'..tostring(space_id))
     end
 end
 box.schema.space.rename = function(space_id, space_name)
@@ -195,12 +193,10 @@ box.schema.index.rename = function(space_id, index_id, name)
 end
 box.schema.index.alter = function(space_id, index_id, options)
     if box.space[space_id] == nil then
-        box.raise(box.error.NO_SUCH_SPACE,
-                  "Space "..space_id.." does not exist")
+        box.error(box.error.NO_SUCH_SPACE, '#'..tostring(space_id))
     end
     if box.space[space_id].index[index_id] == nil then
-        box.raise(box.error.NO_SUCH_INDEX,
-                  "Index "..index_id.." not found in space"..space_id)
+        box.error(box.error.NO_SUCH_INDEX, index_id, box.space[space_id].name)
     end
     if options == nil then
         return
@@ -217,7 +213,7 @@ box.schema.index.alter = function(space_id, index_id, options)
     end
     if options.id ~= nil then
         if options.parts ~= nil then
-            box.raise(box.error.PROC_LUA,
+            box.error(box.error.PROC_LUA,
                       "Don't know how to update both id and parts")
         end
         ops = {}
@@ -324,7 +320,7 @@ function box.schema.space.bless(space)
     index_mt.len = function(index)
         local ret = builtin.boxffi_index_len(index.space.id, index.id)
         if ret == -1 then
-            box.raise()
+            box.error()
         end
         return tonumber(ret)
     end
@@ -335,7 +331,7 @@ function box.schema.space.bless(space)
     -- min and max
     index_mt.min = function(index, key)
         if index.type == 'HASH' then
-            box.raise(box.error.UNSUPPORTED, 'HASH does not support min()')
+            box.error(box.error.UNSUPPORTED, 'HASH', 'min()')
         end
         local lst = index:select(key, { iterator = 'GE', limit = 1 })[1]
         if lst ~= nil then
@@ -346,7 +342,7 @@ function box.schema.space.bless(space)
     end
     index_mt.max = function(index, key)
         if index.type == 'HASH' then
-            box.raise(box.error.UNSUPPORTED, 'HASH does not support max()')
+            box.error(box.error.UNSUPPORTED, 'HASH', 'max()')
         end
         local lst = index:select(key, { iterator = 'LE', limit = 1 })[1]
         if lst ~= nil then
@@ -359,7 +355,7 @@ function box.schema.space.bless(space)
         rnd = rnd or math.random()
         local tuple = builtin.boxffi_index_random(index.space.id, index.id, rnd)
         if tuple == ffi.cast('void *', -1) then
-            box.raise() -- error
+            box.error() -- error
         elseif tuple ~= nil then
             return box.tuple.bless(tuple)
         else
@@ -377,9 +373,7 @@ function box.schema.space.bless(space)
             elseif box.index[opts.iterator] then
                 itype = box.index[opts.iterator]
             elseif opts.iterator ~= nil then
-                box.raise(box.error.ITERATOR_TYPE,
-                         "Unknown iterator type '"..
-                         tostring(opts.iterator).."'")
+                box.error(box.error.ITERATOR_TYPE, tostring(opts.iterator))
             end
         end
 
@@ -387,7 +381,7 @@ function box.schema.space.bless(space)
         local cdata = builtin.boxffi_index_iterator(index.space.id, index.id,
             itype, keybuf);
         if cdata == nil then
-            box.raise()
+            box.error()
         end
 
         return fun.wrap(iterator_gen, keybuf, ffi.gc(cdata, iterator_cdata_gc))
@@ -418,9 +412,7 @@ function box.schema.space.bless(space)
 
     local function check_index(space, index_id)
         if space.index[index_id] == nil then
-            box.raise(box.error.NO_SUCH_INDEX,
-                string.format("No index #%d is defined in space %d", index_id,
-                    space.id))
+            box.error(box.error.NO_SUCH_INDEX, index_id, space.name)
         end
     end
 
@@ -429,15 +421,14 @@ function box.schema.space.bless(space)
         port.size = 0;
         if builtin.boxffi_select(ffi.cast(port_t, port), index.space.id,
            index.id, box.index.EQ, 0, 2, key, key_end) ~=0 then
-            return box.raise()
+            return box.error()
         end
         if port.size == 0 then
             return
         elseif port.size == 1 then
             return box.tuple.bless(port.ret[0])
         else
-            box.raise(box.error.MORE_THAN_ONE_TUPLE,
-                "More than one tuple found by get()")
+            box.error(box.error.MORE_THAN_ONE_TUPLE)
         end
     end
 
@@ -469,7 +460,7 @@ function box.schema.space.bless(space)
         port.size = 0;
         if builtin.boxffi_select(ffi.cast(port_t, port), index.space.id,
             index.id, iterator, offset, limit, key, key_end) ~=0 then
-            return box.raise()
+            return box.error()
         end
 
         local ret = {}
@@ -492,7 +483,7 @@ function box.schema.space.bless(space)
     end
     index_mt.alter= function(index, options)
         if index.id == nil or index.space == nil then
-            box.raise(box.error.PROC_LUA, "Usage: index:alter{opts}")
+            box.error(box.error.PROC_LUA, "Usage: index:alter{opts}")
         end
         return box.schema.index.alter(index.space.id, index.id, options)
     end
@@ -620,7 +611,7 @@ function box.schema.space.bless(space)
     space_mt.run_triggers = function(space, yesno)
         local space = ffi.C.space_by_id(space.id)
         if space == nil then
-            box.raise(box.error.NO_SUCH_SPACE, "Space not found")
+            box.error(box.error.NO_SUCH_SPACE, space.name)
         end
         ffi.C.space_run_triggers(space, yesno)
     end
@@ -662,8 +653,7 @@ local function object_resolve(object_type, object_name)
     if object_type == 'space' then
         local space = box.space[object_name]
         if  space == nil then
-            box.raise(box.error.NO_SUCH_SPACE,
-                      "Space '"..object_name.."' does not exist")
+            box.error(box.error.NO_SUCH_SPACE, object_name)
         end
         return space.id
     end
@@ -678,8 +668,7 @@ local function object_resolve(object_type, object_name)
         if func then
             return func[1]
         else
-            box.raise(box.error.NO_SUCH_FUNCTION,
-                      "Function '"..object_name.."' does not exist")
+            box.error(box.error.NO_SUCH_FUNCTION, object_name)
         end
     end
     if object_type == 'role' then
@@ -693,13 +682,11 @@ local function object_resolve(object_type, object_name)
         if role then
             return role[1]
         else
-            box.raise(box.error.NO_SUCH_USER,
-                      "Role '"..object_name.."' does not exist")
+            box.error(box.error.NO_SUCH_USER, object_name)
         end
     end
 
-    box.raise(box.error.UNKNOWN_SCHEMA_OBJECT,
-              "Unknown object type '"..object_type.."'")
+    box.error(box.error.UNKNOWN_SCHEMA_OBJECT, object_type)
 end
 
 box.schema.func = {}
@@ -707,8 +694,7 @@ box.schema.func.create = function(name)
     local _func = box.space[box.schema.FUNC_ID]
     local func = _func.index['name']:get{name}
     if func then
-            box.raise(box.error.FUNCTION_EXISTS,
-                      "Function '"..name.."' already exists")
+            box.error(box.error.FUNCTION_EXISTS, name)
     end
     _func:auto_increment{session.uid(), name}
 end
@@ -748,8 +734,7 @@ end
 box.schema.user.create = function(name, opts)
     local uid = user_resolve(name)
     if uid then
-        box.raise(box.error.USER_EXISTS,
-                  "User '"..name.."' already exists")
+        box.error(box.error.USER_EXISTS, name)
     end
     if opts == nil then
         opts = {}
@@ -765,8 +750,7 @@ end
 box.schema.user.drop = function(name)
     local uid = user_resolve(name)
     if uid == nil then
-        box.raise(box.error.NO_SUCH_USER,
-                 "User '"..name.."' does not exist")
+        box.error(box.error.NO_SUCH_USER, name)
     end
     -- recursive delete of user data
     local _priv = box.space[box.schema.PRIV_ID]
@@ -789,8 +773,7 @@ box.schema.user.grant = function(user_name, privilege, object_type,
                                  object_name, grantor)
     local uid = user_resolve(user_name)
     if uid == nil then
-        box.raise(box.error.NO_SUCH_USER,
-                  "User '"..user_name.."' does not exist")
+        box.error(box.error.NO_SUCH_USER, user_name)
     end
     privilege = privilege_resolve(privilege)
     local oid = object_resolve(object_type, object_name)
@@ -806,8 +789,7 @@ end
 box.schema.user.revoke = function(user_name, privilege, object_type, object_name)
     local uid = user_resolve(user_name)
     if uid == nil then
-        box.raise(box.error.NO_SUCH_USER,
-                  "User '"..name.."' does not exist")
+        box.error(box.error.NO_SUCH_USER, name)
     end
     privilege = privilege_resolve(privilege)
     local oid = object_resolve(object_type, object_name)
@@ -830,8 +812,7 @@ box.schema.role = {}
 box.schema.role.create = function(name)
     local uid = user_resolve(name)
     if uid then
-        box.raise(box.error.USER_EXISTS,
-                  "Role '"..name.."' already exists")
+        box.error(box.error.USER_EXISTS, name)
     end
     local _user = box.space[box.schema.USER_ID]
     _user:auto_increment{session.uid(), name, 'role'}
diff --git a/src/box/schema.h b/src/box/schema.h
index c8f71c885ce955066317d484852b528ddb309fd7..69b63f66b9464b5f0b245af079b0cc3b4c20137b 100644
--- a/src/box/schema.h
+++ b/src/box/schema.h
@@ -29,6 +29,7 @@
  * SUCH DAMAGE.
  */
 #include "exception.h"
+#include <stdio.h> /* snprintf */
 
 enum schema_id {
 	/** Start of the reserved range of system spaces. */
@@ -81,7 +82,9 @@ space_cache_find(uint32_t id)
 	if (space)
 		return space;
 
-	tnt_raise(ClientError, ER_NO_SUCH_SPACE, id);
+	char name[12];
+	snprintf(name, sizeof(name), "#%u", id);
+	tnt_raise(ClientError, ER_NO_SUCH_SPACE, name);
 }
 
 /**
diff --git a/src/box/space.h b/src/box/space.h
index 73bf6ce8f1e5846e9200a2697385e305df63d8fb..3111b03c33f94eb64fb4f480a96987c7b56696ba 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -268,7 +268,7 @@ index_find(struct space *space, uint32_t index_id)
 	Index *index = space_index(space, index_id);
 	if (index == NULL)
 		tnt_raise(LoggedError, ER_NO_SUCH_INDEX, index_id,
-			  space_id(space));
+			  space->def.name);
 	return index;
 }
 
diff --git a/src/errcode.h b/src/errcode.h
index 4da99c65721600502ff5548cc48bd5042dea407f..d6754b9e197ec0997371b43d9d96ef6478b936ae 100644
--- a/src/errcode.h
+++ b/src/errcode.h
@@ -59,7 +59,7 @@ enum { TNT_ERRMSG_MAX = 512 };
 	/*  7 */_(ER_SECONDARY,			2, "Can't modify data upon a request on the secondary port.") \
 	/*  8 */_(ER_INJECTION,			2, "Error injection '%s'") \
 	/*  9 */_(ER_CREATE_SPACE,		2, "Failed to create space %u: %s") \
-	/* 10 */_(ER_SPACE_EXISTS,		2, "Space %u already exists") \
+	/* 10 */_(ER_SPACE_EXISTS,		2, "Space '%s' already exists") \
 	/* 11 */_(ER_DROP_SPACE,		2, "Can't drop space %u: %s") \
 	/* 12 */_(ER_ALTER_SPACE,		2, "Can't modify space %u: %s") \
 	/* 13 */_(ER_INDEX_TYPE,		2, "Unsupported index type supplied for index %u in space %u") \
@@ -84,13 +84,13 @@ enum { TNT_ERRMSG_MAX = 512 };
 	/* 32 */_(ER_PROC_LUA,			2, "%s") \
 	/* 33 */_(ER_NO_SUCH_PROC,		2, "Procedure '%.*s' is not defined") \
 	/* 34 */_(ER_NO_SUCH_TRIGGER,		2, "Trigger is not found") \
-	/* 35 */_(ER_NO_SUCH_INDEX,		2, "No index #%u is defined in space %u") \
-	/* 36 */_(ER_NO_SUCH_SPACE,		2, "Space %u does not exist") \
+	/* 35 */_(ER_NO_SUCH_INDEX,		2, "No index #%u is defined in space '%s'") \
+	/* 36 */_(ER_NO_SUCH_SPACE,		2, "Space '%s' does not exist") \
 	/* 37 */_(ER_NO_SUCH_FIELD,		2, "Field %u was not found in the tuple") \
 	/* 38 */_(ER_SPACE_FIELD_COUNT,		2, "Tuple field count %u does not match space %u field count %u") \
 	/* 39 */_(ER_INDEX_FIELD_COUNT,		2, "Tuple field count %u is less than required by a defined index (expected %u)") \
 	/* 40 */_(ER_WAL_IO,			2, "Failed to write to disk") \
-	/* 41 */_(ER_MORE_THAN_ONE_TUPLE,	2, "More than one tuple found") \
+	/* 41 */_(ER_MORE_THAN_ONE_TUPLE,	2, "More than one tuple found by get()") \
 	/* 42 */_(ER_ACCESS_DENIED,		2, "%s access denied for user '%s'") \
 	/* 43 */_(ER_CREATE_USER,		2, "Failed to create user '%s': %s") \
 	/* 44 */_(ER_DROP_USER,			2, "Failed to drop user '%s': %s") \
@@ -121,7 +121,7 @@ enum { TNT_ERRMSG_MAX = 512 };
 	/* 69 */_(ER_MISSING_REQUEST_FIELD,	2, "Missing mandatory field '%s' in request") \
 	/* 70 */_(ER_IDENTIFIER,		2, "Invalid identifier '%s' (expected letters, digits or an underscore)") \
 	/* 71 */_(ER_DROP_FUNCTION,		2, "Can't drop function %u: %s") \
-	/* 72 */_(ER_ITERATOR_TYPE,		2, "Unknown iterator type %s") \
+	/* 72 */_(ER_ITERATOR_TYPE,		2, "Unknown iterator type '%s'") \
 	/* 73 */_(ER_REPLICA_MAX,		2, "Replica count limit reached: %u") \
 	/* 74 */_(ER_INVALID_XLOG,		2, "Failed to read xlog: %lld") \
 	/* 75 */_(ER_INVALID_XLOG_NAME,		2, "Invalid xlog name: expected %lld got %lld") \
@@ -146,7 +146,7 @@ extern struct errcode_record tnt_error_codes[];
 static inline const char *tnt_errcode_str(uint32_t errcode)
 {
 	if (errcode >= tnt_error_codes_enum_MAX) {
-		/* Unknown error code - can be triggered using box.raise() */
+		/* Unknown error code - can be triggered using box.error() */
 		return "ER_UNKNOWN";
 	}
 	return tnt_error_codes[errcode].errstr;
diff --git a/src/lua/box_net_box.lua b/src/lua/box_net_box.lua
index 517fe3fb0739c5fe3220c9f466d0719f06dcfec5..149f25552a21970fa64ea86bde932fef469bd8e2 100644
--- a/src/lua/box_net_box.lua
+++ b/src/lua/box_net_box.lua
@@ -171,15 +171,11 @@ local proto = {
             opts = {}
         end
         if spaceno == nil or type(spaceno) ~= 'number' then
-            box.raise(box.error.NO_SUCH_SPACE,
-                string.format("Space %s does not exist", tostring(spaceno)))
+            box.error(box.error.NO_SUCH_SPACE, '#'..tostring(spaceno))
         end
 
         if indexno == nil or type(indexno) ~= 'number' then
-            box.raise(box.error.NO_SUCH_INDEX,
-                string.format("No index #%s is defined in space %s",
-                    tostring(spaceno),
-                    tostring(indexno)))
+            box.error(box.error.NO_SUCH_INDEX, indexno, '#'..tostring(spaceno))
         end
 
         local body = {
@@ -203,8 +199,7 @@ local proto = {
             if type(opts.iterator) == 'string' then
                 local iterator = box.index[ opts.iterator ]
                 if iterator == nil then
-                    box.raise(box.error.INVALID_MSGPACK,
-                        "Wrong iterator " .. opts.iterator)
+                    box.error(box.error.ITERATOR_TYPE, tostring(opts.iterator))
                 end
                 body[ITERATOR] = iterator
             else
@@ -266,8 +261,7 @@ local function space_metatable(self)
                 if #res == 1 then
                     return res[1]
                 end
-                box.raise(box.error.MORE_THAN_ONE_TUPLE,
-                    "More than one tuple found by get()")
+                box.error(box.error.MORE_THAN_ONE_TUPLE)
             end
         }
     }
@@ -291,8 +285,7 @@ local function index_metatable(self)
                 if #res == 1 then
                     return res[1]
                 end
-                box.raise(box.error.MORE_THAN_ONE_TUPLE,
-                    "More than one tuple found by get()")
+                box.error(box.error.MORE_THAN_ONE_TUPLE)
             end,
 
             min = function(idx, key)
@@ -425,7 +418,7 @@ local remote_methods = {
 
                 if not cn:wait_connected(timeout) then
                     cn:close()
-                    box.raise(box.error.TIMEOUT, 'Timeout exceeded')
+                    box.error(box.error.TIMEOUT)
                 end
                 return cn
             end
@@ -845,15 +838,14 @@ local remote_methods = {
 
         if self.state == 'closed' then
             if raise then
-                box.raise(box.error.NO_CONNECTION,
-                    "Connection was closed")
+                box.error(box.error.NO_CONNECTION)
             end
         end
 
         if self.timeouts[fid] <= 0 then
             self.timeouts[fid] = nil
             if raise then
-                box.raise(box.error.TIMEOUT, 'Timeout exceeded')
+                box.error(box.error.TIMEOUT)
             else
                 return {
                     hdr = { [TYPE] = bit.bor(ERROR_TYPE, box.error.TIMEOUT) },
@@ -893,7 +885,7 @@ local remote_methods = {
 
         if response == nil then
             if raise then
-                box.raise(box.error.TIMEOUT, 'Timeout exceeded')
+                box.error(box.error.TIMEOUT)
             else
                 return {
                     hdr = { [TYPE] = bit.bor(ERROR_TYPE, box.error.TIMEOUT) },
@@ -903,8 +895,10 @@ local remote_methods = {
         end
 
         if raise and response.hdr[TYPE] ~= OK then
-            local errcode = bit.band(response.hdr[TYPE], ERROR - 1)
-            box.raise(errcode, response.body[ERROR])
+            box.error({
+                code = response.hdr[TYPE],
+                reason = response.body[ERROR]
+            })
         end
 
         if response.body[DATA] ~= nil then
@@ -971,8 +965,16 @@ remote.self = {
     end
 }
 
-setmetatable(remote.self, { __index = box })
 
+setmetatable(remote.self, {
+    __index = function(self, key)
+        if key == 'space' then
+            -- proxy self.space to box.space
+            return require('box').space
+        end
+        return nil
+    end
+})
 
 return remote
 
diff --git a/src/lua/console.lua b/src/lua/console.lua
index 3bc0ff68ca172fef163c66f4d4cea0a43ba597ef..3db0a7053fff830ed0fbf0dacb8eab3b00ffc5c1 100644
--- a/src/lua/console.lua
+++ b/src/lua/console.lua
@@ -26,7 +26,7 @@ local function format(status, ...)
     return formatter.encode(res)
 end
 
-local function local_eval(self, line, ...)
+local function local_eval(self, line)
     --
     -- Attempt to append 'return ' before the chunk: if the chunk is
     -- an expression, this pushes results of the expression onto the
@@ -40,7 +40,7 @@ local function local_eval(self, line, ...)
     if not fun then
         return format(false, errmsg)
     end
-    return format(pcall(fun, ...))
+    return format(pcall(fun))
 end
 
 local function eval(line)
@@ -55,7 +55,7 @@ local local_mt = {
     }
 }
 
-local function remote_eval(self, line, ...)
+local function remote_eval(self, line)
     --
     -- call remote 'console.eval' function using 'dostring' and return result
     --
@@ -66,7 +66,7 @@ local function remote_eval(self, line, ...)
         return format(status, res)
     end
     -- return formatted output from remote
-    return res[1][0]
+    return res[1][1]
 end
 
 local function remote_close(self)
@@ -136,7 +136,7 @@ local function connect(...)
     if not session.storage.console then
         error("console.connect() works only in interactive mode")
     end
-    local conn = box.net.box.new(...)
+    local conn = require('net.box'):new(...)
     conn:ping() -- test connection
     table.insert(session.storage.console.handlers, setmetatable(
         { conn =  conn}, remote_mt))
diff --git a/src/lua/errinj.cc b/src/lua/errinj.cc
deleted file mode 100644
index 7425035d79728ddf7410d259dff9fbbb4e6d7ec5..0000000000000000000000000000000000000000
--- a/src/lua/errinj.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * 1. Redistributions of source code must retain the above
- *    copyright notice, this list of conditions and the
- *    following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above
- *    copyright notice, this list of conditions and the following
- *    disclaimer in the documentation and/or other materials
- *    provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
- * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include "lua/errinj.h"
-
-#include <errinj.h>
-
-extern "C" {
-#include <lua.h>
-#include <lauxlib.h>
-#include <lualib.h>
-} /* extern "C" */
-
-static int
-lbox_errinj_set(struct lua_State *L)
-{
-	char *name = (char*)luaL_checkstring(L, 1);
-	bool state = lua_toboolean(L, 2);
-	if (errinj_set_byname(name, state)) {
-		lua_pushfstring(L, "error: can't find error injection '%s'", name);
-		return 1;
-	}
-	lua_pushstring(L, "ok");
-	return 1;
-}
-
-static inline int
-lbox_errinj_cb(struct errinj *e, void *cb_ctx)
-{
-	struct lua_State *L = (struct lua_State*)cb_ctx;
-	lua_pushstring(L, e->name);
-	lua_newtable(L);
-	lua_pushstring(L, "state");
-	lua_pushboolean(L, e->state);
-	lua_settable(L, -3);
-	lua_settable(L, -3);
-	return 0;
-}
-
-static int
-lbox_errinj_info(struct lua_State *L)
-{
-	lua_newtable(L);
-	errinj_foreach(lbox_errinj_cb, L);
-	return 1;
-}
-
-static const struct luaL_reg errinjlib[] = {
-	{"info", lbox_errinj_info},
-	{"set", lbox_errinj_set},
-	{NULL, NULL}
-};
-
-/** Initialize box.errinj package. */
-void
-tarantool_lua_errinj_init(struct lua_State *L)
-{
-	luaL_register(L, "errinj", errinjlib);
-	lua_pop(L, 1);
-}
diff --git a/src/lua/init.cc b/src/lua/init.cc
index 31e13f3305d0d6026118fe24760eac586ccb80d0..8a3b910372ebaf72337ad5907ed457159f2756b6 100644
--- a/src/lua/init.cc
+++ b/src/lua/init.cc
@@ -47,7 +47,6 @@ extern "C" {
 #include <scoped_guard.h>
 #include "coeio.h"
 #include "lua/fiber.h"
-#include "lua/errinj.h"
 #include "lua/ipc.h"
 #include "lua/errno.h"
 #include "lua/socket.h"
@@ -77,7 +76,8 @@ extern char uuid_lua[],
 	log_lua[],
 	console_lua[],
 	box_net_box_lua[],
-	help_lua[];
+	help_lua[],
+	tap_lua[];
 
 static const char *lua_sources[] = {
 	init_lua,
@@ -94,6 +94,7 @@ static const char *lua_modules[] = {
 	"uuid", uuid_lua,
 	"log", log_lua,
 	"net.box", box_net_box_lua,
+	"tap", tap_lua,
 	NULL
 };
 
@@ -173,25 +174,6 @@ lbox_coredump(struct lua_State *L __attribute__((unused)))
 	return 1;
 }
 
-static const struct luaL_reg errorlib [] = {
-	{NULL, NULL}
-};
-
-static void
-tarantool_lua_error_init(struct lua_State *L) {
-	luaL_register(L, "box.error", errorlib);
-	for (int i = 0; i < tnt_error_codes_enum_MAX; i++) {
-		const char *name = tnt_error_codes[i].errstr;
-		if (strstr(name, "UNUSED") || strstr(name, "RESERVED"))
-			continue;
-		assert(strncmp(name, "ER_", 3) == 0);
-		lua_pushnumber(L, i);
-		/* cut ER_ prefix from constant */
-		lua_setfield(L, -2, name + 3);
-	}
-	lua_pop(L, 1);
-}
-
 /* }}} */
 
 /*
@@ -293,7 +275,6 @@ tarantool_lua_init(const char *tarantool_bin, int argc, char **argv)
 	lua_register(L, "tonumber64", lbox_tonumber64);
 	lua_register(L, "coredump", lbox_coredump);
 
-	tarantool_lua_errinj_init(L);
 	tarantool_lua_fiber_init(L);
 	tarantool_lua_cjson_init(L);
 	tarantool_lua_yaml_init(L);
@@ -302,7 +283,6 @@ tarantool_lua_init(const char *tarantool_bin, int argc, char **argv)
 	tarantool_lua_socket_init(L);
 	tarantool_lua_bsdsocket_init(L);
 	tarantool_lua_session_init(L);
-	tarantool_lua_error_init(L);
 	tarantool_lua_pickle_init(L);
 	luaopen_msgpack(L);
 	lua_pop(L, 1);
diff --git a/src/lua/tap.lua b/src/lua/tap.lua
new file mode 100644
index 0000000000000000000000000000000000000000..83356030be80a6053c915e7d6650a71e2ea4a379
--- /dev/null
+++ b/src/lua/tap.lua
@@ -0,0 +1,215 @@
+--- tap.lua internal file
+---
+--- The Test Anything Protocol vesion 13 producer
+---
+
+local yaml = require('yaml')
+local ffi = require('ffi') -- for iscdata
+
+local function traceback(level)
+    local trace = {}
+    level = level or 3
+    while true do
+        local info = debug.getinfo(level, "nSl")
+        if not info then break end
+        local frame = {
+            source = info.source;
+            src = info.short_src;
+            line = info.linedefined or 0;
+            what = info.what;
+            name = info.name;
+            namewhat = info.namewhat;
+            filename = info.source:sub(1, 1) == "@" and info.source:sub(2) or 
+                'eval'
+        }
+        table.insert(trace, frame)
+        level = level + 1
+    end
+    return trace
+end
+
+local function diag(test, fmt, ...)
+    io.write(string.rep(' ', 4 * test.level), "# ", string.format(fmt, ...),
+        "\n")
+end
+
+local function ok(test, cond, message, extra)
+    test.total = test.total + 1
+    io.write(string.rep(' ', 4 * test.level))
+    if cond then
+        io.write(string.format("ok - %s\n", message))
+        return true
+    end
+
+    test.failed = test.failed + 1
+    io.write(string.format("not ok - %s\n", message))
+    extra = extra or {}
+    if test.trace then
+        local frame = debug.getinfo(3, "Sl")
+        extra.trace = traceback()
+        extra.filename = extra.trace[#extra.trace].filename
+        extra.line = extra.trace[#extra.trace].line
+    end
+    if next(extra) == nil then
+        return false -- don't have extra information
+    end
+    -- print aligned yaml output
+    for line in yaml.encode(extra):gmatch("[^\n]+") do
+        io.write(string.rep(' ', 2 + 4 * test.level), line, "\n")
+    end
+    return false
+end
+
+local function fail(test, message, extra)
+    return ok(test, false, message, extra)
+end
+
+local function skip(test, message, extra)
+    ok(test, true, message.." # skip", extra)
+end
+
+local function is(test, got, expected, message, extra)
+    extra = extra or {}
+    extra.got = got
+    extra.expected = expected
+    return ok(test, got == expected, message, extra)
+end
+
+local function isnt(test, got, unexpected, message, extra)
+    extra = extra or {}
+    extra.got = got
+    extra.unexpected = unexpected
+    return ok(test, got ~= unexpected, message, extra)
+end
+
+local function isnil(test, v, message, extra)
+    return is(test, not v and 'nil' or v, 'nil', message, extra)
+end
+
+local function isnumber(test, v, message, extra)
+    return is(test, type(v), 'number', message, extra)
+end
+
+local function isstring(test, v, message, extra)
+    return is(test, type(v), 'string', message, extra)
+end
+
+local function istable(test, v, message, extra)
+    return is(test, type(v), 'table', message, extra)
+end
+
+local function isboolean(test, v, message, extra)
+    return is(test, type(v), 'boolean', message, extra)
+end
+
+local function isudata(test, v, utype, message, extra)
+    extra = extra or {}
+    extra.expected = 'userdata<'..utype..'>'
+    if type(v) == 'userdata' then
+        extra.got = 'userdata<'..getmetatable(v)..'>'
+        return ok(test, getmetatable(v) == utype, message, extra)
+    else
+        extra.got = type(v)
+        return fail(test, message, extra)
+    end
+end
+
+local function iscdata(test, v, ctype, message, extra)
+    extra = extra or {}
+    extra.expected = ffi.typeof(ctype)
+    if type(v) == 'cdata' then
+        extra.got = ffi.typeof(v)
+        return ok(test, ffi.istype(ctype, v), message, extra)
+    else
+        extra.got = type(v)
+        return fail(test, message, extra)
+    end
+end
+
+local test_mt
+local function test(parent, name, fun)
+    local level = parent ~= nil and parent.level + 1 or 0
+    local test = setmetatable({
+        parent  = parent;
+        name    = name;
+        level   = level;
+        total   = 0;
+        failed  = 0;
+        planned = 0;
+        trace   = parent == nil and true or parent.trace;
+    }, test_mt)
+    if fun ~= nil then
+        test:diag('%s', test.name)
+        fun(test)
+        test:diag('%s: end', test.name)
+        return test:check()
+    else
+        return test
+    end
+end
+
+local function plan(test, planned)
+    test.planned = planned
+    io.write(string.rep(' ', 4 * test.level), string.format("1..%d\n", planned))
+end
+
+local function check(test)
+    if test.checked then
+        error('check called twice')
+    end
+    test.checked = true
+    if test.planned ~= test.total then
+        if test.parent ~= nil then
+            ok(test.parent, false, "bad plan", { planned = test.planned;
+                run = test.total})
+        else
+            diag(test, string.format("bad plan: planned %d run %d",
+                test.planned, test.total))
+        end
+    elseif test.failed > 0 then
+        if test.parent ~= nil then
+            ok(test.parent, false, "failed subtests", {
+                failed = test.failed;
+                planned = test.planned;
+            })
+            return false
+        else
+            diag(test, "failed subtest: %d", test.failed)
+        end
+    else
+        if test.parent ~= nil then
+            ok(test.parent, true, test.name)
+        end
+    end
+    return true
+end
+
+test_mt = {
+    __index = {
+        test      = test;
+        plan      = plan;
+        check     = check;
+        diag      = diag;
+        ok        = ok;
+        fail      = fail;
+        skip      = skip;
+        is        = is;
+        isnt      = isnt;
+        isnil     = isnil;
+        isnumber  = isnumber;
+        isstring  = isstring;
+        istable   = istable;
+        isboolean = isboolean;
+        isudata   = isudata;
+        iscdata   = iscdata;
+    }
+}
+
+local function root_test(...)
+    io.write('TAP version 13', '\n')
+    return test(nil, ...)
+end
+
+return {
+    test = root_test;
+}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 017e4998a5fda33e8afe4bc20930193caaa290e6..ad6576d4806507961cd2f2455bd649b6e0446679 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -13,13 +13,3 @@ add_custom_target(test-force
 add_subdirectory(unit)
 # Disable connector_c for 1.6
 #add_subdirectory(connector_c)
-
-if (ENABLE_RPM)
-    install (FILES ${CMAKE_SOURCE_DIR}/src/box/bootstrap.snap
-             DESTINATION ${CMAKE_INSTALL_DATADIR}/tarantool
-             RENAME 00000000000000000001.snap)
-elseif(NOT ENABLE_RPM_SCL)
-    install (FILES ${CMAKE_SOURCE_DIR}/src/box/bootstrap.snap
-             DESTINATION "${CMAKE_INSTALL_LOCALSTATEDIR}/lib/tarantool"
-             RENAME 00000000000000000001.snap)
-endif()
diff --git a/test/app/cfg.result b/test/app/cfg.result
index 804aeb7327bdb676764a53f975588204543d8dc5..1ce036cf2d52286c3780141fd4461229e6948f2d 100644
--- a/test/app/cfg.result
+++ b/test/app/cfg.result
@@ -1,3 +1,3 @@
-false	[string "-- load_cfg.lua - internal file..."]:100: Please call box.cfg{} first
+false	[string "-- load_cfg.lua - internal file..."]:99: Please call box.cfg{} first
 true	table
-false	[string "-- load_cfg.lua - internal file..."]:100: Please call box.cfg{} first
+false	[string "-- load_cfg.lua - internal file..."]:99: Please call box.cfg{} first
diff --git a/test/app/pcall.result b/test/app/pcall.result
index e70f316bc71f624ac6e84a1e1646fd9aecb6442d..38429cee4f3dd09d68120c3431823f0cf01a2f86 100644
--- a/test/app/pcall.result
+++ b/test/app/pcall.result
@@ -4,7 +4,7 @@
 
 pcall inside xpcall:	true	pcall is ok
 pcall with Lua error():	false	some message
-pcall with box.raise():	false	some message
+pcall with box.error():	false	Illegal parameters, some message
 pcall with no return:	1
 pcall with multireturn:	true	1	2	3
 --------------------------------------------------------------------------------
diff --git a/test/app/pcall.test.lua b/test/app/pcall.test.lua
index 248ccbd7b6344cb687f062fa61ebbd10eb63bcfa..91557e3218925e2200b40bb60fbc9bfe052842b5 100755
--- a/test/app/pcall.test.lua
+++ b/test/app/pcall.test.lua
@@ -26,9 +26,9 @@ local status, msg = pcall(function() error('some message') end)
 print('pcall with Lua error():', status, msg:match('some message'))
 
 local status, msg = pcall(function()
-    box.raise(box.error.ILLEGAL_PARAMS, 'some message')
+    box.error(box.error.ILLEGAL_PARAMS, 'some message')
 end)
-print('pcall with box.raise():', status, msg)
+print('pcall with box.error():', status, msg)
 
 print('pcall with no return:', select('#', pcall(function() end)))
 print('pcall with multireturn:', pcall(function() return 1, 2, 3 end))
diff --git a/test/app/tap.result b/test/app/tap.result
new file mode 100644
index 0000000000000000000000000000000000000000..052e3ad667e5a3812a36a102ce4a7d0051eb16cf
--- /dev/null
+++ b/test/app/tap.result
@@ -0,0 +1,121 @@
+TAP version 13
+1..30
+ok - true
+ok - extra information is not printed on success
+not ok - extra printed using yaml only on failure
+  ---
+  state: some userful information to debug on failure
+  details: a table argument formatted using yaml.encode()
+  ...
+not ok - failed
+ok - test marked as ok and skipped # skip
+ok - tonumber(48) is 48
+ok - 0xff is not 64
+not ok - 1 is not 1
+  ---
+  unexpected: 1
+  got: 1
+  ...
+ok - nil is nil
+not ok - 48 is nil
+  ---
+  expected: nil
+  got: 48
+  ...
+ok - 10 is a number
+ok - 0 is also a number
+ok - "blabla" is string
+not ok - 48 is string
+  ---
+  expected: string
+  got: number
+  ...
+not ok - nil is string
+  ---
+  expected: string
+  got: nil
+  ...
+ok - true is boolean
+not ok - 1 is boolean
+  ---
+  expected: boolean
+  got: number
+  ...
+ok - {} is a table
+not ok - udata
+  ---
+  expected: userdata<fiber>
+  got: nil
+  ...
+not ok - udata
+  ---
+  expected: userdata<some utype>
+  got: userdata<fiber>
+  ...
+ok - udata
+not ok - cdata type
+  ---
+  expected: ctype<int>
+  got: string
+  ...
+not ok - cdata type
+  ---
+  expected: ctype<int>
+  got: number
+  ...
+ok - cdata type
+not ok - cdata type
+  ---
+  expected: ctype<int>
+  got: ctype<unsigned int>
+  ...
+    # subtest 1
+    1..2
+    ok - true
+    ok - true
+    # subtest 1: end
+ok - subtest 1
+    1..1
+    ok - true in subtest
+    # hello from subtest
+ok - subtest 2
+    # 1 level
+    1..1
+        # 2 level
+        1..1
+            # 3 level
+            1..1
+                # 4 level
+                1..1
+                    # 5 level
+                    1..1
+                    ok - ok
+                    # 5 level: end
+                ok - 5 level
+                # 4 level: end
+            ok - 4 level
+            # 3 level: end
+        ok - 3 level
+        # 2 level: end
+    ok - 2 level
+    # 1 level: end
+ok - 1 level
+    # bad plan
+    1..3
+    ok - true
+    # bad plan: end
+not ok - bad plan
+  ---
+  planned: 3
+  run: 1
+  ...
+    # failed subtest
+    1..1
+    not ok - failed subtest
+    # failed subtest: end
+not ok - failed subtests
+  ---
+  planned: 1
+  failed: 1
+  ...
+# failed subtest: 14
diff --git a/test/app/tap.test.lua b/test/app/tap.test.lua
new file mode 100755
index 0000000000000000000000000000000000000000..243d1cbdc90f92e20f4c4a7f1af920f78c60c60e
--- /dev/null
+++ b/test/app/tap.test.lua
@@ -0,0 +1,123 @@
+#!/usr/bin/env tarantool
+
+--
+-- Test suite for The Test Anything Protocol module implemented
+-- using module itself.
+--
+
+-- Load 'tap' module
+local tap = require "tap"
+
+--
+-- Create a root test
+--
+test = tap.test("root test")
+-- Disable stack traces for this test because Tarantool test system also
+-- checks test output.
+test.trace = false
+
+--
+-- ok, fail and skip predicates
+--
+
+test:plan(30) -- plan to run 3 test
+test:ok(true, 'true') -- basic function
+local extra = { state = 'some userful information to debug on failure',
+        details = 'a table argument formatted using yaml.encode()' }
+test:ok(true, "extra information is not printed on success", extra)
+test:ok(false, "extra printed using yaml only on failure", extra)
+
+test:fail('failed') -- always fail the test
+test:skip('test marked as ok and skipped')
+
+--
+-- is and isnt predicates
+--
+test:is(tonumber("48"), 48, "tonumber(48) is 48")
+test:isnt(0xff, 64, "0xff is not 64")
+test:isnt(1, 1, "1 is not 1")
+
+--
+-- type predicates
+--
+test:isnil(nil, 'nil is nil')
+test:isnil(48, '48 is nil')
+test:isnumber(10, '10 is a number')
+test:isnumber(0, '0 is also a number')
+test:isstring("blabla", '"blabla" is string')
+test:isstring(48, '48 is string')
+test:isstring(nil, 'nil is string')
+test:isboolean(true, 'true is boolean')
+test:isboolean(1, '1 is boolean')
+test:istable({}, '{} is a table')
+local udata = require('fiber').self()
+test:isudata(nil, 'fiber', 'udata')
+test:isudata(udata, 'some utype', 'udata')
+test:isudata(udata, 'fiber', 'udata')
+local ffi = require('ffi')
+test:iscdata('xx', 'int', 'cdata type')
+test:iscdata(10, 'int', 'cdata type')
+test:iscdata(ffi.new('int', 10), 'int', 'cdata type')
+test:iscdata(ffi.new('unsigned int', 10), 'int', 'cdata type')
+
+--
+-- Any test also can create unlimited number of sub tests.
+-- Subtest with callbacks (preferred).
+--
+test:test("subtest 1", function(t)
+    t:plan(2)
+    t:ok(true, 'true')
+    t:ok(true, 'true')
+    -- test:check() is called automatically
+end)
+-- each subtest is counted in parent
+
+--
+-- Subtest without callbacks.
+--
+sub2 = test:test("subtest 2")
+    sub2:plan(1)
+    sub2:ok(true, 'true in subtest')
+    sub2:diag('hello from subtest')
+    sub2:check() -- please call check() explicitly
+
+--
+-- Multisubtest
+--
+test:test("1 level", function(t)
+    t:plan(1)
+    t:test("2 level", function(t)
+        t:plan(1)
+        t:test("3 level", function(t)
+            t:plan(1)
+            t:test("4 level", function(t)
+                t:plan(1)
+                t:test("5 level", function(t)
+                    t:plan(1)
+                    t:ok(true, 'ok')
+                end)
+            end)
+        end)
+    end)
+end)
+
+---
+--- Subtest with bad plan()
+---
+test:test("bad plan", function(t)
+    t:plan(3)
+    t:ok(true, 'true')
+end)
+
+
+test:test("failed subtest", function(t)
+    t:plan(1)
+    t:fail("failed subtest")
+end)
+
+--
+-- Finish root test. Since we used non-callback variant, we have to
+-- call check explicitly.
+--
+test:check() -- call check() explicitly
+os.exit(0)
diff --git a/test/box/access.result b/test/box/access.result
index 207457a2fab55ee68b3732b7c0b727dab64be8c5..d4fdbc98140a02a4901e96160de4863be7df8d1a 100644
--- a/test/box/access.result
+++ b/test/box/access.result
@@ -82,7 +82,7 @@ end;
 ...
 usermax();
 ---
-- error: User 'user29' does not exist
+- error: User 'user29' is not found
 ...
 --# setopt delimiter ''
 box.schema.user.create('rich')
diff --git a/test/box/alter.result b/test/box/alter.result
index 1d454079d4b400085b3c122fb1ee105c30f1e6c0..76edd8110395f9d10bcd836728b90823b9c05f24 100644
--- a/test/box/alter.result
+++ b/test/box/alter.result
@@ -104,23 +104,23 @@ space.index[0]
 --
 space:select{0}
 ---
-- error: 'No index #0 is defined in space 321'
+- error: 'No index #0 is defined in space ''hello'''
 ...
 space:insert{0, 0}
 ---
-- error: 'No index #0 is defined in space 321'
+- error: 'No index #0 is defined in space ''hello'''
 ...
 space:replace{0, 0}
 ---
-- error: 'No index #0 is defined in space 321'
+- error: 'No index #0 is defined in space ''hello'''
 ...
 space:update({0}, {{'+', 1, 1}})
 ---
-- error: 'No index #0 is defined in space 321'
+- error: 'No index #0 is defined in space ''hello'''
 ...
 space:delete{0}
 ---
-- error: 'No index #0 is defined in space 321'
+- error: 'No index #0 is defined in space ''hello'''
 ...
 t = _space:delete{space.id}
 ---
@@ -134,7 +134,7 @@ space_deleted
 ...
 space:replace{0}
 ---
-- error: Space 321 does not exist
+- error: Space '#321' does not exist
 ...
 _index:insert{_space.id, 0, 'primary', 'tree', 1, 1, 0, 'num'}
 ---
diff --git a/test/box/alter_limits.result b/test/box/alter_limits.result
index f03a38d38630c715a302a785cab82b37fb0201f8..87d1cc2bbda53d04bea0bfcd3efefa66f9d3d2da 100644
--- a/test/box/alter_limits.result
+++ b/test/box/alter_limits.result
@@ -66,7 +66,7 @@ s:drop()
 -- no such space
 s:drop()
 ---
-- error: Space 512 does not exist
+- error: Space '#512' does not exist
 ...
 -- no such engine
 box.schema.create_space('tweedleedee', { engine = 'unknown' })
@@ -135,23 +135,23 @@ s = box.schema.create_space('tweedledum')
 ...
 s:insert{0}
 ---
-- error: 'No index #0 is defined in space 512'
+- error: 'No index #0 is defined in space ''tweedledum'''
 ...
 s:select{}
 ---
-- error: 'No index #0 is defined in space 512'
+- error: 'No index #0 is defined in space ''tweedledum'''
 ...
 s:delete{0}
 ---
-- error: 'No index #0 is defined in space 512'
+- error: 'No index #0 is defined in space ''tweedledum'''
 ...
 s:update(0, {{"=", 1, 0}})
 ---
-- error: 'No index #0 is defined in space 512'
+- error: 'No index #0 is defined in space ''tweedledum'''
 ...
 s:insert{0}
 ---
-- error: 'No index #0 is defined in space 512'
+- error: 'No index #0 is defined in space ''tweedledum'''
 ...
 s.index[0]
 ---
@@ -898,7 +898,7 @@ primary.id
 ...
 primary:select{}
 ---
-- error: 'No index #0 is defined in space 512'
+- error: 'No index #0 is defined in space ''test'''
 ...
 s:drop()
 ---
diff --git a/test/box/auth_access.result b/test/box/auth_access.result
index 02db9c129ba103ac7419a587fc786dd4de74a421..47f6b8bec95929b8f1a93a0e70d3161217704490 100644
--- a/test/box/auth_access.result
+++ b/test/box/auth_access.result
@@ -19,7 +19,7 @@ s:drop()
 ...
 s:drop()
 ---
-- error: Space 512 does not exist
+- error: Space '#512' does not exist
 ...
 --
 -- Check double create user
@@ -131,7 +131,7 @@ box.schema.user.drop('testus')
 ...
 box.schema.user.drop('testus')
 ---
-- error: User 'testus' does not exist
+- error: User 'testus' is not found
 ...
 --
 -- Check 'guest' user
@@ -288,7 +288,7 @@ box.schema.func.drop('uniuser_func')
 ...
 box.schema.user.drop('uniuser_testus')
 ---
-- error: User 'uniuser_testus' does not exist
+- error: User 'uniuser_testus' is not found
 ...
 session.su('admin')
 ---
@@ -301,7 +301,7 @@ box.schema.user.drop('someuser')
 ...
 box.schema.user.drop('uniuser_testus')
 ---
-- error: User 'uniuser_testus' does not exist
+- error: User 'uniuser_testus' is not found
 ...
 box.schema.user.drop('uniuser')
 ---
diff --git a/test/box/box.net.box.result b/test/box/box.net.box.result
index 907414a7e56febfc3b5d488e1f2a8a7bd77e38e8..9f6f528170de25db79b00db6638f29b7d50a8f4b 100644
--- a/test/box/box.net.box.result
+++ b/test/box/box.net.box.result
@@ -254,7 +254,7 @@ cn:ping()
 ...
 cn:call('test_foo')
 ---
-- error: Connection was closed
+- error: Connection is not established
 ...
 -- -- 2 reconnect
 cn = remote:new('127.0.0.1', port, { reconnect_after = .1 })
diff --git a/test/box/box.net.box.test.lua b/test/box/box.net.box.test.lua
index 4911ce6a9002c41682a08705f0ea53bbdb4967f0..4bae183153b932e5af5aba64167a3a0a0f19783b 100644
--- a/test/box/box.net.box.test.lua
+++ b/test/box/box.net.box.test.lua
@@ -149,6 +149,9 @@ cn:timeout(.01):call('ret_after', 1)
 cn = remote:timeout(0.0000000001):new('127.0.0.1', port, { user = 'netbox', password = '123' })
 cn = remote:timeout(1):new('127.0.0.1', port, { user = 'netbox', password = '123' })
 
+
+
+
 remote.self:ping()
 remote.self.space.net_box_test_space:select{234}
 remote.self:timeout(123).space.net_box_test_space:select{234}
diff --git a/test/box/call.result b/test/box/call.result
index 9682d126ae08f73d33afabfd9275a7715fcab54f..3e0fa00834b1834c756d40e50428fbcca15c7e94 100644
--- a/test/box/call.result
+++ b/test/box/call.result
@@ -45,11 +45,11 @@ call f1()
     errcode: ER_PROC_RET
     errmsg: msgpack.encode: can not encode Lua type 'function'
 ...
-call dostring('box.raise(33333, "Hey!")')
+call dostring('box.error(33333, "Hey!")')
 ---
 - error:
-    errcode: ER_PROC_LUA
-    errmsg: [string "box.raise(33333, "Hey!")"]:1: box.raise(): unknown error code
+    errcode: ER_UNKNOWN (565)
+    errmsg: Unknown error
 ...
 
 # A test case for Bug#103491
diff --git a/test/box/call.test.py b/test/box/call.test.py
index 30e9c12d2e6d87f64c0aa47120685540dad931af..cba95b6cf7dde82d65172ab3d44f74723c4b3092 100644
--- a/test/box/call.test.py
+++ b/test/box/call.test.py
@@ -14,7 +14,7 @@ sql("call f1()")
 
 # A test case for https://github.com/tarantool/tarantool/issues/44
 # IPROTO required!
-sql("call dostring('box.raise(33333, \"Hey!\")')")
+sql("call dostring('box.error(33333, \"Hey!\")')")
 
 print """
 # A test case for Bug#103491
@@ -70,7 +70,7 @@ sql("call f1('jason', 1, 'test', 2, 'stewart')")
 
 admin("space = box.schema.create_space('tweedledum', { id = 0 })")
 admin("space:create_index('primary', { type = 'hash' })")
- 
+
 admin("function myreplace(...) return space:replace{...} end")
 admin("function myinsert(...) return space:insert{...} end")
 sql("insert into t0 values (1, 'test box delete')")
diff --git a/test/box/cfg.result b/test/box/cfg.result
index 04c0a7f051d4a8d7af2574b17904bb9279c7a069..f063128c00b65ce88c1b9abac8fd29610ab24c6f 100644
--- a/test/box/cfg.result
+++ b/test/box/cfg.result
@@ -2,7 +2,7 @@
 --# push filter 'admin: .*' to 'admin: <uri>'
 box.cfg.nosuchoption = 1
 ---
-- error: '[string "-- load_cfg.lua - internal file..."]:127: Attempt to modify a read-only
+- error: '[string "-- load_cfg.lua - internal file..."]:126: Attempt to modify a read-only
     table'
 ...
 t = {} for k,v in pairs(box.cfg) do if type(v) ~= 'table' and type(v) ~= 'function' then table.insert(t, k..': '..tostring(v)) end end
diff --git a/test/box/errinj.result b/test/box/errinj.result
index b12dcf3861ef1132049fb4c0361740b3e3b92460..e525ed83c1d11dab10a269a7d30ab154aca470f6 100644
--- a/test/box/errinj.result
+++ b/test/box/errinj.result
@@ -1,4 +1,4 @@
-errinj = require('errinj')
+errinj = require('box.error.injection')
 ---
 ...
 space = box.schema.create_space('tweedledum')
@@ -196,7 +196,7 @@ s_disabled.enabled
 ...
 s_disabled:insert{0}
 ---
-- error: 'No index #0 is defined in space 512'
+- error: 'No index #0 is defined in space ''disabled'''
 ...
 s_withindex:create_index('secondary', { type = 'tree', parts = { 2, 'num'} })
 ---
@@ -271,7 +271,7 @@ box.space['withdata']
 ...
 s_withdata:create_index('another', { type = 'tree', parts = { 5, 'num' }, unique = false})
 ---
-- error: Space 514 does not exist
+- error: Space '#514' does not exist
 ...
 s_withdata.index.another
 ---
diff --git a/test/box/errinj.test.lua b/test/box/errinj.test.lua
index 45bebb3f53f9415929048e0466303c325c8ef814..d16edf5c9d85b9de3c613aaef34e611b2fee85c7 100644
--- a/test/box/errinj.test.lua
+++ b/test/box/errinj.test.lua
@@ -1,4 +1,4 @@
-errinj = require('errinj')
+errinj = require('box.error.injection')
 
 space = box.schema.create_space('tweedledum')
 space:create_index('primary', { type = 'hash' })
diff --git a/test/box/errinj_index.result b/test/box/errinj_index.result
index a13929773801ed7f204bb5168778b42bcb5c000f..0bce3216dacf3de941595092b597ceb614018a3d 100644
--- a/test/box/errinj_index.result
+++ b/test/box/errinj_index.result
@@ -1,4 +1,4 @@
-errinj = require('errinj')
+errinj = require('box.error.injection')
 ---
 ...
 -- Check a failed realloc in tree index.
diff --git a/test/box/errinj_index.test.lua b/test/box/errinj_index.test.lua
index cf4baaf4c3af3584082ffae0e21f2e41511edce4..3a339d4aca6007e14c35271211903ab268327824 100644
--- a/test/box/errinj_index.test.lua
+++ b/test/box/errinj_index.test.lua
@@ -1,4 +1,4 @@
-errinj = require('errinj')
+errinj = require('box.error.injection')
 
 -- Check a failed realloc in tree index.
 
diff --git a/test/box/misc.result b/test/box/misc.result
index 351a4edfc542b17f51772d973662a19466094985..cc87edfc46dc383cfbbe214d84da93021f0b7fbc 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -22,7 +22,6 @@ t
   - error
   - index
   - info
-  - raise
   - rollback
   - schema
   - slab
@@ -35,36 +34,25 @@ t = nil
 ---
 ...
 ----------------
--- # box.raise
+-- # box.error
 ----------------
 --# stop server default
 --# start server default
--- during server starting there could be exceptions,
--- therefore box.raise() call can throw and can not throw,
--- that's why we can't test box.raise() result
-1 + 1
----
-- 2
-...
-box.raise(123, 'test')
----
-- error: 'box.raise(): unknown error code'
-...
-box.raise(0, 'the other test')
+box.error({code = 123, reason = 'test'})
 ---
-- error: the other test
+- error: test
 ...
-box.raise(12, 345)
+box.error(box.error.ILLEGAL_PARAMS, "bla bla")
 ---
-- error: '345'
+- error: Illegal parameters, bla bla
 ...
-box.raise()
+box.error()
 ---
-- error: '345'
+- error: Illegal parameters, bla bla
 ...
-box.raise()
+box.error()
 ---
-- error: '345'
+- error: Illegal parameters, bla bla
 ...
 space = box.space.tweedledum
 ---
diff --git a/test/box/misc.test.lua b/test/box/misc.test.lua
index 94c70cb5b6d830b8290a5bc97c3a348f3ae77090..4a5ea92d6a2b8ceeebbdc13d6ba5a6ab58a0dbd5 100644
--- a/test/box/misc.test.lua
+++ b/test/box/misc.test.lua
@@ -10,21 +10,15 @@ t
 t = nil
 
 ----------------
--- # box.raise
+-- # box.error
 ----------------
 
 --# stop server default
 --# start server default
--- during server starting there could be exceptions,
--- therefore box.raise() call can throw and can not throw,
--- that's why we can't test box.raise() result
-
-1 + 1
-box.raise(123, 'test')
-box.raise(0, 'the other test')
-box.raise(12, 345)
-box.raise()
-box.raise()
+box.error({code = 123, reason = 'test'})
+box.error(box.error.ILLEGAL_PARAMS, "bla bla")
+box.error()
+box.error()
 
 space = box.space.tweedledum
 
diff --git a/test/box/sql.result b/test/box/sql.result
index 764055da360e41705bd97128c0b21b94c28de046..cd40f5349dea5e2321d31a20ee043b4966752c6a 100644
--- a/test/box/sql.result
+++ b/test/box/sql.result
@@ -168,19 +168,19 @@ select * from t1 where k0 = 0
 ---
 - error:
     errcode: ER_NO_SUCH_SPACE
-    errmsg: Space 1 does not exist
+    errmsg: Space '#1' does not exist
 ...
 select * from t65537 where k0 = 0
 ---
 - error:
     errcode: ER_NO_SUCH_SPACE
-    errmsg: Space 65537 does not exist
+    errmsg: Space '#65537' does not exist
 ...
 select * from t4294967295 where k0 = 0
 ---
 - error:
     errcode: ER_NO_SUCH_SPACE
-    errmsg: Space 4294967295 does not exist
+    errmsg: Space '#4294967295' does not exist
 ...
 box.space[0]:drop()
 ---
diff --git a/test/replication/hot_standby.result b/test/replication/hot_standby.result
index 24ed629b95eb11755ad2d986e90664219b916d96..3756f45d96df68b83e651ea6b984313490a71389 100644
--- a/test/replication/hot_standby.result
+++ b/test/replication/hot_standby.result
@@ -9,7 +9,8 @@ box.schema.user.grant('guest', 'read,write,execute', 'universe')
 --# set connection default
 --# setopt delimiter ';'
 --# set connection default, hot_standby, replica
-while box.space['_priv']:len() < 1 do require('fiber').sleep(0.01) end;
+fiber = require('fiber')
+while box.space['_priv']:len() < 1 do fiber.sleep(0.001) end;
 ---
 ...
 do
@@ -47,7 +48,7 @@ do
 
     function _wait_lsn(_lsnd)
         while _get_pri_lsn() < _lsnd + begin_lsn do
-            require('fiber').sleep(0.001)
+            fiber.sleep(0.001)
         end
         begin_lsn = begin_lsn + _lsnd
     end
@@ -118,9 +119,11 @@ _select(1, 10)
 - [10, 'the tuple 10']
 ...
 --# stop server default
-require('fiber').sleep(0.2)
+--# set connection hot_standby
+while box.info.status ~= 'running' do fiber.sleep(0.001) end
 ---
 ...
+--# set connection replica
 -- hot_standby.listen is garbage, since hot_standby.lua
 -- uses MASTER environment variable for its listen
 --# set variable hot_standby_port to 'hot_standby.master'
diff --git a/test/replication/hot_standby.test.lua b/test/replication/hot_standby.test.lua
index 997dbd1395da4394267c58d64ecfab1a345450c3..911b1dd7719a56a20d3a22fa0dc5e23a45069aaf 100644
--- a/test/replication/hot_standby.test.lua
+++ b/test/replication/hot_standby.test.lua
@@ -8,7 +8,8 @@ box.schema.user.grant('guest', 'read,write,execute', 'universe')
 
 --# setopt delimiter ';'
 --# set connection default, hot_standby, replica
-while box.space['_priv']:len() < 1 do require('fiber').sleep(0.01) end;
+fiber = require('fiber')
+while box.space['_priv']:len() < 1 do fiber.sleep(0.001) end;
 do
     local pri_id = ''
     local begin_lsn = 0
@@ -44,7 +45,7 @@ do
 
     function _wait_lsn(_lsnd)
         while _get_pri_lsn() < _lsnd + begin_lsn do
-            require('fiber').sleep(0.001)
+            fiber.sleep(0.001)
         end
         begin_lsn = begin_lsn + _lsnd
     end
@@ -69,7 +70,9 @@ _wait_lsn(10)
 _select(1, 10)
 
 --# stop server default
-require('fiber').sleep(0.2)
+--# set connection hot_standby
+while box.info.status ~= 'running' do fiber.sleep(0.001) end
+--# set connection replica
 
 -- hot_standby.listen is garbage, since hot_standby.lua
 -- uses MASTER environment variable for its listen