diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 117879382ef8b0c52aacfec60eb71ee2e7cd3745..25171da8e81e9bc7c61afb01af6990cdbe196a12 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -106,6 +106,7 @@ set (server_sources
      lua/trigger.cc
      lua/ipc.cc
      lua/msgpack.cc
+     lua/net_box.cc
      lua/utils.cc
      lua/errno.c
      lua/bsdsocket.cc
diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc
index 87e26cf4c0ca8c4d18d45d1db02b349ccdfe790d..5c56e67ddde0a7aa53154378dfabc54554cb48b5 100644
--- a/src/box/lua/call.cc
+++ b/src/box/lua/call.cc
@@ -467,26 +467,6 @@ SetuidGuard::~SetuidGuard()
 		fiber_set_user(fiber(), orig_credentials);
 }
 
-/**
- * A quick approximation if a Lua table is an array.
- *
- * JSON can only have strings as keys, so if the first
- * table key is 1, it's definitely not a json map,
- * and very likely an array.
- */
-static inline bool
-lua_isarray(struct lua_State *L, int i)
-{
-	if (lua_istable(L, i) == false)
-		return false;
-	lua_pushnil(L);
-	if (lua_next(L, i) == 0) /* the table is empty */
-		return true;
-	bool index_starts_at_1 = lua_isnumber(L, -2) &&
-		lua_tonumber(L, -2) == 1;
-	lua_pop(L, 2);
-	return index_starts_at_1;
-}
 
 static inline void
 execute_c_call(struct func *func, struct request *request, struct obuf *out)
