diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ffd08aa786b01d173daf8575f45e8606efe2e65c..4860ec58209b176aa9597553df222c92d49f74ee 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -26,6 +26,7 @@ lua_source(lua_sources lua/load_cfg.lua)
 lua_source(lua_sources lua/bsdsocket.lua)
 lua_source(lua_sources lua/errno.lua)
 lua_source(lua_sources lua/log.lua)
+lua_source(lua_sources lua/box_net_box.lua)
 file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/third_party/luafun)
 lua_source(lua_sources ../third_party/luafun/fun.lua)
 
diff --git a/src/lua/box_net_box.lua b/src/lua/box_net_box.lua
new file mode 100644
index 0000000000000000000000000000000000000000..865e32037e261b79fa264407815525146442f58b
--- /dev/null
+++ b/src/lua/box_net_box.lua
@@ -0,0 +1,214 @@
+-- net_box.lua (internal file)
+
+local msgpack = require 'msgpack'
+local fiber = require 'fiber'
+local socket = require 'socket'
+local log = require 'log'
+        
+local PING          = 64
+local SELECT        = 1
+local INSERT        = 2
+local REPLACE       = 3
+local UPDATE        = 4
+local DELETE        = 5
+local CALL          = 6
+local TYPE          = 0x00
+local SYNC          = 0x01
+local SPACE_ID      = 0x10
+local INDEX_ID      = 0x11
+local LIMIT         = 0x12
+local OFFSET        = 0x13
+local ITERATOR      = 0x14
+local KEY           = 0x20
+local TUPLE         = 0x21
+local FUNCTION_NAME = 0x22
+local DATA          = 0x30
+local ERROR         = 0x31
+local GREETING_SIZE = 128
+    
+    
+local function request(header, body)
+    header = msgpack.encode(header)
+    body = msgpack.encode(body)
+
+    local len = msgpack.encode(string.len(header) + string.len(body))
+
+    return len .. header .. body
+end
+
+local _sync = -1
+
+local proto = {
+    -- sync
+    sync    = function(self)
+        _sync = _sync + 1
+        if _sync >= 0x7FFFFFFF then
+            _sync = 0
+        end
+        return _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,
+
+    -- 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)
+        return request(
+            { SYNC = sync, TYPE = DELETE },
+            { SPACE_ID = spaceno, KEY = key }
+        )
+    end,
+
+    -- update
+    -- select
+
+}
+
+
+local function connect(self)
+    if self.state == 'connected' or self.state == 'connecting' then
+        return
+    end
+
+    self.state = 'connecting'
+
+    self.dns = socket.getaddrinfo(self.host, self.port,
+                                            nil, { protocol = 'tcp' }) 
+    if not self.is_run then
+        return
+    end
+    
+    if self.dns == nil or #self.dns < 1 then
+        self:fatal("Cant resolve address %s:%s: %s", self.host, self.port,
+            self.errno.strerror(self.errno()))
+        return
+    end
+
+    for i, addr in pairs(self.dns) do
+        if self.s ~= nil then
+            self.s:close()
+        end
+        self.s = socket(addr.family, addr.type, addr.protocol)
+        if self.s ~= nil then
+            if self.s:sysconnect(addr.host, addr.port) then
+                self.state = 'connected'
+                return
+            end
+        end
+    end
+
+    if self.s ~= nil then
+        self:fatal("Cant resolve address %s:%s: %s", self.host, self.port,
+            self.s:errstr())
+        self.s:close()
+    else
+        self:fatal("Cant resolve address %s:%s: %s", self.host, self.port,
+            self.errno.strerror(self.errno()))
+    end
+end
+
+local function run(self)
+    fiber.wrap(
+        function()
+            while self.is_run do
+                if self.state == 'connected' then
+                    self:io()
+                else
+                    connect(self)
+                end
+            end
+        end
+    ) 
+end
+
+local connector = {
+
+    new     = function(parent, host, port, opts)
+        if opts == nil then
+            opts = {}
+        end
+        local self = {
+            host        = host,
+            port        = port,
+            state       = 'init',
+            is_run      = true,
+            opts        = opts
+        }
+
+        setmetatable(self, { __index = parent })
+
+        run(self)
+        return self
+    end,
+
+
+    on_error    = function(self, message) end,
+    on_connect  = function(self) end,
+    io          = function(self) fiber.sleep(1) end,
+
+
+    fatal = function(self, msg, ...)
+        msg = string.format(msg, ...)
+        self.state = 'error'
+        self.message = msg
+        if self.s then
+            self.s:close()
+            self.s = nil
+        end
+        self:on_error(msg)
+    end
+}
+
+
+local remote = {
+    __index = {
+        new     = function(class, host, port, user, password, opts)
+            local self = connector:new(host, port, opts)
+            self.user = user
+            self.password = password
+
+            setmetatable(self, class)
+            return self
+        end,
+
+        proto   = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
+        __index = connector
+    }
+}
+
+setmetatable(remote, remote)
+
+return remote
diff --git a/src/lua/init.cc b/src/lua/init.cc
index 0b47d3dd87f53324fce5513c21d00a4bba44e165..9991647815326e74a7acd68de76792166f175411 100644
--- a/src/lua/init.cc
+++ b/src/lua/init.cc
@@ -71,12 +71,18 @@ struct lua_State *tarantool_L;
 /* contents of src/lua/ files */
 extern char uuid_lua[], session_lua[], msgpackffi_lua[], fun_lua[],
        load_cfg_lua[], interactive_lua[], digest_lua[], init_lua[],
-       log_lua[];
+       log_lua[], box_net_box_lua[];
 static const char *lua_sources[] = { init_lua, session_lua, load_cfg_lua, NULL };
-static const char *lua_modules[] = { "msgpackffi", msgpackffi_lua,
-	"fun", fun_lua, "digest", digest_lua,
+static const char *lua_modules[] = {
+	"msgpackffi", msgpackffi_lua,
+	"fun", fun_lua,
+	"digest", digest_lua,
 	"interactive", interactive_lua,
-	"uuid", uuid_lua, "log", log_lua, NULL };
+	"uuid", uuid_lua,
+	"log", log_lua,
+	"net.box", box_net_box_lua,
+	NULL
+};
 /*
  * {{{ box Lua library: common functions
  */
diff --git a/test/box/box.net.box.result b/test/box/box.net.box.result
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/test/box/box.net.box.test.lua b/test/box/box.net.box.test.lua
new file mode 100644
index 0000000000000000000000000000000000000000..ea6f98b3c33ad2e61a40b4da5812fd680a5ee71f
--- /dev/null
+++ b/test/box/box.net.box.test.lua
@@ -0,0 +1,8 @@
+remote = require 'net.box'
+
+port = box.cfg.primary_port
+
+cn = remote:new('localhost', port)
+cn
+
+cn.proto.ping(0)