@@ -605,7 +585,7 @@ execute_lua_call(lua_State *L, struct func *func, struct request *request,
 	try {
 		/** Check if we deal with a table of tables. */
 		int nrets = lua_gettop(L);
-		if (nrets == 1 && lua_isarray(L, 1)) {
+		if (nrets == 1 && luaL_isarray(L, 1)) {
 			/*
 			 * The table is not empty and consists of tables
 			 * or tuples. Treat each table element as a tuple,
@@ -613,7 +593,7 @@ execute_lua_call(lua_State *L, struct func *func, struct request *request,
 		 */
 			lua_pushnil(L);
 			int has_keys = lua_next(L, 1);
-			if (has_keys  && (lua_isarray(L, lua_gettop(L)) || lua_istuple(L, -1))) {
+			if (has_keys && (luaL_isarray(L, lua_gettop(L)) || lua_istuple(L, -1))) {
 				do {
 					luamp_encode_tuple(L, luaL_msgpack_default,
 							   &stream, -1);
@@ -626,12 +606,7 @@ execute_lua_call(lua_State *L, struct func *func, struct request *request,
 			}
 		}
 		for (int i = 1; i <= nrets; ++i) {
-			if (lua_isarray(L, i) || lua_istuple(L, i)) {
-				luamp_encode_tuple(L, luaL_msgpack_default, &stream, i);
-			} else {
-				luamp_encode_array(luaL_msgpack_default, &stream, 1);
-				luamp_encode(L, luaL_msgpack_default, &stream, i);
-			}
+			luamp_convert_tuple(L, luaL_msgpack_default, &stream, i);
 			++count;
 		}
 
diff --git a/src/box/lua/tuple.cc b/src/box/lua/tuple.cc
index ac75b438f287e56e3cebd44b65f6ff6e08ff63bb..f67e23d59bd07a46765754080c453d41a21ad703 100644
--- a/src/box/lua/tuple.cc
+++ b/src/box/lua/tuple.cc
@@ -171,6 +171,30 @@ luamp_encode_extension_box(struct lua_State *L, int idx,
 	return MP_EXT;
 }
 
+void
+luamp_convert_tuple(struct lua_State *L, struct luaL_serializer *cfg,
+		    struct mpstream *stream, int index)
+{
+	if (luaL_isarray(L, index) || lua_istuple(L, index)) {
+		luamp_encode_tuple(L, cfg, stream, index);
+	} else {
+		luamp_encode_array(cfg, stream, 1);
+		luamp_encode(L, cfg, stream, index);
+	}
+}
+
+void
+luamp_convert_key(struct lua_State *L, struct luaL_serializer *cfg,
+		  struct mpstream *stream, int index)
+{
+	/* Performs keyfy() logic */
+	if (lua_isnil(L, index)) {
+		luamp_encode_array(cfg, stream, 0);
+	} else {
+		return luamp_convert_tuple(L, cfg, stream, index);
+	}
+}
+
 void
 luamp_encode_tuple(struct lua_State *L, struct luaL_serializer *cfg,
 		   struct mpstream *stream, int index)
diff --git a/src/box/lua/tuple.h b/src/box/lua/tuple.h
index def6d88d8aede83df4b059d900a0e6f8fb973894..c17ce7da112b0a46b3a02974964cb2d6e92876df 100644
--- a/src/box/lua/tuple.h
+++ b/src/box/lua/tuple.h
@@ -55,9 +55,17 @@ lbox_pushtupleornil(lua_State *L, box_tuple_t *tuple)
 struct tuple *lua_istuple(struct lua_State *L, int narg);
 
 void
-luamp_encode_tuple(struct lua_State *L, struct luaL_serializer *cfg,
+luamp_convert_key(struct lua_State *L, struct luaL_serializer *cfg,
 		  struct mpstream *stream, int index);
 
+void
+luamp_convert_tuple(struct lua_State *L, struct luaL_serializer *cfg,
+		    struct mpstream *stream, int index);
+
+void
+luamp_encode_tuple(struct lua_State *L, struct luaL_serializer *cfg,
+		    struct mpstream *stream, int index);
+
 char *
 lbox_encode_tuple_on_gc(lua_State *L, int idx, size_t *p_len);
 
diff --git a/src/lib/msgpuck b/src/lib/msgpuck
index b261d2c986f4de373e947a7d6eb6ca7d18a66488..0fb5dddd2a7f2ec4dd55a22e9c81d65bc0b992b0 160000
--- a/src/lib/msgpuck
+++ b/src/lib/msgpuck
@@ -1 +1 @@
-Subproject commit b261d2c986f4de373e947a7d6eb6ca7d18a66488
+Subproject commit 0fb5dddd2a7f2ec4dd55a22e9c81d65bc0b992b0
diff --git a/src/lib/small/ibuf.h b/src/lib/small/ibuf.h
index 40da17f53a2865ce5043c946943d952860c8ffe4..7b73660a310553f6de4ac2c42a32d214bac2247d 100644
--- a/src/lib/small/ibuf.h
+++ b/src/lib/small/ibuf.h
@@ -159,6 +159,21 @@ ibuf_reserve(struct ibuf *ibuf, size_t size)
 	return ptr;
 }
 
+static inline void *
+ibuf_reserve_cb(void *ptr, size_t *size)
+{
+	struct ibuf *b = (struct ibuf*) ptr;
+	size_t s = *size;
+	return ibuf_reserve(b, s);
+}
+
+static inline void *
+ibuf_alloc_cb(void *ptr, size_t size)
+{
+	struct ibuf *b = (struct ibuf*) ptr;
+	return ibuf_alloc_nothrow(b, size);
+}
+
 #endif /* defined(__cplusplus) */
 
 #endif /* TARANTOOL_SMALL_IBUF_H_INCLUDED */
diff --git a/src/lua/fiber.cc b/src/lua/fiber.cc
index 823ffcf7524c8d32a469aa1ebbab1ec21c543d25..4c4fe8020ca8f381c97d5027fcdc97b73433510b 100644
--- a/src/lua/fiber.cc
+++ b/src/lua/fiber.cc
@@ -161,7 +161,12 @@ lbox_pushfiber(struct lua_State *L, struct fiber *f)
 static struct fiber *
 lbox_checkfiber(struct lua_State *L, int index)
 {
-	uint32_t fid = *(uint32_t *) luaL_checkudata(L, index, fiberlib_name);
+	uint32_t fid;
+	if (lua_type(L, index) == LUA_TNUMBER) {
+		fid = lua_tointeger(L, index);
+	} else {
+		fid = *(uint32_t *) luaL_checkudata(L, index, fiberlib_name);
+	}
 	struct fiber *f = fiber_find(fid);
 	if (f == NULL)
 		luaL_error(L, "the fiber is dead");
@@ -564,6 +569,7 @@ static const struct luaL_reg fiberlib[] = {
 	{"id", lbox_fiber_id},
 	{"find", lbox_fiber_find},
 	{"kill", lbox_fiber_kill},
+	{"wakeup", lbox_fiber_wakeup},
 	{"cancel", lbox_fiber_cancel},
 	{"testcancel", lbox_fiber_testcancel},
 	{"create", lbox_fiber_create},
diff --git a/src/lua/init.cc b/src/lua/init.cc
index 11d7ac945cd8ceaf839ae18fdb69368ead9f6d8f..31c7f3a6c19094d50cafb9566060f2ed6f225161 100644
--- a/src/lua/init.cc
+++ b/src/lua/init.cc
@@ -56,6 +56,7 @@ extern "C" {
 #include "third_party/lua-cjson/lua_cjson.h"
 #include "third_party/lua-yaml/lyaml.h"
 #include "lua/msgpack.h"
+#include "lua/net_box.h"
 #include "lua/pickle.h"
 #include "lua/fio.h"
 
@@ -368,6 +369,10 @@ tarantool_lua_init(const char *tarantool_bin, int argc, char **argv)
 	lua_pop(L, 1);
 	luaopen_json(L);
 	lua_pop(L, 1);
+	luaopen_msgpack(L);
+	lua_pop(L, 1);
+	luaopen_net_box(L);
+	lua_pop(L, 1);
 
 #if defined(HAVE_GNU_READLINE)
 	/*
diff --git a/src/lua/msgpack.cc b/src/lua/msgpack.cc
index ca3fe1282024dfdd53650db350602dd1897fc01f..cb5953663d74ef2d5d1c55561dab0d27eebfb5a0 100644
--- a/src/lua/msgpack.cc
+++ b/src/lua/msgpack.cc
@@ -437,7 +437,7 @@ const luaL_reg msgpacklib[] = {
 	{ "encode", lua_msgpack_encode },
 	{ "decode", lua_msgpack_decode },
 	{ "ibuf_decode", lua_ibuf_msgpack_decode },
-	{ "new",    lua_msgpack_new },
+	{ "new",	lua_msgpack_new },
 	{ NULL, NULL}
 };
 
diff --git a/src/lua/msgpackffi.lua b/src/lua/msgpackffi.lua
index b6168ed57b7f9ee213cf8ddeb5048bbe119282d5..f02f72a3e8ae5a57b373c9a24d536dbc4ec45c0a 100644
--- a/src/lua/msgpackffi.lua
+++ b/src/lua/msgpackffi.lua
@@ -232,6 +232,15 @@ local function encode(obj)
     return r
 end
 
+local function encode_ibuf(obj, ibuf)
+    encode_r(ibuf, obj, 0)
+end
+
+local function encode_len(len, wpos)
+    wpos[0] = 0xce
+    ffi.cast(uint32_ptr_t, wpos + 1)[0] = bswap_u32(len)
+end
+
 on_encode(ffi.typeof('uint8_t'), encode_int)
 on_encode(ffi.typeof('uint16_t'), encode_int)
 on_encode(ffi.typeof('uint32_t'), encode_int)
@@ -497,4 +506,9 @@ return {
     decode_unchecked = decode_unchecked;
     decode = decode_unchecked; -- just for tests
     encode_tuple = encode_tuple;
+    encode_ibuf = encode_ibuf;
+    encode_len = encode_len;
+    encode_map = encode_map;
+    encode_int = encode_int;
+    encode_array = encode_array;
 }
diff --git a/src/lua/net_box.cc b/src/lua/net_box.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9e6fc68e0c9e5702205a096818d73f5c05dc0f46
--- /dev/null
+++ b/src/lua/net_box.cc
@@ -0,0 +1,378 @@
+/*
+ * Copyright 2010-2015, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * 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/net_box.h"
+#include "lua/msgpack.h"
+
+#include <lib/small/ibuf.h>
+#include "scramble.h"
+
+/* TODO: net.box depends on src/box/ */
+#include "box/iproto_constants.h"
+#include "box/lua/tuple.h" /* luamp_convert_tuple() / luamp_convert_key() */
+
+#include <msgpuck/msgpuck.h> /* mp_store_u32() */
+#include "third_party/base64.h"
+
+#define cfg luaL_msgpack_default
+
+static inline size_t
+netbox_prepare_request(lua_State *L, struct mpstream *stream, uint32_t r_type)
+{
+	struct ibuf *ibuf = (struct ibuf *) lua_topointer(L, 1);
+	uint64_t sync = luaL_touint64(L, 2);
+
+	mpstream_init(stream, ibuf, ibuf_reserve_cb, ibuf_alloc_cb);
+
+	/* Remember initial size of ibuf (see netbox_encode_request()) */
+	size_t used = ibuf_used(ibuf);
+
+	/* Reserve and skip space for fixheader */
+	size_t fixheader_size = mp_sizeof_uint(UINT32_MAX);
+	mpstream_reserve(stream, fixheader_size);
+	mpstream_advance(stream, fixheader_size);
+
+	/* encode header */
+	luamp_encode_map(cfg, stream, 2);
+
+	luamp_encode_uint(cfg, stream, IPROTO_SYNC);
+	luamp_encode_uint(cfg, stream, sync);
+
+	luamp_encode_uint(cfg, stream, IPROTO_REQUEST_TYPE);
+	luamp_encode_uint(cfg, stream, r_type);
+
+	/* Caller should remember how many bytes was used in ibuf */
+	return used;
+}
+
+static inline void
+netbox_encode_request(struct mpstream *stream, size_t initial_size)
+{
+	mpstream_flush(stream);
+
+	struct ibuf *ibuf = (struct ibuf *) stream->ctx;
+
+	/*
+	 * Calculation the start position in ibuf by getting current size
+	 * and then substracting initial size. Since we don't touch
+	 * ibuf->rpos during encoding this approach should always work
+	 * even on realloc or memmove inside ibuf.
+	 */
+	size_t fixheader_size = mp_sizeof_uint(UINT32_MAX);
+	size_t used = ibuf_used(ibuf);
+	assert(initial_size + fixheader_size <= used);
+	size_t total_size = used - initial_size;
+	char *fixheader = ibuf->wpos - total_size;
+	assert(fixheader >= ibuf->rpos);
+
+	/* patch skipped len */
+	*(fixheader++) = 0xce;
+	/* fixheader size is not included */
+	mp_store_u32(fixheader, total_size - fixheader_size);
+}
+
+static int
+netbox_encode_ping(lua_State *L)
+{
+	if (lua_gettop(L) < 2)
+		return luaL_error(L, "Usage: netbox.encode_ping(ibuf, sync)");
+
+	struct mpstream stream;
+	size_t svp = netbox_prepare_request(L, &stream, IPROTO_PING);
+	netbox_encode_request(&stream, svp);
+	return 0;
+}
+
+static int
+netbox_encode_auth(lua_State *L)
+{
+	if (lua_gettop(L) < 5)
+		return luaL_error(L, "Usage: netbox.encode_update(ibuf, sync, "
+		       "user, password, greeting)");
+
+	struct mpstream stream;
+	size_t svp = netbox_prepare_request(L, &stream, IPROTO_AUTH);
+
+	size_t user_len;
+	const char *user = lua_tolstring(L, 3, &user_len);
+	size_t password_len;
+	const char *password = lua_tolstring(L, 4, &password_len);
+	size_t greeting_len;
+	const char *greeting = lua_tolstring(L, 5, &greeting_len);
+	if (greeting_len != IPROTO_GREETING_SIZE)
+		return luaL_error(L, "Invalid greeting");
+
+	/* Adapted from xrow_encode_auth() */
+	luamp_encode_map(cfg, &stream, password != NULL ? 2 : 1);
+	luamp_encode_uint(cfg, &stream, IPROTO_USER_NAME);
+	luamp_encode_str(cfg, &stream, user, user_len);
+	if (password != NULL) { /* password can be omitted */
+		char salt[SCRAMBLE_SIZE];
+		char scramble[SCRAMBLE_SIZE];
+		if (base64_decode(greeting + 64, SCRAMBLE_BASE64_SIZE, salt,
+				  SCRAMBLE_SIZE) != SCRAMBLE_SIZE)
+			return luaL_error(L, "invalid salt");
+		scramble_prepare(scramble, salt, password, password_len);
+		luamp_encode_uint(cfg, &stream, IPROTO_TUPLE);
+		luamp_encode_array(cfg, &stream, 2);
+		luamp_encode_str(cfg, &stream, "chap-sha1", strlen("chap-sha1"));
+		luamp_encode_str(cfg, &stream, scramble, SCRAMBLE_SIZE);
+	}
+
+	netbox_encode_request(&stream, svp);
+	return 0;
+}
+
+static int
+netbox_encode_call(lua_State *L)
+{
+	if (lua_gettop(L) < 4)
+		return luaL_error(L, "Usage: netbox.encode_call(ibuf, sync, "
+		       "function_name, args)");
+
+	struct mpstream stream;
+	size_t svp = netbox_prepare_request(L, &stream, IPROTO_CALL);
+
+	luamp_encode_map(cfg, &stream, 2);
+
+	/* encode proc name */
+	size_t name_len;
+	const char *name = lua_tolstring(L, 3, &name_len);
+	luamp_encode_uint(cfg, &stream, IPROTO_FUNCTION_NAME);
+	luamp_encode_str(cfg, &stream, name, name_len);
+
+	/* encode args */
+	luamp_encode_uint(cfg, &stream, IPROTO_TUPLE);
+	luamp_encode_tuple(L, cfg, &stream, 4);
+
+	netbox_encode_request(&stream, svp);
+	return 0;
+}
+
+static int
+netbox_encode_eval(lua_State *L)
+{
+	if (lua_gettop(L) < 4)
+		return luaL_error(L, "Usage: netbox.encode_eval(ibuf, sync, "
+		       "expr, args)");
+
+	struct mpstream stream;
+	size_t svp = netbox_prepare_request(L, &stream, IPROTO_EVAL);
+
+	luamp_encode_map(cfg, &stream, 2);
+
+	/* encode expr */
+	size_t expr_len;
+	const char *expr = lua_tolstring(L, 3, &expr_len);
+	luamp_encode_uint(cfg, &stream, IPROTO_EXPR);
+	luamp_encode_str(cfg, &stream, expr, expr_len);
+
+	/* encode args */
+	luamp_encode_uint(cfg, &stream, IPROTO_TUPLE);
+	luamp_encode_tuple(L, cfg, &stream, 4);
+
+	netbox_encode_request(&stream, svp);
+	return 0;
+}
+
+static int
+netbox_encode_select(lua_State *L)
+{
+	if (lua_gettop(L) < 8)
+		return luaL_error(L, "Usage netbox.encode_select(ibuf, sync, "
+				  "space_id, index_id, iterator, offset, "
+				  "limit, key)");
+
+	struct mpstream stream;
+	size_t svp = netbox_prepare_request(L, &stream, IPROTO_SELECT);
+
+	luamp_encode_map(cfg, &stream, 6);
+
+	uint32_t space_id = lua_tointeger(L, 3);
+	uint32_t index_id = lua_tointeger(L, 4);
+	int iterator = lua_tointeger(L, 5);
+	uint32_t offset = lua_tointeger(L, 6);
+	uint32_t limit = lua_tointeger(L, 7);
+
+	/* encode space_id */
+	luamp_encode_uint(cfg, &stream, IPROTO_SPACE_ID);
+	luamp_encode_uint(cfg, &stream, space_id);
+
+	/* encode index_id */
+	luamp_encode_uint(cfg, &stream, IPROTO_INDEX_ID);
+	luamp_encode_uint(cfg, &stream, index_id);
+
+	/* encode iterator */
+	luamp_encode_uint(cfg, &stream, IPROTO_ITERATOR);
+	luamp_encode_uint(cfg, &stream, iterator);
+
+	/* encode offset */
+	luamp_encode_uint(cfg, &stream, IPROTO_OFFSET);
+	luamp_encode_uint(cfg, &stream, offset);
+
+	/* encode limit */
+	luamp_encode_uint(cfg, &stream, IPROTO_LIMIT);
+	luamp_encode_uint(cfg, &stream, limit);
+
+	/* encode key */
+	luamp_encode_uint(cfg, &stream, IPROTO_KEY);
+	luamp_convert_key(L, cfg, &stream, 8);
+
+	netbox_encode_request(&stream, svp);
+	return 0;
+}
+
+static inline int
+netbox_encode_insert_or_replace(lua_State *L, uint32_t reqtype)
+{
+	if (lua_gettop(L) < 4)
+		return luaL_error(L, "Usage: netbox.encode_insert(ibuf, sync, "
+		       "space_id, tuple)");
+	lua_settop(L, 4);
+
+	struct mpstream stream;
+	size_t svp = netbox_prepare_request(L, &stream, reqtype);
+
+	luamp_encode_map(cfg, &stream, 2);
+
+	/* encode space_id */
+	uint32_t space_id = lua_tointeger(L, 3);
+	luamp_encode_uint(cfg, &stream, IPROTO_SPACE_ID);
+	luamp_encode_uint(cfg, &stream, space_id);
+
+	/* encode args */
+	luamp_encode_uint(cfg, &stream, IPROTO_TUPLE);
+	luamp_encode_tuple(L, cfg, &stream, 4);
+
+	netbox_encode_request(&stream, svp);
+	return 0;
+}
+
+static int
+netbox_encode_insert(lua_State *L)
+{
+	return netbox_encode_insert_or_replace(L, IPROTO_INSERT);
+}
+
+static int
+netbox_encode_replace(lua_State *L)
+{
+	return netbox_encode_insert_or_replace(L, IPROTO_REPLACE);
+}
+
+static int
+netbox_encode_delete(lua_State *L)
+{
+	if (lua_gettop(L) < 5)
+		return luaL_error(L, "Usage: netbox.encode_delete(ibuf, sync, "
+		       "space_id, index_id, key)");
+
+	struct mpstream stream;
+	size_t svp = netbox_prepare_request(L, &stream, IPROTO_DELETE);
+
+	luamp_encode_map(cfg, &stream, 3);
+
+	/* encode space_id */
+	uint32_t space_id = lua_tointeger(L, 3);
+	luamp_encode_uint(cfg, &stream, IPROTO_SPACE_ID);
+	luamp_encode_uint(cfg, &stream, space_id);
+
+	/* encode space_id */
+	uint32_t index_id = lua_tointeger(L, 4);
+	luamp_encode_uint(cfg, &stream, IPROTO_INDEX_ID);
+	luamp_encode_uint(cfg, &stream, index_id);
+
+	/* encode key */
+	luamp_encode_uint(cfg, &stream, IPROTO_KEY);
+	luamp_convert_key(L, cfg, &stream, 5);
+
+	netbox_encode_request(&stream, svp);
+	return 0;
+}
+
+static int
+netbox_encode_update(lua_State *L)
+{
+	if (lua_gettop(L) < 6)
+		return luaL_error(L, "Usage: netbox.encode_update(ibuf, sync, "
+		       "space_id, index_id, key, ops)");
+
+	struct mpstream stream;
+	size_t svp = netbox_prepare_request(L, &stream, IPROTO_UPDATE);
+
+	luamp_encode_map(cfg, &stream, 5);
+
+	/* encode space_id */
+	uint32_t space_id = lua_tointeger(L, 3);
+	luamp_encode_uint(cfg, &stream, IPROTO_SPACE_ID);
+	luamp_encode_uint(cfg, &stream, space_id);
+
+	/* encode index_id */
+	uint32_t index_id = lua_tointeger(L, 4);
+	luamp_encode_uint(cfg, &stream, IPROTO_INDEX_ID);
+	luamp_encode_uint(cfg, &stream, index_id);
+
+	/* encode index_id */
+	luamp_encode_uint(cfg, &stream, IPROTO_INDEX_BASE);
+	luamp_encode_uint(cfg, &stream, 1);
+
+	/* encode in reverse order for speedup - see luamp_encode() code */
+	/* encode ops */
+	luamp_encode_uint(cfg, &stream, IPROTO_TUPLE);
+	luamp_encode_tuple(L, cfg, &stream, 6);
+	lua_pop(L, 1); /* ops */
+
+	/* encode key */
+	luamp_encode_uint(cfg, &stream, IPROTO_KEY);
+	luamp_convert_key(L, cfg, &stream, 5);
+
+	netbox_encode_request(&stream, svp);
+	return 0;
+}
+
+int
+luaopen_net_box(struct lua_State *L)
+{
+	const luaL_reg net_box_lib[] = {
+		{ "encode_ping",    netbox_encode_ping },
+		{ "encode_call",    netbox_encode_call },
+		{ "encode_eval",    netbox_encode_eval },
+		{ "encode_select",  netbox_encode_select },
+		{ "encode_insert",  netbox_encode_insert },
+		{ "encode_replace", netbox_encode_replace },
+		{ "encode_delete",  netbox_encode_delete },
+		{ "encode_update",  netbox_encode_update },
+		{ "encode_auth",    netbox_encode_auth },
+		{ NULL, NULL}
+	};
+	luaL_register(L, "net.box.lib", net_box_lib);
+	return 1;
+}
diff --git a/src/lua/net_box.h b/src/lua/net_box.h
new file mode 100644
index 0000000000000000000000000000000000000000..5a48476a318af73b5e4660c3e9444201e5478c7a
--- /dev/null
+++ b/src/lua/net_box.h
@@ -0,0 +1,47 @@
+#ifndef TARANTOOL_LUA_NET_BOX_H_INCLUDED
+#define TARANTOOL_LUA_NET_BOX_H_INCLUDED
+/*
+ * Copyright 2010-2015, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * 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.
+ */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+struct lua_State;
+
+int
+luaopen_net_box(struct lua_State *L);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
+
+#endif /* TARANTOOL_LUA_NET_BOX_H_INCLUDED */
diff --git a/src/lua/net_box.lua b/src/lua/net_box.lua
index 536e681ce0ebe0a7484d78dca3a78261b2aa1ff6..76cbfa1371ab294b1606c8aadf243b8b86259b26 100644
--- a/src/lua/net_box.lua
+++ b/src/lua/net_box.lua
@@ -1,12 +1,12 @@
 -- net_box.lua (internal file)
 
+local internal = require 'net.box.lib'
 local msgpack = require 'msgpack'
 local fiber = require 'fiber'
 local socket = require 'socket'
 local log = require 'log'
 local errno = require 'errno'
 local ffi = require 'ffi'
-local digest = require 'digest'
 local yaml = require 'yaml'
 local urilib = require 'uri'
 local buffer = require 'buffer'
@@ -53,39 +53,24 @@ local mapping_mt = { __serialize = 'mapping' }
 local CONSOLE_FAKESYNC  = 15121974
 local CONSOLE_DELIMITER = "$EOF$"
 
-local function request(header, body)
-    -- hint msgpack to always encode header and body as a map
-    header = msgpack.encode(setmetatable(header, mapping_mt))
-    body = msgpack.encode(setmetatable(body, mapping_mt))
+local ch_buf = {}
+local ch_buf_size = 0
 
-    local len = msgpack.encode(string.len(header) + string.len(body))
-
-    return len .. header .. body
-end
-
-
-local function strxor(s1, s2)
-    local res = ''
-    for i = 1, string.len(s1) do
-        if i > string.len(s2) then
-            break
-        end
-
-        local b1 = string.byte(s1, i)
-        local b2 = string.byte(s2, i)
-        res = res .. string.char(bit.bxor(b1, b2))
+local function get_channel()
+    if ch_buf_size == 0 then
+        return fiber.channel()
     end
-    return res
+
+    local ch = ch_buf[ch_buf_size]
+    ch_buf[ch_buf_size] = nil
+    ch_buf_size = ch_buf_size - 1
+    return ch
 end
 
-local function keyfy(v)
-    if type(v) == 'table' then
-        return v
-    end
-    if v == nil then
-        return {}
-    end
-    return { v }
+local function free_channel(ch)
+    -- return channel to buffer
+    ch_buf[ch_buf_size + 1] = ch
+    ch_buf_size = ch_buf_size + 1
 end
 
 local function one_tuple(tbl)
@@ -98,84 +83,16 @@ local function one_tuple(tbl)
     return
 end
 
-local proto = {
-    _sync = -1,
-
-    -- sync
-    sync    = function(self)
-        self._sync = self._sync + 1
-        if self._sync >= 0x7FFFFFFF then
-            self._sync = 0
-        end
-        return self._sync
-    end,
-
-
-    ping    = function(sync)
-        return request(
-            { [SYNC] = sync, [TYPE] = PING },
-            {}
-        )
-    end,
-
-
-    -- lua call
-    call = function(sync, proc, args)
-        if args == nil then
-            args = {}
-        end
-        return request(
-            { [SYNC] = sync, [TYPE] = CALL  },
-            { [FUNCTION_NAME] = proc, [TUPLE] = args }
-        )
-    end,
-
-    -- lua eval
-    eval = function(sync, expr, args)
-        if args == nil then
-            args = {}
-        end
-        return request(
-            { [SYNC] = sync, [TYPE] = EVAL  },
-            { [EXPR] = expr, [TUPLE] = args }
-        )
-    end,
-
-    -- insert
-    insert = function(sync, spaceno, tuple)
-        return request(
-            { [SYNC] = sync, [TYPE] = INSERT },
-            { [SPACE_ID] = spaceno, [TUPLE] = tuple }
-        )
-    end,
-
-    -- replace
-    replace = function(sync, spaceno, tuple)
-        return request(
-            { [SYNC] = sync, [TYPE] = REPLACE },
-            { [SPACE_ID] = spaceno, [TUPLE] = tuple }
-        )
-    end,
-
-    -- delete
-    delete = function(sync, spaceno, key, index_id)
-        return request(
-            { [SYNC] = sync, [TYPE] = DELETE },
-            { [SPACE_ID] = spaceno, [INDEX_ID] = index_id, [KEY] = keyfy(key) }
-        )
-    end,
-
-    -- update
-    update = function(sync, spaceno, key, oplist, index_id)
-        return request(
-            { [SYNC] = sync, [TYPE] = UPDATE },
-            { [KEY] = keyfy(key), [INDEX_BASE] = 1 , [TUPLE]  = oplist,
-              [SPACE_ID] = spaceno, [INDEX_ID] = index_id }
-        )
-    end,
-
-    -- select
-    select = function(sync, spaceno, indexno, key, opts)
+local requests = {
+    [PING]    = internal.encode_ping;
+    [AUTH]    = internal.encode_auth;
+    [CALL]    = internal.encode_call;
+    [EVAL]    = internal.encode_eval;
+    [INSERT]  = internal.encode_insert;
+    [REPLACE] = internal.encode_replace;
+    [DELETE] = internal.encode_delete;
+    [UPDATE]  = internal.encode_update;
+    [SELECT]  = function(wbuf, sync, spaceno, indexno, key, opts)
         if opts == nil then
             opts = {}
         end
@@ -187,49 +104,25 @@ local proto = {
             box.error(box.error.NO_SUCH_INDEX, indexno, '#'..tostring(spaceno))
         end
 
-        key = keyfy(key)
-
-        local body = {
-            [SPACE_ID] = spaceno,
-            [INDEX_ID] = indexno,
-            [KEY] = key
-        }
-
+        local limit, offset
         if opts.limit ~= nil then
-            body[LIMIT] = tonumber(opts.limit)
+            limit = tonumber(opts.limit)
         else
-            body[LIMIT] = 0xFFFFFFFF
+            limit = 0xFFFFFFFF
         end
         if opts.offset ~= nil then
-            body[OFFSET] = tonumber(opts.offset)
+            offset = tonumber(opts.offset)
         else
-            body[OFFSET] = 0
+            offset = 0
         end
+        local iterator = require('box.internal').check_iterator_type(opts,
+            key == nil or (type(key) == 'table' and #key == 0))
 
-        body[ITERATOR] = require('box.internal').check_iterator_type(opts, #key == 0)
-
-        return request( { [SYNC] = sync, [TYPE] = SELECT }, body )
-    end,
-
-    auth = function(sync, user, password, handshake)
-        local saltb64 = string.sub(handshake, 65)
-        local salt = string.sub(digest.base64_decode(saltb64), 1, 20)
-
-        local hpassword = digest.sha1(password)
-        local hhpassword = digest.sha1(hpassword)
-        local scramble = digest.sha1(salt .. hhpassword)
-
-        local hash = strxor(hpassword, scramble)
-        return request(
-            { [SYNC] = sync, [TYPE] = AUTH },
-            { [USER] = user, [TUPLE] = { 'chap-sha1', hash } }
-        )
-    end,
-
-    b64decode = digest.base64_decode,
+        internal.encode_select(wbuf, sync, spaceno, indexno, iterator,
+            offset, limit, key)
+    end;
 }
 
-
 local function check_if_space(space)
     if type(space) == 'table' and space.id ~= nil then
         return
@@ -360,10 +253,8 @@ local errno_is_transient = {
 local remote = {}
 
 local remote_methods = {
-    proto = proto,
-
     new = function(cls, host, port, opts)
-        local self = {}
+        local self = { _sync = -1 }
 
         if type(cls) == 'table' then
             setmetatable(self, getmetatable(cls))
@@ -423,7 +314,7 @@ local remote_methods = {
 
         self.is_run = true
         self.state = 'init'
-        self.wbuf = {}
+        self.wbuf = buffer.ibuf(buffer.READAHEAD)
         self.rpos = 1
         self.rlen = 0
 
@@ -442,6 +333,14 @@ local remote_methods = {
         return self
     end,
 
+    -- sync
+    sync    = function(self)
+        self._sync = self._sync + 1
+        if self._sync >= 0x7FFFFFFF then
+            self._sync = 0
+        end
+        return self._sync
+    end,
 
     ping    = function(self)
         if type(self) ~= 'table' then
@@ -450,10 +349,7 @@ local remote_methods = {
         if not self:is_connected() then
             return false
         end
-        local sync = self.proto:sync()
-        local req = self.proto.ping(sync)
-
-        local res = self:_request('ping', false)
+        local res = self:_request(PING, false)
 
 
         if res == nil then
@@ -467,8 +363,11 @@ local remote_methods = {
     end,
 
     _console = function(self, line)
-        local res = self:_request_raw('eval', CONSOLE_FAKESYNC,
-            line..CONSOLE_DELIMITER.."\n", true)
+        local data = line..CONSOLE_DELIMITER.."\n\n"
+        ffi.copy(self.wbuf.wpos, data, #data)
+        self.wbuf.wpos = self.wbuf.wpos + #data
+
+        local res = self:_request_raw(EVAL, CONSOLE_FAKESYNC, data, true)
         return res.body[DATA]
     end,
 
@@ -479,7 +378,7 @@ local remote_methods = {
 
         proc_name = tostring(proc_name)
 
-        local res = self:_request('call', true, proc_name, {...})
+        local res = self:_request(CALL, true, proc_name, {...})
         return res.body[DATA]
 
     end,
@@ -490,7 +389,7 @@ local remote_methods = {
         end
 
         expr = tostring(expr)
-        local data = self:_request('eval', true, expr, {...}).body[DATA]
+        local data = self:_request(EVAL, true, expr, {...}).body[DATA]
         local data_len = #data
         if data_len == 1 then
             return data[1]
@@ -577,15 +476,26 @@ local remote_methods = {
         self:_error_waiters(emsg)
         self.rpos = 1
         self.rlen = 0
-        self.wbuf = {}
         self.handshake = ''
     end,
 
+    _wakeup_client = function(self, hdr, body)
+        local sync = hdr[SYNC]
+
+        local ch = self.ch.sync[sync]
+        if ch ~= nil then
+            ch.response = { hdr = hdr, body = body }
+            fiber.wakeup(ch.fid)
+        else
+            log.warn("Unexpected response %s", tostring(sync))
+        end
+    end,
+
     _error_waiters = function(self, emsg)
         local waiters = self.ch.sync
         self.ch.sync = {}
         for sync, channel in pairs(waiters) do
-            channel:put{
+            channel.response = {
                 hdr = {
                     [TYPE] = bit.bor(ERROR_TYPE, box.error.NO_CONNECTION),
                     [SYNC] = sync
@@ -594,6 +504,7 @@ local remote_methods = {
                     [ERROR] = emsg
                 }
             }
+            fiber.wakeup(channel.fid)
         end
     end,
 
@@ -609,27 +520,11 @@ local remote_methods = {
         local hdr = { [SYNC] = CONSOLE_FAKESYNC, [TYPE] = 0 }
         local body = { [DATA] = resp }
 
-        if self.ch.sync[CONSOLE_FAKESYNC] ~= nil then
-            self.ch.sync[CONSOLE_FAKESYNC]:put({hdr = hdr, body = body })
-            self.ch.sync[CONSOLE_FAKESYNC] = nil
-        else
-            log.warn("Unexpected console response: %s", resp)
-        end
+        self:_wakeup_client(hdr, body)
         self.rbuf:read(#resp)
         return 0
     end,
 
-    _wakeup_client = function(self, hdr, body)
-        local sync = hdr[SYNC]
-
-        if self.ch.sync[sync] ~= nil then
-            self.ch.sync[sync]:put({ hdr = hdr, body = body })
-            self.ch.sync[sync] = nil
-        else
-            log.warn("Unexpected response %s", tostring(sync))
-        end
-    end,
-
     _check_binary_response = function(self)
         while true do
             if self.rbuf.rpos + 5 > self.rbuf.wpos then
@@ -682,7 +577,7 @@ local remote_methods = {
         while timeout > 0 and self:_is_state(states) ~= true do
             local started = fiber.time()
             local fid = fiber.id()
-            local ch = fiber.channel()
+            local ch = get_channel()
             for state, _ in pairs(states) do
                 self.wait.state[state] = self.wait.state[state] or {}
                 self.wait.state[state][fid] = fid
@@ -691,6 +586,7 @@ local remote_methods = {
             self.ch.fid[fid] = ch
             ch:get(timeout)
             self.ch.fid[fid] = nil
+            free_channel(ch)
 
             for state, _ in pairs(states) do
                 self.wait.state[state][fid] = nil
@@ -700,7 +596,6 @@ local remote_methods = {
         return self.state
     end,
 
-
     _connect_worker = function(self)
         fiber.name('net.box.connector')
         local connect_states = { init = true, error = true, closed = true }
@@ -735,9 +630,13 @@ local remote_methods = {
                         self._check_response = self._check_console_response
                         -- set delimiter
                         self:_switch_state('schema')
-                        local res = self:_request_raw('eval', CONSOLE_FAKESYNC,
-                            "require('console').delimiter('"..CONSOLE_DELIMITER.."')\n\n",
-                            true)
+
+                        local line = "require('console').delimiter('"..CONSOLE_DELIMITER.."')\n\n"
+                        ffi.copy(self.wbuf.wpos, line, #line)
+                        self.wbuf.wpos = self.wbuf.wpos + #line
+
+                        local res = self:_request_raw(EVAL, CONSOLE_FAKESYNC, line, true)
+
                         if res.hdr[TYPE] ~= OK then
                             self:_fatal(res.body[ERROR])
                         end
@@ -773,7 +672,7 @@ local remote_methods = {
 
         self:_switch_state('auth')
 
-        local auth_res = self:_request_internal('auth',
+        local auth_res = self:_request_internal(AUTH,
             false, self.opts.user, self.opts.password, self.handshake)
 
         if auth_res.hdr[TYPE] ~= OK then
@@ -816,9 +715,9 @@ local remote_methods = {
 
         self:_switch_state('schema')
 
-        local spaces = self:_request_internal('select',
+        local spaces = self:_request_internal(SELECT,
             true, SPACE_SPACE_ID, 0, nil, { iterator = 'ALL' }).body[DATA]
-        local indexes = self:_request_internal('select',
+        local indexes = self:_request_internal(SELECT,
             true, SPACE_INDEX_ID, 0, nil, { iterator = 'ALL' }).body[DATA]
 
         local sl = {}
@@ -930,16 +829,11 @@ local remote_methods = {
     _write_worker = function(self)
         fiber.name('net.box.write')
         while self:_wait_state(self._rw_states) ~= 'closed' do
-            while self.wbuf[1] ~= nil do
-                local s = table.concat(self.wbuf)
-                self.wbuf = {}
-                local written = self.s:syswrite(s)
+            while self.wbuf:size() > 0 do
+                local written = self.s:syswrite(self.wbuf.rpos, self.wbuf:size())
                 if written ~= nil then
-                    if written ~= #s then
-                        table.insert(self.wbuf, string.sub(s, written + 1))
-                    end
+                    self.wbuf.rpos = self.wbuf.rpos + written
                 else
-                    table.insert(self.wbuf, s)
                     if errno_is_transient[errno()] then
                     -- the write is with a timeout to detect FIN
                     -- packet on the receiving end, and close the connection.
@@ -953,13 +847,14 @@ local remote_methods = {
                     end
                 end
             end
+            self.wbuf:reserve(buffer.READAHEAD)
             self:_switch_state(self._to_rstate[self.state])
         end
     end,
 
-    _request = function(self, name, raise, ...)
+    _request = function(self, reqtype, raise, ...)
         if self.console then
-            box.error(box.error.UNSUPPORTED, "console", name)
+            box.error(box.error.UNSUPPORTED, "console", reqtype)
         end
         local fid = fiber.id()
         if self.timeouts[fid] == nil then
@@ -990,29 +885,25 @@ local remote_methods = {
             end
         end
 
-        return self:_request_internal(name, raise, ...)
+        return self:_request_internal(reqtype, raise, ...)
     end,
 
-    _request_raw = function(self, name, sync, request, raise)
+    _request_raw = function(self, reqtype, sync, request, raise)
 
         local fid = fiber.id()
         if self.timeouts[fid] == nil then
             self.timeouts[fid] = TIMEOUT_INFINITY
         end
 
-        table.insert(self.wbuf, request)
-
         self:_switch_state(self._to_wstate[self.state])
 
-        local ch = fiber.channel()
-
+        local ch = { fid = fid; }
         self.ch.sync[sync] = ch
-
-        local response = ch:get(self.timeouts[fid])
+        fiber.sleep(self.timeouts[fid])
+        local response = ch.response
         self.ch.sync[sync] = nil
         self.timeouts[fid] = nil
 
-
         if response == nil then
             if raise then
                 box.error(box.error.TIMEOUT)
@@ -1031,7 +922,7 @@ local remote_methods = {
             })
         end
 
-        if response.body[DATA] ~= nil and name ~= 'eval' then
+        if response.body[DATA] ~= nil and reqtype ~= EVAL then
             if rawget(box, 'tuple') ~= nil then
                 for i, v in pairs(response.body[DATA]) do
                     response.body[DATA][i] =
@@ -1045,35 +936,35 @@ local remote_methods = {
         return response
     end,
 
-    _request_internal = function(self, name, raise, ...)
-        local sync = self.proto:sync()
-        local request = self.proto[name](sync, ...)
-        return self:_request_raw(name, sync, request, raise)
+    _request_internal = function(self, reqtype, raise, ...)
+        local sync = self:sync()
+        local request = requests[reqtype](self.wbuf, sync, ...)
+        return self:_request_raw(reqtype, sync, request, raise)
     end,
 
     -- private (low level) methods
     _select = function(self, spaceno, indexno, key, opts)
-        local res = self:_request('select', true, spaceno, indexno, key, opts)
+        local res = self:_request(SELECT, true, spaceno, indexno, key, opts)
         return res.body[DATA]
     end,
 
     _insert = function(self, spaceno, tuple)
-        local res = self:_request('insert', true, spaceno, tuple)
+        local res = self:_request(INSERT, true, spaceno, tuple)
         return one_tuple(res.body[DATA])
     end,
 
     _replace = function(self, spaceno, tuple)
-        local res = self:_request('replace', true, spaceno, tuple)
+        local res = self:_request(REPLACE, true, spaceno, tuple)
         return one_tuple(res.body[DATA])
     end,
 
     _delete  = function(self, spaceno, key, index_id)
-        local res = self:_request('delete', true, spaceno, key, index_id)
+        local res = self:_request(DELETE, true, spaceno, index_id, key, index_id)
         return one_tuple(res.body[DATA])
     end,
 
     _update = function(self, spaceno, key, oplist, index_id)
-        local res = self:_request('update', true, spaceno, key, oplist, index_id)
+        local res = self:_request(UPDATE, true, spaceno, index_id, key, oplist)
         return one_tuple(res.body[DATA])
     end
 }
diff --git a/src/lua/utils.h b/src/lua/utils.h
index e2990fcfb71c4b49d521f0ee095511c35a96c8c4..2cfce28b5f7da95ba88878372996cfcccfc57f48 100644
--- a/src/lua/utils.h
+++ b/src/lua/utils.h
@@ -421,6 +421,29 @@ luaL_toint64(struct lua_State *L, int idx);
 
 /** \endcond public */
 
+/**
+ * A quick approximation if a Lua table is an array.
+ *
+ * JSON can only have strings as keys, so if the first
+ * table key is 1, it's definitely not a json map,
+ * and very likely an array.
+ */
+static inline bool
+luaL_isarray(struct lua_State *L, int idx)
+{
+	if (!lua_istable(L, idx))
+		return false;
+	if (idx < 0)
+		idx = lua_gettop(L) + idx + 1;
+	lua_pushnil(L);
+	if (lua_next(L, idx) == 0) /* the table is empty */
+		return true;
+	bool index_starts_at_1 = lua_isnumber(L, -2) &&
+		lua_tonumber(L, -2) == 1;
+	lua_pop(L, 2);
+	return index_starts_at_1;
+}
+
 /**
  * Push Lua Table with __serialize = 'map' hint onto the stack.
  * Tables with __serialize hint are properly handled by all serializers.
diff --git a/test/box/net.box.result b/test/box/net.box.result
index f112a388b10967eef6bf98aa884b0078eca66acf..2f38086ed423cfa437c3a54dfed8a87a4f2c668a 100644
--- a/test/box/net.box.result
+++ b/test/box/net.box.result
@@ -288,7 +288,7 @@ cn.space.net_box_test_space:insert{234, 1,2,3}
 ...
 cn.space.net_box_test_space.insert{234, 1,2,3}
 ---
-- error: 'builtin/net.box.lua:237: Use space:method(...) instead space.method(...)'
+- error: 'builtin/net.box.lua:130: Use space:method(...) instead space.method(...)'
 ...
 cn.space.net_box_test_space:replace{354, 1,2,3}
 ---
@@ -584,14 +584,6 @@ res[1][2] == string.rep('a', 50000)
 - true
 ...
 -- auth
-cn.proto.b64decode('gJLocxbO32VmfO8x04xRVxKfgwzmNVM2t6a1ME8XsD0=')
----
-- !!binary gJLocxbO32VmfO8x04xRVxKfgwzmNVM2t6a1ME8XsD0=
-...
-cn.proto.b64decode('gJLoc!!!!!!!')
----
-- !!binary gJLo
-...
 cn = remote:new(LISTEN.host, LISTEN.service, { user = 'netbox', password = '123', wait_connected = true })
 ---
 ...
diff --git a/test/box/net.box.test.lua b/test/box/net.box.test.lua
index 62b3566c85327d684a331f396dda3a8251cdf091..0ea5f5b40e89a2dfc06d39d4fad8133fa47f7a9c 100644
--- a/test/box/net.box.test.lua
+++ b/test/box/net.box.test.lua
@@ -225,8 +225,6 @@ res[1][1] == 1
 res[1][2] == string.rep('a', 50000)
 
 -- auth
-cn.proto.b64decode('gJLocxbO32VmfO8x04xRVxKfgwzmNVM2t6a1ME8XsD0=')
-cn.proto.b64decode('gJLoc!!!!!!!')
 
 cn = remote:new(LISTEN.host, LISTEN.service, { user = 'netbox', password = '123', wait_connected = true })
 cn:is_connected()
diff --git a/test/unit/msgpack.result b/test/unit/msgpack.result
index a9a2e64987cd9fc18a03db282fd7ad3b38617c70..680d81fa946e8f081f1959b6c92d57b5e1b91128 100644
--- a/test/unit/msgpack.result
+++ b/test/unit/msgpack.result
@@ -1,4 +1,4 @@
-1..15
+1..16
     1..135
     # *** test_uints ***
     # uint 0U
@@ -1323,3 +1323,237 @@ ok 14 - subtests
     ok 227 - mp_compare_uint(18446744073709551615, 18446744073709551615) == 0
     # *** test_compare_uints: done ***
 ok 15 - subtests
+    1..230
+    # *** test_format ***
+    ok 1 - Test type on step 0
+    ok 2 - Test value on step 0
+    ok 3 - Test type on step 1
+    ok 4 - Test value on step 1
+    ok 5 - Test type on step 2
+    ok 6 - Test value on step 2
+    ok 7 - Test type on step 3
+    ok 8 - Test value on step 3
+    ok 9 - Test type on step 4
+    ok 10 - Test value on step 4
+    ok 11 - Test type on step 5
+    ok 12 - Test value on step 5
+    ok 13 - Test type on step 6
+    ok 14 - Test value on step 6
+    ok 15 - Test type on step 7
+    ok 16 - Test value on step 7
+    ok 17 - Test type on step 8
+    ok 18 - Test value on step 8
+    ok 19 - Test type on step 9
+    ok 20 - Test value on step 9
+    ok 21 - Test type on step 10
+    ok 22 - Test value on step 10
+    ok 23 - Test type on step 11
+    ok 24 - Test value on step 11
+    ok 25 - Test type on step 12
+    ok 26 - Test value on step 12
+    ok 27 - Test type on step 13
+    ok 28 - Test value on step 13
+    ok 29 - Test type on step 14
+    ok 30 - Test value on step 14
+    ok 31 - Test type on step 0
+    ok 32 - Test value on step 0
+    ok 33 - Test type on step 1
+    ok 34 - Test value on step 1
+    ok 35 - Test type on step 2
+    ok 36 - Test value on step 2
+    ok 37 - Test type on step 3
+    ok 38 - Test value on step 3
+    ok 39 - Test type on step 4
+    ok 40 - Test value on step 4
+    ok 41 - Test type on step 5
+    ok 42 - Test value on step 5
+    ok 43 - Test type on step 6
+    ok 44 - Test value on step 6
+    ok 45 - Test type on step 7
+    ok 46 - Test value on step 7
+    ok 47 - Test type on step 8
+    ok 48 - Test value on step 8
+    ok 49 - Test type on step 9
+    ok 50 - Test value on step 9
+    ok 51 - Test type on step 10
+    ok 52 - Test value on step 10
+    ok 53 - Test type on step 11
+    ok 54 - Test value on step 11
+    ok 55 - Test type on step 12
+    ok 56 - Test value on step 12
+    ok 57 - Test type on step 13
+    ok 58 - Test value on step 13
+    ok 59 - Test type on step 14
+    ok 60 - Test value on step 14
+    ok 61 - check at 551
+    ok 62 - type at 552
+    ok 63 - decode at 553
+    ok 64 - check at 556
+    ok 65 - type at 557
+    ok 66 - check at 561
+    ok 67 - type at 562
+    ok 68 - decode at 563
+    ok 69 - check at 566
+    ok 70 - type at 567
+    ok 71 - decode at 568
+    ok 72 - check at 571
+    ok 73 - type at 572
+    ok 74 - decode at 573
+    ok 75 - check at 576
+    ok 76 - type at 577
+    ok 77 - decode at 578
+    ok 78 - check at 581
+    ok 79 - type at 582
+    ok 80 - decode at 583
+    ok 81 - check at 586
+    ok 82 - type at 587
+    ok 83 - decode at 588
+    ok 84 - check at 591
+    ok 85 - type at 592
+    ok 86 - decode at 593
+    ok 87 - check at 596
+    ok 88 - type at 597
+    ok 89 - decode at 598
+    ok 90 - check at 601
+    ok 91 - type at 602
+    ok 92 - decode at 603
+    ok 93 - check at 606
+    ok 94 - type at 607
+    ok 95 - decode at 608
+    ok 96 - check at 611
+    ok 97 - type at 612
+    ok 98 - decode at 614
+    ok 99 - compare at 615
+    ok 100 - check at 618
+    ok 101 - type at 619
+    ok 102 - decode at 620
+    ok 103 - check at 623
+    ok 104 - type at 624
+    ok 105 - decode at 626
+    ok 106 - compare at 627
+    ok 107 - check at 630
+    ok 108 - type at 631
+    ok 109 - decode at 632
+    ok 110 - check at 635
+    ok 111 - type at 636
+    ok 112 - decode at 638
+    ok 113 - check at 641
+    ok 114 - type at 642
+    ok 115 - check at 646
+    ok 116 - type at 647
+    ok 117 - decode at 648
+    ok 118 - check at 651
+    ok 119 - type at 652
+    ok 120 - decode at 653
+    ok 121 - check at 656
+    ok 122 - type at 657
+    ok 123 - decode at 658
+    ok 124 - check at 661
+    ok 125 - type at 662
+    ok 126 - decode at 663
+    ok 127 - nothing more
+    ok 128 - no magic detected
+    ok 129 - return value on step 0
+    ok 130 - buffer overflow on step 0
+    ok 131 - return value on step 1
+    ok 132 - buffer overflow on step 1
+    ok 133 - return value on step 2
+    ok 134 - buffer overflow on step 2
+    ok 135 - return value on step 3
+    ok 136 - buffer overflow on step 3
+    ok 137 - return value on step 4
+    ok 138 - buffer overflow on step 4
+    ok 139 - return value on step 5
+    ok 140 - buffer overflow on step 5
+    ok 141 - return value on step 6
+    ok 142 - buffer overflow on step 6
+    ok 143 - return value on step 7
+    ok 144 - buffer overflow on step 7
+    ok 145 - return value on step 8
+    ok 146 - buffer overflow on step 8
+    ok 147 - return value on step 9
+    ok 148 - buffer overflow on step 9
+    ok 149 - return value on step 10
+    ok 150 - buffer overflow on step 10
+    ok 151 - return value on step 11
+    ok 152 - buffer overflow on step 11
+    ok 153 - return value on step 12
+    ok 154 - buffer overflow on step 12
+    ok 155 - return value on step 13
+    ok 156 - buffer overflow on step 13
+    ok 157 - return value on step 14
+    ok 158 - buffer overflow on step 14
+    ok 159 - return value on step 15
+    ok 160 - buffer overflow on step 15
+    ok 161 - return value on step 16
+    ok 162 - buffer overflow on step 16
+    ok 163 - return value on step 17
+    ok 164 - buffer overflow on step 17
+    ok 165 - return value on step 18
+    ok 166 - buffer overflow on step 18
+    ok 167 - return value on step 19
+    ok 168 - buffer overflow on step 19
+    ok 169 - return value on step 20
+    ok 170 - buffer overflow on step 20
+    ok 171 - return value on step 21
+    ok 172 - buffer overflow on step 21
+    ok 173 - return value on step 22
+    ok 174 - buffer overflow on step 22
+    ok 175 - return value on step 23
+    ok 176 - buffer overflow on step 23
+    ok 177 - return value on step 24
+    ok 178 - buffer overflow on step 24
+    ok 179 - return value on step 25
+    ok 180 - buffer overflow on step 25
+    ok 181 - return value on step 26
+    ok 182 - buffer overflow on step 26
+    ok 183 - return value on step 27
+    ok 184 - buffer overflow on step 27
+    ok 185 - return value on step 28
+    ok 186 - buffer overflow on step 28
+    ok 187 - return value on step 29
+    ok 188 - buffer overflow on step 29
+    ok 189 - return value on step 30
+    ok 190 - buffer overflow on step 30
+    ok 191 - return value on step 31
+    ok 192 - buffer overflow on step 31
+    ok 193 - return value on step 32
+    ok 194 - buffer overflow on step 32
+    ok 195 - return value on step 33
+    ok 196 - buffer overflow on step 33
+    ok 197 - return value on step 34
+    ok 198 - buffer overflow on step 34
+    ok 199 - return value on step 35
+    ok 200 - buffer overflow on step 35
+    ok 201 - return value on step 36
+    ok 202 - buffer overflow on step 36
+    ok 203 - return value on step 37
+    ok 204 - buffer overflow on step 37
+    ok 205 - return value on step 38
+    ok 206 - buffer overflow on step 38
+    ok 207 - return value on step 39
+    ok 208 - buffer overflow on step 39
+    ok 209 - return value on step 40
+    ok 210 - buffer overflow on step 40
+    ok 211 - return value on step 41
+    ok 212 - buffer overflow on step 41
+    ok 213 - return value on step 42
+    ok 214 - buffer overflow on step 42
+    ok 215 - return value on step 43
+    ok 216 - buffer overflow on step 43
+    ok 217 - return value on step 44
+    ok 218 - buffer overflow on step 44
+    ok 219 - return value on step 45
+    ok 220 - buffer overflow on step 45
+    ok 221 - return value on step 46
+    ok 222 - buffer overflow on step 46
+    ok 223 - return value on step 47
+    ok 224 - buffer overflow on step 47
+    ok 225 - return value on step 48
+    ok 226 - buffer overflow on step 48
+    ok 227 - return value on step 49
+    ok 228 - buffer overflow on step 49
+    ok 229 - return value on step 50
+    ok 230 - buffer overflow on step 50
+    # *** test_format: done ***
+ok 16 - subtests