diff --git a/Makefile b/Makefile
index 93e16e43c64c11cfcf2e9dde08ff23765995f121..63b307ab51ba67b69076c46c4754b44d4751b6b4 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,8 @@ BUILD_CFLAGS += --coverage
 endif
 
 # Dependencies
-$(eval $(call find_lib,libknot))
+$(eval $(call find_lib,libknot,2.1))
+$(eval $(call find_lib,libzscanner,2.1))
 $(eval $(call find_lib,libuv,1.0))
 $(eval $(call find_alt,lua,luajit))
 $(eval $(call find_lib,cmocka))
@@ -49,6 +50,9 @@ BUILD_CFLAGS += $(addprefix -I,$(wildcard contrib/ccan/*) contrib/murmurhash3)
 info:
 	$(info Target:     Knot DNS Resolver $(MAJOR).$(MINOR).$(PATCH)-$(PLATFORM))
 	$(info Compiler:   $(CC) $(BUILD_CFLAGS))
+	$(info )
+	$(info Variables)
+	$(info ---------)
 	$(info HARDENING:  $(HARDENING))
 	$(info BUILDMODE:  $(BUILDMODE))
 	$(info PREFIX:     $(PREFIX))
@@ -63,14 +67,14 @@ info:
 	$(info Dependencies)
 	$(info ------------)
 	$(info [$(HAS_libknot)] libknot (lib))
-	$(info [$(HAS_lua)] LuaJIT (daemon))
+	$(info [$(HAS_lua)] luajit (daemon))
 	$(info [$(HAS_libuv)] libuv (daemon))
 	$(info )
 	$(info Optional)
 	$(info --------)
 	$(info [$(HAS_doxygen)] doxygen (doc))
-	$(info [$(HAS_go)] Go 1.5+ on amd64 (modules/go))
-	$(info [$(HAS_geoip)] github.com/abh/geoip (modules/tinyweb))
+	$(info [$(HAS_go)] go (modules/go, Go 1.5+ on amd64))
+	$(info [$(HAS_geoip)] geoip (modules/tinyweb, github.com/abh/geoip))
 	$(info [$(HAS_libmemcached)] libmemcached (modules/memcached))
 	$(info [$(HAS_hiredis)] hiredis (modules/redis))
 	$(info [$(HAS_cmocka)] cmocka (tests/unit))
diff --git a/contrib/base32hex.c b/contrib/base32hex.c
new file mode 100644
index 0000000000000000000000000000000000000000..28701e56f597f7a274b789a4c5bc37d113c4b8e6
--- /dev/null
+++ b/contrib/base32hex.c
@@ -0,0 +1,207 @@
+/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "contrib/base32hex.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+
+/*! \brief Maximal length of binary input to Base32hex encoding. */
+#define MAX_BIN_DATA_LEN	((INT32_MAX / 8) * 5)
+
+/*! \brief Base32hex padding character. */
+const uint8_t base32hex_pad = '=';
+/*! \brief Base32hex alphabet. */
+const uint8_t base32hex_enc[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
+
+/*! \brief Indicates bad Base32hex character. */
+#define KO	255
+/*! \brief Indicates Base32hex padding character. */
+#define PD	 32
+
+/*! \brief Transformation and validation table for decoding Base32hex. */
+const uint8_t base32hex_dec[256] = {
+	[  0] = KO, [ 43] = KO, ['V'] = 31, [129] = KO, [172] = KO, [215] = KO,
+	[  1] = KO, [ 44] = KO, ['W'] = KO, [130] = KO, [173] = KO, [216] = KO,
+	[  2] = KO, [ 45] = KO, ['X'] = KO, [131] = KO, [174] = KO, [217] = KO,
+	[  3] = KO, [ 46] = KO, ['Y'] = KO, [132] = KO, [175] = KO, [218] = KO,
+	[  4] = KO, [ 47] = KO, ['Z'] = KO, [133] = KO, [176] = KO, [219] = KO,
+	[  5] = KO, ['0'] =  0, [ 91] = KO, [134] = KO, [177] = KO, [220] = KO,
+	[  6] = KO, ['1'] =  1, [ 92] = KO, [135] = KO, [178] = KO, [221] = KO,
+	[  7] = KO, ['2'] =  2, [ 93] = KO, [136] = KO, [179] = KO, [222] = KO,
+	[  8] = KO, ['3'] =  3, [ 94] = KO, [137] = KO, [180] = KO, [223] = KO,
+	[  9] = KO, ['4'] =  4, [ 95] = KO, [138] = KO, [181] = KO, [224] = KO,
+	[ 10] = KO, ['5'] =  5, [ 96] = KO, [139] = KO, [182] = KO, [225] = KO,
+	[ 11] = KO, ['6'] =  6, ['a'] = 10, [140] = KO, [183] = KO, [226] = KO,
+	[ 12] = KO, ['7'] =  7, ['b'] = 11, [141] = KO, [184] = KO, [227] = KO,
+	[ 13] = KO, ['8'] =  8, ['c'] = 12, [142] = KO, [185] = KO, [228] = KO,
+	[ 14] = KO, ['9'] =  9, ['d'] = 13, [143] = KO, [186] = KO, [229] = KO,
+	[ 15] = KO, [ 58] = KO, ['e'] = 14, [144] = KO, [187] = KO, [230] = KO,
+	[ 16] = KO, [ 59] = KO, ['f'] = 15, [145] = KO, [188] = KO, [231] = KO,
+	[ 17] = KO, [ 60] = KO, ['g'] = 16, [146] = KO, [189] = KO, [232] = KO,
+	[ 18] = KO, ['='] = PD, ['h'] = 17, [147] = KO, [190] = KO, [233] = KO,
+	[ 19] = KO, [ 62] = KO, ['i'] = 18, [148] = KO, [191] = KO, [234] = KO,
+	[ 20] = KO, [ 63] = KO, ['j'] = 19, [149] = KO, [192] = KO, [235] = KO,
+	[ 21] = KO, [ 64] = KO, ['k'] = 20, [150] = KO, [193] = KO, [236] = KO,
+	[ 22] = KO, ['A'] = 10, ['l'] = 21, [151] = KO, [194] = KO, [237] = KO,
+	[ 23] = KO, ['B'] = 11, ['m'] = 22, [152] = KO, [195] = KO, [238] = KO,
+	[ 24] = KO, ['C'] = 12, ['n'] = 23, [153] = KO, [196] = KO, [239] = KO,
+	[ 25] = KO, ['D'] = 13, ['o'] = 24, [154] = KO, [197] = KO, [240] = KO,
+	[ 26] = KO, ['E'] = 14, ['p'] = 25, [155] = KO, [198] = KO, [241] = KO,
+	[ 27] = KO, ['F'] = 15, ['q'] = 26, [156] = KO, [199] = KO, [242] = KO,
+	[ 28] = KO, ['G'] = 16, ['r'] = 27, [157] = KO, [200] = KO, [243] = KO,
+	[ 29] = KO, ['H'] = 17, ['s'] = 28, [158] = KO, [201] = KO, [244] = KO,
+	[ 30] = KO, ['I'] = 18, ['t'] = 29, [159] = KO, [202] = KO, [245] = KO,
+	[ 31] = KO, ['J'] = 19, ['u'] = 30, [160] = KO, [203] = KO, [246] = KO,
+	[ 32] = KO, ['K'] = 20, ['v'] = 31, [161] = KO, [204] = KO, [247] = KO,
+	[ 33] = KO, ['L'] = 21, ['w'] = KO, [162] = KO, [205] = KO, [248] = KO,
+	[ 34] = KO, ['M'] = 22, ['x'] = KO, [163] = KO, [206] = KO, [249] = KO,
+	[ 35] = KO, ['N'] = 23, ['y'] = KO, [164] = KO, [207] = KO, [250] = KO,
+	[ 36] = KO, ['O'] = 24, ['z'] = KO, [165] = KO, [208] = KO, [251] = KO,
+	[ 37] = KO, ['P'] = 25, [123] = KO, [166] = KO, [209] = KO, [252] = KO,
+	[ 38] = KO, ['Q'] = 26, [124] = KO, [167] = KO, [210] = KO, [253] = KO,
+	[ 39] = KO, ['R'] = 27, [125] = KO, [168] = KO, [211] = KO, [254] = KO,
+	[ 40] = KO, ['S'] = 28, [126] = KO, [169] = KO, [212] = KO, [255] = KO,
+	[ 41] = KO, ['T'] = 29, [127] = KO, [170] = KO, [213] = KO,
+	[ 42] = KO, ['U'] = 30, [128] = KO, [171] = KO, [214] = KO,
+};
+
+int32_t base32hex_decode(const uint8_t  *in,
+                         const uint32_t in_len,
+                         uint8_t        *out,
+                         const uint32_t out_len)
+{
+	// Checking inputs.
+	if (in == NULL || out == NULL) {
+		return -1;
+	}
+	if (in_len > INT32_MAX || out_len < ((in_len + 7) / 8) * 5) {
+		return -1;
+	}
+	if ((in_len % 8) != 0) {
+		return -1;
+	}
+
+	const uint8_t	*stop = in + in_len;
+	uint8_t		*bin = out;
+	uint8_t		pad_len = 0;
+	uint8_t		c1, c2, c3, c4, c5, c6, c7, c8;
+
+	// Decoding loop takes 8 characters and creates 5 bytes.
+	while (in < stop) {
+		// Filling and transforming 8 Base32hex chars.
+		c1 = base32hex_dec[in[0]];
+		c2 = base32hex_dec[in[1]];
+		c3 = base32hex_dec[in[2]];
+		c4 = base32hex_dec[in[3]];
+		c5 = base32hex_dec[in[4]];
+		c6 = base32hex_dec[in[5]];
+		c7 = base32hex_dec[in[6]];
+		c8 = base32hex_dec[in[7]];
+
+		// Check 8. char if is bad or padding.
+		if (c8 >= PD) {
+			if (c8 == PD && pad_len == 0) {
+				pad_len = 1;
+			} else {
+				return -1;
+			}
+		}
+
+		// Check 7. char if is bad or padding (if so, 6. must be too).
+		if (c7 >= PD) {
+			if (c7 == PD && c6 == PD && pad_len == 1) {
+				pad_len = 3;
+			} else {
+				return -1;
+			}
+		}
+
+		// Check 6. char if is bad or padding.
+		if (c6 >= PD) {
+			if (!(c6 == PD && pad_len == 3)) {
+				return -1;
+			}
+		}
+
+		// Check 5. char if is bad or padding.
+		if (c5 >= PD) {
+			if (c5 == PD && pad_len == 3) {
+				pad_len = 4;
+			} else {
+				return -1;
+			}
+		}
+
+		// Check 4. char if is bad or padding (if so, 3. must be too).
+		if (c4 >= PD) {
+			if (c4 == PD && c3 == PD && pad_len == 4) {
+				pad_len = 6;
+			} else {
+				return -1;
+			}
+		}
+
+		// Check 3. char if is bad or padding.
+		if (c3 >= PD) {
+			if (!(c3 == PD && pad_len == 6)) {
+				return -1;
+			}
+		}
+
+		// 1. and 2. chars must not be padding.
+		if (c2 >= PD || c1 >= PD) {
+			return -1;
+		}
+
+		// Computing of output data based on padding length.
+		switch (pad_len) {
+		case 0:
+			bin[4] = (c7 << 5) + c8;
+		case 1:
+			bin[3] = (c5 << 7) + (c6 << 2) + (c7 >> 3);
+		case 3:
+			bin[2] = (c4 << 4) + (c5 >> 1);
+		case 4:
+			bin[1] = (c2 << 6) + (c3 << 1) + (c4 >> 4);
+		case 6:
+			bin[0] = (c1 << 3) + (c2 >> 2);
+		}
+
+		// Update output end.
+		switch (pad_len) {
+		case 0:
+			bin += 5;
+			break;
+		case 1:
+			bin += 4;
+			break;
+		case 3:
+			bin += 3;
+			break;
+		case 4:
+			bin += 2;
+			break;
+		case 6:
+			bin += 1;
+			break;
+		}
+
+		in += 8;
+	}
+
+	return (bin - out);
+}
diff --git a/contrib/base32hex.h b/contrib/base32hex.h
new file mode 100644
index 0000000000000000000000000000000000000000..404b981a81347d6117f2a4c7cf198db52ad0f223
--- /dev/null
+++ b/contrib/base32hex.h
@@ -0,0 +1,52 @@
+/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/*!
+ * \file
+ *
+ * \brief Base32hex implementation (RFC 4648).
+ *
+ * \note Input Base32hex string can contain a-v characters. These characters
+ *       are considered as A-V equivalent.
+ *
+ * \addtogroup contrib
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*!
+ * \brief Decodes text data using Base32hex.
+ *
+ * \note Input data needn't be terminated with '\0'.
+ *
+ * \note Input data must be continuous Base32hex string!
+ *
+ * \param in		Input text data.
+ * \param in_len	Length of input string.
+ * \param out		Output data buffer.
+ * \param out_len	Size of output buffer.
+ *
+ * \retval >=0		length of output data.
+ * \retval KNOT_E*	if error.
+ */
+int32_t base32hex_decode(const uint8_t  *in,
+                         const uint32_t in_len,
+                         uint8_t        *out,
+                         const uint32_t out_len);
+
+/*! @} */
diff --git a/contrib/contrib.mk b/contrib/contrib.mk
index 3daf43df53f5eeb163856ea91867c9330df1c1e6..5d924734a740b4ffdb16eb44d6563c5d25a7f545 100644
--- a/contrib/contrib.mk
+++ b/contrib/contrib.mk
@@ -4,7 +4,8 @@ contrib_SOURCES := \
 	contrib/ccan/isaac/isaac.c \
 	contrib/ccan/json/json.c \
 	contrib/ucw/mempool.c \
-	contrib/murmurhash3/murmurhash3.c
+	contrib/murmurhash3/murmurhash3.c \
+	contrib/base32hex.c
 contrib_CFLAGS := -fPIC
 contrib_TARGET := $(abspath contrib)/contrib$(AREXT)
 $(eval $(call make_static,contrib,contrib))
diff --git a/contrib/wire.h b/contrib/wire.h
new file mode 100644
index 0000000000000000000000000000000000000000..7a367cd177a045119a5d7f902ac415c274517d83
--- /dev/null
+++ b/contrib/wire.h
@@ -0,0 +1,174 @@
+/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/*!
+ * \file
+ *
+ * \brief Wire integer operations.
+ *
+ * \addtogroup contrib
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <string.h>
+
+#if defined(__linux__)
+#       include <endian.h>
+#  ifndef be64toh
+#       include <arpa/inet.h>
+#       include <byteswap.h>
+#    if BYTE_ORDER == LITTLE_ENDIAN
+#       define be16toh(x) ntohs(x)
+#       define be32toh(x) ntohl(x)
+#       define be64toh(x) bswap_64 (x)
+#       define le16toh(x) (x)
+#       define le32toh(x) (x)
+#       define le64toh(x) (x)
+#    else
+#       define be16toh(x) (x)
+#       define be32toh(x) (x)
+#       define be64toh(x) (x)
+#       define le16toh(x) ntohs(x)
+#       define le32toh(x) ntohl(x)
+#       define le64toh(x) bswap_64 (x)
+#    endif
+#  endif
+#elif defined(__FreeBSD__) || defined(__NetBSD__)
+#       include <sys/endian.h>
+#elif defined(__OpenBSD__)
+#       include <endian.h>
+#elif defined(__APPLE__)
+#       include <libkern/OSByteOrder.h>
+#       define be16toh(x) OSSwapBigToHostInt16(x)
+#       define be32toh(x) OSSwapBigToHostInt32(x)
+#       define be64toh(x) OSSwapBigToHostInt64(x)
+#       define htobe16(x) OSSwapHostToBigInt16(x)
+#       define htobe32(x) OSSwapHostToBigInt32(x)
+#       define htobe64(x) OSSwapHostToBigInt64(x)
+#       define le16toh(x) OSSwapLittleToHostInt16(x)
+#       define le32toh(x) OSSwapLittleToHostInt32(x)
+#       define le64toh(x) OSSwapLittleToHostInt64(x)
+#       define htole16(x) OSSwapHostToLittleInt16(x)
+#       define htole32(x) OSSwapHostToLittleInt32(x)
+#       define htole64(x) OSSwapHostToLittleInt64(x)
+#endif
+
+/*!
+ * \brief Reads 2 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 2 bytes from.
+ *
+ * \return The 2 bytes read, in host byte order.
+ */
+inline static uint16_t wire_read_u16(const uint8_t *pos)
+{
+	return be16toh(*(uint16_t *)pos);
+}
+
+/*!
+ * \brief Reads 4 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 4 bytes from.
+ *
+ * \return The 4 bytes read, in host byte order.
+ */
+inline static uint32_t wire_read_u32(const uint8_t *pos)
+{
+	return be32toh(*(uint32_t *)pos);
+}
+
+/*!
+ * \brief Reads 6 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 6 bytes from.
+ *
+ * \return The 6 bytes read, in host byte order.
+ */
+inline static uint64_t wire_read_u48(const uint8_t *pos)
+{
+	uint64_t input = 0;
+	memcpy((uint8_t *)&input + 1, pos, 6);
+	return be64toh(input) >> 8;
+}
+
+/*!
+ * \brief Read 8 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 8 bytes from.
+ *
+ * \return The 8 bytes read, in host byte order.
+ */
+inline static uint64_t wire_read_u64(const uint8_t *pos)
+{
+	return be64toh(*(uint64_t *)pos);
+}
+
+/*!
+ * \brief Writes 2 bytes in wireformat.
+ *
+ * The data are stored in network byte order (big endian).
+ *
+ * \param pos Position where to put the 2 bytes.
+ * \param data Data to put.
+ */
+inline static void wire_write_u16(uint8_t *pos, uint16_t data)
+{
+	*(uint16_t *)pos = htobe16(data);
+}
+
+/*!
+ * \brief Writes 4 bytes in wireformat.
+ *
+ * The data are stored in network byte order (big endian).
+ *
+ * \param pos Position where to put the 4 bytes.
+ * \param data Data to put.
+ */
+inline static void wire_write_u32(uint8_t *pos, uint32_t data)
+{
+	*(uint32_t *)pos = htobe32(data);
+}
+
+/*!
+ * \brief Writes 6 bytes in wireformat.
+ *
+ * The data are stored in network byte order (big endian).
+ *
+ * \param pos Position where to put the 4 bytes.
+ * \param data Data to put.
+ */
+inline static void wire_write_u48(uint8_t *pos, uint64_t data)
+{
+	uint64_t swapped = htobe64(data << 8);
+	memcpy(pos, (uint8_t *)&swapped + 1, 6);
+}
+
+/*!
+ * \brief Writes 8 bytes in wireformat.
+ *
+ * The data are stored in network byte order (big endian).
+ *
+ * \param pos Position where to put the 8 bytes.
+ * \param data Data to put.
+ */
+inline static void wire_write_u64(uint8_t *pos, uint64_t data)
+{
+	*(uint64_t *)pos = htobe64(data);
+}
+
+/*! @} */
diff --git a/daemon/bindings.c b/daemon/bindings.c
index e97230c91af78593f598024f9f298fc97b28ca85..0c62422c372bbae4fcff31d4fbd9fa0fca5c5237 100644
--- a/daemon/bindings.c
+++ b/daemon/bindings.c
@@ -14,6 +14,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <assert.h>
 #include <uv.h>
 #include <contrib/cleanup.h>
 #include <libknot/descriptor.h>
@@ -334,11 +335,11 @@ static int cache_backends(lua_State *L)
 static int cache_count(lua_State *L)
 {
 	struct engine *engine = engine_luaget(L);
-	const namedb_api_t *storage = engine->resolver.cache.api;
+	const knot_db_api_t *storage = engine->resolver.cache.api;
 
 	/* Fetch item count */
 	struct kr_cache_txn txn;
-	int ret = kr_cache_txn_begin(&engine->resolver.cache, &txn, NAMEDB_RDONLY);
+	int ret = kr_cache_txn_begin(&engine->resolver.cache, &txn, KNOT_DB_RDONLY);
 	if (ret != 0) {
 		format_error(L, kr_strerror(ret));
 		lua_error(L);
diff --git a/daemon/daemon.mk b/daemon/daemon.mk
index c96c1e46de7eecebdc3bf84da6087084e0be5a06..2693ab7ec0f7ed192c51936f547cca47dc2c8a94 100644
--- a/daemon/daemon.mk
+++ b/daemon/daemon.mk
@@ -24,7 +24,7 @@ bindings-install: $(kresd_DIST) $(DESTDIR)$(MODULEDIR)
 
 kresd_CFLAGS := -fPIE
 kresd_DEPEND := $(libkres) $(contrib)
-kresd_LIBS := $(libkres_TARGET) $(contrib_TARGET) $(libknot_LIBS) $(libdnssec_LIBS) $(libuv_LIBS) $(lua_LIBS)
+kresd_LIBS := $(libkres_TARGET) $(contrib_TARGET) $(libknot_LIBS) $(libzscanner_LIBS) $(libdnssec_LIBS) $(libuv_LIBS) $(lua_LIBS)
 
 # Make binary
 ifeq ($(HAS_lua)|$(HAS_libuv), yes|yes)
diff --git a/daemon/engine.c b/daemon/engine.c
index fcdca1aad89837f6154bde5f3efcaa59fca1dfe2..7e4fc8b25ec3685fdbd9911b4f7a5dd354beba0d 100644
--- a/daemon/engine.c
+++ b/daemon/engine.c
@@ -21,9 +21,9 @@
 #include <unistd.h>
 #include <grp.h>
 #include <pwd.h>
-#include <libknot/internal/mempattern.h>
-/* #include <libknot/internal/namedb/namedb_trie.h> @todo Not supported (doesn't keep value copy) */
-#include <libknot/internal/namedb/namedb_lmdb.h>
+/* #include <libknot/internal/namedb/knot_db_trie.h> @todo Not supported (doesn't keep value copy) */
+#include <libknot/db/db_lmdb.h>
+#include <zscanner/scanner.h>
 
 #include "daemon/engine.h"
 #include "daemon/bindings.h"
@@ -177,7 +177,7 @@ static int l_option(lua_State *L)
 	unsigned opt_code = 0;
 	if (lua_isstring(L, 1)) {
 		const char *opt = lua_tostring(L, 1);
-		for (const lookup_table_t *it = kr_query_flag_names(); it->name; ++it) {
+		for (const knot_lookup_t *it = kr_query_flag_names(); it->name; ++it) {
 			if (strcmp(it->name, opt) == 0) {
 				opt_code = it->id;
 				break;
@@ -200,6 +200,52 @@ static int l_option(lua_State *L)
 	return 1;
 }
 
+/** Enable/disable trust anchor. */
+static int l_trustanchor(lua_State *L)
+{
+	struct engine *engine = engine_luaget(L);
+	const char *anchor = lua_tostring(L, 1);
+	bool enable = lua_isboolean(L, 2) ? lua_toboolean(L, 2) : true;
+	if (!anchor || strlen(anchor) == 0) {
+		return 0;
+	}
+	/* If disabling, parse the owner string only. */
+	if (!enable) {
+		knot_dname_t *owner = knot_dname_from_str(NULL, anchor, KNOT_DNAME_MAXLEN);
+		if (!owner) {
+			lua_pushstring(L, "invalid trust anchor owner");
+			lua_error(L);
+		}
+		lua_pushboolean(L, kr_ta_del(&engine->resolver.trust_anchors, owner) == 0);
+		free(owner);
+		return 1;
+	}
+
+	/* Parse the record */
+	zs_scanner_t *zs = malloc(sizeof(*zs));
+	if (!zs || zs_init(zs, ".", 1, 0) != 0) {
+		free(zs);
+		lua_pushstring(L, "not enough memory");
+		lua_error(L);
+	}
+	int ok = zs_set_input_string(zs, anchor, strlen(anchor)) == 0 &&
+	         zs_parse_all(zs) == 0;
+	/* Add it to TA set and cleanup */
+	if (ok) {
+		ok = kr_ta_add(&engine->resolver.trust_anchors,
+		               zs->r_owner, zs->r_type, zs->r_ttl, zs->r_data, zs->r_data_length) == 0;
+	}
+	zs_deinit(zs);
+	free(zs);
+	/* Report errors */
+	if (!ok) {
+		lua_pushstring(L, "failed to process trust anchor RR");
+		lua_error(L);
+	}
+	lua_pushboolean(L, true);
+	return 1;
+}
+
 /** Unpack JSON object to table */
 static void l_unpack_json(lua_State *L, JsonNode *table)
 {
@@ -320,9 +366,9 @@ static int l_trampoline(lua_State *L)
  */
 
 /** @internal Make lmdb options. */
-void *namedb_lmdb_mkopts(const char *conf, size_t maxsize)
+void *knot_db_lmdb_mkopts(const char *conf, size_t maxsize)
 {
-	struct namedb_lmdb_opts *opts = malloc(sizeof(*opts));
+	struct knot_db_lmdb_opts *opts = malloc(sizeof(*opts));
 	if (opts) {
 		memset(opts, 0, sizeof(*opts));
 		opts->path = (conf && strlen(conf)) ? conf : ".";
@@ -366,7 +412,7 @@ static int init_resolver(struct engine *engine)
 
 	/* Initialize storage backends */
 	struct storage_api lmdb = {
-		"lmdb://", namedb_lmdb_api, namedb_lmdb_mkopts
+		"lmdb://", knot_db_lmdb_api, knot_db_lmdb_mkopts
 	};
 
 	return array_push(engine->storage_registry, lmdb);
@@ -395,6 +441,8 @@ static int init_state(struct engine *engine)
 	lua_setglobal(engine->L, "option");
 	lua_pushcfunction(engine->L, l_setuser);
 	lua_setglobal(engine->L, "user");
+	lua_pushcfunction(engine->L, l_trustanchor);
+	lua_setglobal(engine->L, "trustanchor");
 	lua_pushcfunction(engine->L, l_libpath);
 	lua_setglobal(engine->L, "libpath");
 	lua_pushliteral(engine->L, MODULEDIR);
@@ -406,7 +454,7 @@ static int init_state(struct engine *engine)
 	return kr_ok();
 }
 
-int engine_init(struct engine *engine, mm_ctx_t *pool)
+int engine_init(struct engine *engine, knot_mm_t *pool)
 {
 	if (engine == NULL) {
 		return kr_error(EINVAL);
diff --git a/daemon/engine.h b/daemon/engine.h
index 0b32dfd80ca6a2325080c131663f092452364e2e..d9755157bf6e619b541b58f942125ba478b4ab14 100644
--- a/daemon/engine.h
+++ b/daemon/engine.h
@@ -35,13 +35,14 @@
  */
 struct lua_State;
 
+#include "lib/utils.h"
 #include "lib/resolve.h"
 #include "daemon/network.h"
 
 /** Cache storage backend. */
 struct storage_api {
 	const char *prefix; /**< Storage prefix, e.g. 'lmdb://' */
-	const namedb_api_t *(*api)(void); /**< Storage API implementation */
+	const knot_db_api_t *(*api)(void); /**< Storage API implementation */
 	void *(*opts_create)(const char *, size_t); /**< Storage options factory */
 };
 
@@ -53,11 +54,11 @@ struct engine {
     struct network net;
     module_array_t modules;
     storage_registry_t storage_registry;
-    mm_ctx_t *pool;
+    knot_mm_t *pool;
     struct lua_State *L;
 };
 
-int engine_init(struct engine *engine, mm_ctx_t *pool);
+int engine_init(struct engine *engine, knot_mm_t *pool);
 void engine_deinit(struct engine *engine);
 /** @warning This function leaves 1 string result on stack. */
 int engine_cmd(struct engine *engine, const char *str);
diff --git a/daemon/lua/kres.lua b/daemon/lua/kres.lua
index 0834b65d7f010a2ae7ce767ae3a5343c95799a33..a041c3b7008d6aa11be3743aea1193db7297b2be 100644
--- a/daemon/lua/kres.lua
+++ b/daemon/lua/kres.lua
@@ -11,7 +11,7 @@ local bit = require('bit')
 local bor = bit.bor
 local band = bit.band
 local C = ffi.C
-local knot = ffi.load(libpath('libknot', '1'))
+local knot = ffi.load(libpath('libknot', '2'))
 ffi.cdef[[
 
 /*
@@ -118,9 +118,6 @@ struct sockaddr {
 /* libknot */
 typedef int knot_section_t; /* Do not touch */
 typedef void knot_rrinfo_t; /* Do not touch */
-typedef struct node {
-  struct node *next, *prev;
-} node_t;
 typedef uint8_t knot_dname_t;
 typedef uint8_t knot_rdata_t;
 typedef struct knot_rdataset {
@@ -174,7 +171,6 @@ typedef struct {
 	size_t cap;
 } rr_array_t;
 struct kr_query {
-	node_t _node;
 	struct kr_query *parent;
 	knot_dname_t *sname;
 	uint16_t type;
@@ -258,6 +254,7 @@ int kr_pkt_put(knot_pkt_t *pkt, const knot_dname_t *name, uint32_t ttl,
                uint16_t rclass, uint16_t rtype, const uint8_t *rdata, uint16_t rdlen);
 int kr_pkt_recycle(knot_pkt_t *pkt);
 const char *kr_inaddr(const struct sockaddr *addr);
+int kr_inaddr_family(const struct sockaddr *addr);
 int kr_inaddr_len(const struct sockaddr *addr);
 int kr_straddr_family(const char *addr);
 int kr_straddr_subnet(void *dst, const char *addr);
@@ -285,6 +282,7 @@ ffi.metatype( sockaddr_t, {
 	__index = {
 		len = function(sa) return C.kr_inaddr_len(sa) end,
 		ip = function (sa) return C.kr_inaddr(sa) end,
+		family = function (sa) return C.kr_inaddr_family(sa) end,
 	}
 })
 
@@ -353,10 +351,6 @@ local kr_query_t = ffi.typeof('struct kr_query')
 ffi.metatype( kr_query_t, {
 	__index = {
 		name = function(qry, new_name) return ffi.string(qry.sname, knot.knot_dname_size(qry.sname)) end,
-		next = function(qry)
-			assert(qry)
-			return C.kr_rplan_next(qry)
-		end,
 		resolved = function(qry)
 			return band(qry.flags, kres.query.RESOLVED) ~= 0
 		end,
@@ -443,4 +437,4 @@ local kres = {
 	context = function () return ffi.cast('struct kr_context *', __engine) end,
 }
 
-return kres
\ No newline at end of file
+return kres
diff --git a/daemon/lua/trust_anchors.lua b/daemon/lua/trust_anchors.lua
index 3ffb123e8e1b4ac4922f42aa4bcd6e19afe962d0..bc756648bfb62b3ac993e10e8d4adc1abb5ff367 100644
--- a/daemon/lua/trust_anchors.lua
+++ b/daemon/lua/trust_anchors.lua
@@ -1,6 +1,66 @@
+-- Fetch over HTTPS with peert cert checked
+local function https_fetch(url, ca)
+	local ssl_ok, https = pcall(require, 'ssl.https')
+	local ltn_ok, ltn12 = pcall(require, 'ltn12')
+	if not ssl_ok or not ltn_ok then
+		return nil, 'luasec and luasocket needed for root TA bootstrap'
+	end
+	local resp = {}
+	local r, c, h, s = https.request{
+	       url = url,
+	       cafile = ca,
+	       verify = {'peer', 'fail_if_no_peer_cert' },
+	       protocol = 'tlsv1_2',
+	       sink = ltn12.sink.table(resp),
+	}
+	if r == nil then return r, c end
+	return resp[1]
+end
+
+-- Fetch root anchors in XML over HTTPS
+local function bootstrap(url, ca)
+	-- @todo ICANN certificate is verified against current CA
+	--       this is not ideal, as it should rather verify .xml signature which
+	--       is signed by ICANN long-lived cert, but luasec has no PKCS7
+	ca = ca or etcdir..'/icann-ca.pem'
+	url = url or 'https://data.iana.org/root-anchors/root-anchors.xml'
+	local xml, err = https_fetch(url, ca)
+	if not xml then
+		return false, string.format('[ ta ] fetch of "%s" failed: %s', url, err)
+	end
+	-- Parse root trust anchor
+	local fields = {}
+	string.gsub(xml, "<([%w]+).->([^<]+)</[%w]+>", function (k, v) fields[k] = v end)
+	local rrdata = string.format('%s %s %s %s', fields.KeyDigest, fields.Algorithm, fields.DigestType, fields.Digest)
+	local rr = string.format('%s 0 IN DS %s', fields.TrustAnchor, rrdata)
+	-- Add to key set, create an empty keyset file to be filled
+	print('[ ta ] warning: root anchor bootstrapped, you SHOULD check the key manually, see: '..
+	      'https://data.iana.org/root-anchors/draft-icann-dnssec-trust-anchor.html#sigs')
+	return rr
+end
+
+-- Load the module (check for FFI)
 local ffi_ok, ffi = pcall(require, 'ffi')
 if not ffi_ok then
-	return { error = 'FFI not available, trust_anchors disabled.' }
+	-- Simplified TA management, no RFC5011 automatics
+	return {
+		-- Reuse Lua/C global function
+		add = trustanchor,
+		-- Simplified trust anchor management
+		config = function (path)
+			if not path then return end
+			if not io.open(path, 'r') then
+				local rr, err = bootstrap()
+				if not rr then print(err) return false end
+				io.open(path, 'w'):write(rr..'\n')
+			end
+			for line in io.lines(path) do
+				trustanchor(line)
+			end
+		end,
+		-- Disabled
+		set_insecure = function () error('[ ta ] FFI not available, this function is disabled') end,
+	}
 end
 local kres = require('kres')
 local C = ffi.C
@@ -147,25 +207,6 @@ local function keyset_write(keyset, path)
 	os.rename(path..'.lock', path)
 end
 
--- Fetch over HTTPS with peert cert checked
-local function https_fetch(url, ca)
-	local ssl_ok, https = pcall(require, 'ssl.https')
-	local ltn_ok, ltn12 = pcall(require, 'ltn12')
-	if not ssl_ok or not ltn_ok then
-		return nil, 'luasec and luasocket needed for root TA bootstrap'
-	end
-	local resp = {}
-	local r, c, h, s = https.request{
-	       url = url,
-	       cafile = ca,
-	       verify = {'peer', 'fail_if_no_peer_cert' },
-	       protocol = 'tlsv1_2',
-	       sink = ltn12.sink.table(resp),
-	}
-	if r == nil then return r, c end
-	return resp[1]
-end
-
 -- TA store management
 local trust_anchors = {
 	keyset = {},
@@ -210,19 +251,21 @@ local trust_anchors = {
 		return true
 	end,
 	-- Load keys from a file (managed)
-	config = function (path, unmanaged, bootstrap)
-		bootstrap = true
+	config = function (path, unmanaged)
 		-- Bootstrap if requested and keyfile doesn't exist
-		if bootstrap and not io.open(path, 'r') then
-			if not trust_anchors.bootstrap() then
+		if not io.open(path, 'r') then
+			local rr, msg = bootstrap()
+			print(msg)
+			if not rr then
 				error('you MUST obtain the root TA manually, see: '..
 				      'http://knot-resolver.readthedocs.org/en/latest/daemon.html#enabling-dnssec')
 			end
+			trustanchor(rr)
 		elseif path == trust_anchors.file_current then
 			return
 		end
 		-- Parse new keys
-		local new_keys = require('zonefile').parse_file(path)
+		local new_keys = require('zonefile').file(path)
 		trust_anchors.file_current = path
 		if unmanaged then trust_anchors.file_current = nil end
 		trust_anchors.keyset = {}
@@ -233,11 +276,7 @@ local trust_anchors = {
 	end,
 	-- Add DS/DNSKEY record(s) (unmanaged)
 	add = function (keystr)
-		local store = kres.context().trust_anchors
-		return require('zonefile').parser(function (p)
-			local rr = p:current_rr()
-			C.kr_ta_add(store, rr.owner, rr.type, rr.ttl, rr.rdata, #rr.rdata)
-		end):read(keystr..'\n')
+		return trustanchor(keystr)
 	end,
 	-- Negative TA management
 	set_insecure = function (list)
@@ -249,33 +288,6 @@ local trust_anchors = {
 		end
 		trust_anchors.insecure = list
 	end,
-	bootstrap = function (url, ca)
-		-- Fetch root anchors in XML over HTTPS
-		-- @todo ICANN certificate is verified against current CA
-		--       this is not ideal, as it should rather verify .xml signature which
-		--       is signed by ICANN long-lived cert, but luasec has no PKCS7
-		ca = ca or etcdir..'/icann-ca.pem'
-		url = url or 'https://data.iana.org/root-anchors/root-anchors.xml'
-		local xml, err = https_fetch(url, ca)
-		if not xml then
-			print(string.format('[ ta ] fetch of "%s" failed: %s', url, err))
-			return false
-		end
-		-- Parse root trust anchor
-		local fields = {}
-		string.gsub(xml, "<([%w]+).->([^<]+)</[%w]+>", function (k, v) fields[k] = v end)
-		local rrdata = string.format('%s %s %s %s', fields.KeyDigest, fields.Algorithm, fields.DigestType, fields.Digest)
-		local rr = string.format('%s 0 IN DS %s', fields.TrustAnchor, rrdata)
-		-- Add to key set, create an empty keyset file to be filled
-		if trust_anchors.add(rr) ~= 0 then
-			print(string.format('[ ta ] invalid format of the RR "%s"', rr))
-			return false
-		end
-		print(string.format('[ ta ] bootstrapped root anchor "%s"', rrdata))
-		print('[ ta ] warning: you SHOULD check the key manually, see: '..
-		      'https://data.iana.org/root-anchors/draft-icann-dnssec-trust-anchor.html#sigs')
-		return true
-	end,
 }
 
 return trust_anchors
\ No newline at end of file
diff --git a/daemon/main.c b/daemon/main.c
index 62375a40e30bdd8d1a7bd090e729703fb62e731d..4eda21ac848fdef101115d249b2f0487bf4237bf 100644
--- a/daemon/main.c
+++ b/daemon/main.c
@@ -18,9 +18,11 @@
 #include <string.h>
 #include <getopt.h>
 #include <uv.h>
+#include <assert.h>
 #include <contrib/cleanup.h>
 #include <contrib/ucw/mempool.h>
 #include <contrib/ccan/asprintf/asprintf.h>
+#include <libknot/error.h>
 
 #include "lib/defines.h"
 #include "lib/resolve.h"
@@ -141,7 +143,7 @@ static void help(int argc, char *argv[])
 	       " [rundir]             Path to the working directory (default: .)\n");
 }
 
-static struct worker_ctx *init_worker(uv_loop_t *loop, struct engine *engine, mm_ctx_t *pool, int worker_id, int worker_count)
+static struct worker_ctx *init_worker(uv_loop_t *loop, struct engine *engine, knot_mm_t *pool, int worker_id, int worker_count)
 {
 	/* Load bindings */
 	engine_lualib(engine, "modules", lib_modules);
@@ -333,9 +335,9 @@ int main(int argc, char **argv)
 	uv_signal_start(&sigint, signal_handler, SIGINT);
 	uv_signal_start(&sigterm, signal_handler, SIGTERM);
 	/* Create a server engine. */
-	mm_ctx_t pool = {
+	knot_mm_t pool = {
 		.ctx = mp_new (4096),
-		.alloc = (mm_alloc_t) mp_alloc
+		.alloc = (knot_mm_alloc_t) mp_alloc
 	};
 	struct engine engine;
 	ret = engine_init(&engine, &pool);
diff --git a/daemon/network.c b/daemon/network.c
index 103823dc682369aac5f20cd84601a700124a3cfa..ace172595b5930819be06dc82f0d0516edeb118c 100644
--- a/daemon/network.c
+++ b/daemon/network.c
@@ -14,6 +14,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <unistd.h>
 #include "daemon/network.h"
 #include "daemon/worker.h"
 #include "daemon/io.h"
diff --git a/daemon/worker.c b/daemon/worker.c
index b05643a2726f2a99ad4137d704eed1fa25631e83..b541b1eee70a811166a56f1879336b5e0a9b89a1 100644
--- a/daemon/worker.c
+++ b/daemon/worker.c
@@ -20,10 +20,12 @@
 #include <libknot/descriptor.h>
 #include <contrib/ucw/lib.h>
 #include <contrib/ucw/mempool.h>
+#include <contrib/wire.h>
 #if defined(__GLIBC__) && defined(_GNU_SOURCE)
 #include <malloc.h>
 #endif
-
+#include <assert.h>
+#include "lib/utils.h"
 #include "daemon/worker.h"
 #include "daemon/engine.h"
 #include "daemon/io.h"
@@ -227,9 +229,9 @@ static struct qr_task *qr_task_create(struct worker_ctx *worker, uv_handle_t *ha
 	}
 
 	/* Recycle available mempool if possible */
-	mm_ctx_t pool = {
+	knot_mm_t pool = {
 		.ctx = pool_take(worker),
-		.alloc = (mm_alloc_t) mp_alloc
+		.alloc = (knot_mm_alloc_t) mp_alloc
 	};
 
 	/* Create resolution task */
@@ -263,9 +265,14 @@ static struct qr_task *qr_task_create(struct worker_ctx *worker, uv_handle_t *ha
 	task->retry.data = task;
 	task->timeout.data = task;
 	task->on_complete = NULL;
+	task->req.qsource.key = NULL;
+	task->req.qsource.addr = NULL;
 	/* Remember query source addr */
 	if (addr) {
-		memcpy(&task->source.addr, addr, sockaddr_len(addr));
+		size_t addr_len = sizeof(struct sockaddr_in);
+		if (addr->sa_family == AF_INET6)
+			addr_len = sizeof(struct sockaddr_in6);
+		memcpy(&task->source.addr, addr, addr_len);
 		task->req.qsource.addr = (const struct sockaddr *)&task->source.addr;
 	} else {
 		task->source.addr.ip4.sin_family = AF_UNSPEC;
@@ -505,10 +512,10 @@ static void subreq_finalize(struct qr_task *task, const struct sockaddr *packet_
 		map_del(&task->worker->outstanding, key);
 	}
 	/* Notify waiting tasks. */
-	struct kr_query *leader_qry = TAIL(task->req.rplan.pending);
+	struct kr_query *leader_qry = array_tail(task->req.rplan.pending);
 	for (size_t i = task->waiting.len; i --> 0;) {
 		struct qr_task *follower = task->waiting.at[i];
-		struct kr_query *qry = TAIL(follower->req.rplan.pending);
+		struct kr_query *qry = array_tail(follower->req.rplan.pending);
 		/* Reuse MSGID and 0x20 secret */
 		if (qry) {
 			qry->id = leader_qry->id;
@@ -773,7 +780,7 @@ int worker_reserve(struct worker_ctx *worker, size_t ring_maxlen)
 		return kr_error(ENOMEM);
 	memset(&worker->pkt_pool, 0, sizeof(worker->pkt_pool));
 	worker->pkt_pool.ctx = mp_new (4 * sizeof(knot_pkt_t));
-	worker->pkt_pool.alloc = (mm_alloc_t) mp_alloc;
+	worker->pkt_pool.alloc = (knot_mm_alloc_t) mp_alloc;
 	worker->outstanding = map_make();
 	return kr_ok();
 }
diff --git a/daemon/worker.h b/daemon/worker.h
index 00a67a56bed24fd2ee27163a3c2cbe4e142e82f2..eed855d82d72a72e1b86915ac4736ec498c9298e 100644
--- a/daemon/worker.h
+++ b/daemon/worker.h
@@ -16,8 +16,6 @@
 
 #pragma once
 
-#include <libknot/internal/mempattern.h>
-
 #include "daemon/engine.h"
 #include "lib/generic/array.h"
 #include "lib/generic/map.h"
@@ -52,7 +50,7 @@ struct worker_ctx {
 	map_t outstanding;
 	mp_freelist_t pools;
 	mp_freelist_t ioreqs;
-	mm_ctx_t pkt_pool;
+	knot_mm_t pkt_pool;
 };
 
 /* Worker callback */
diff --git a/doc/build.rst b/doc/build.rst
index 9d0dccb882254649f0762e37b27fc6c19c689049..0374be556f45cf8e0df87de48dbf4c87ef915cd5 100644
--- a/doc/build.rst
+++ b/doc/build.rst
@@ -30,7 +30,7 @@ The following is a list of software required to build Knot DNS Resolver from sou
    "`GNU Make`_ 3.80+", "*all*", "*(build only)*"
    "`pkg-config`_", "*all*", "*(build only)* [#]_"
    "C compiler", "*all*", "*(build only)* [#]_"
-   "libknot_ 2.0+", "*all*", "Knot DNS library (requires autotools, GnuTLS and Jansson)."
+   "libknot_ 2.1+", "*all*", "Knot DNS library (requires autotools, GnuTLS and Jansson)."
    "LuaJIT_ 2.0+", "``daemon``", "Embedded scripting language."
    "libuv_ 1.7+", "``daemon``", "Multiplatform I/O and services (libuv_ 1.0 with limitations [#]_)."
 
@@ -45,7 +45,6 @@ There are also *optional* packages that enable specific functionality in Knot DN
    "hiredis_", "``modules/redis``", "To build redis backend module."
    "Go_ 1.5+", "``modules``", "Build modules written in Go."
    "cmocka_", "``unit tests``", "Unit testing framework."
-   "Python_", "``integration tests``", "For test scripts."
    "Doxygen_", "``documentation``", "Generating API documentation."
    "Sphinx_", "``documentation``", "Building this HTML/PDF documentation."
    "breathe_", "``documentation``", "Exposing Doxygen API doc to Sphinx."
@@ -169,31 +168,37 @@ By default the resolver library is built as a dynamic library with versioned ABI
 
 When the library is linked statically, it usually produces a smaller binary. However linking it to various C modules might violate ODR and increase the size. 
 
-Building dependencies
-~~~~~~~~~~~~~~~~~~~~~
+Resolving dependencies
+~~~~~~~~~~~~~~~~~~~~~~
 
-Several dependencies may not be in the packages yet, the script pulls and installs all dependencies in a chroot.
-You can avoid rebuilding dependencies by specifying `BUILD_IGNORE` variable, see the Dockerfile_ for example.
-Usually you only really need to rebuild libknot_.
+The build system relies on `pkg-config`_ to find dependencies.
+You can override it to force custom versions of the software by environment variables.
 
 .. code-block:: bash
 
-   $ export FAKEROOT="${HOME}/.local"
-   $ export PKG_CONFIG_PATH="${FAKEROOT}/lib/pkgconfig"
-   $ export BUILD_IGNORE="..." # Ignore installed dependencies
-   $ ./scripts/bootstrap-depends.sh ${FAKEROOT}
+   $ make libknot_CFLAGS="-I/opt/include" libknot_LIBS="-L/opt/lib -lknot -ldnssec"
 
-.. note:: The build system relies on `pkg-config`_ to find dependencies.
-   You can override it to force custom versions of the software by environment variables.
+Optional dependencies may be disabled as well using ``HAS_x=yes|no`` variable.
 
-   .. code-block:: bash
+.. code-block:: bash
 
-      $ make libknot_CFLAGS="-I/opt/include" libknot_LIBS="-L/opt/lib -lknot -lknot-int -ldnssec"
+   $ make HAS_go=no HAS_cmocka=no
 
 .. warning:: If the dependencies lie outside of library search path, you need to add them somehow.
    Try ``LD_LIBRARY_PATH`` on Linux/BSD, and ``DYLD_FALLBACK_LIBRARY_PATH`` on OS X.
    Otherwise you need to add the locations to linker search path.
 
+Several dependencies may not be in the packages yet, the script pulls and installs all dependencies in a chroot.
+You can avoid rebuilding dependencies by specifying `BUILD_IGNORE` variable, see the Dockerfile_ for example.
+Usually you only really need to rebuild libknot_.
+
+.. code-block:: bash
+
+   $ export FAKEROOT="${HOME}/.local"
+   $ export PKG_CONFIG_PATH="${FAKEROOT}/lib/pkgconfig"
+   $ export BUILD_IGNORE="..." # Ignore installed dependencies
+   $ ./scripts/bootstrap-depends.sh ${FAKEROOT}
+
 Building extras
 ~~~~~~~~~~~~~~~
 
diff --git a/lib/README.rst b/lib/README.rst
index 56b94dc27439b6dc68de9dfb70938e54e4658a73..ac0d5e94a2efd4db7a0d945e6ff192eecc9cb289 100644
--- a/lib/README.rst
+++ b/lib/README.rst
@@ -259,10 +259,6 @@ In layers that either begin or finalize, you can walk the list of resolved queri
 
 	local last = req:resolved()
 	print(last.type)
-	last = last:next()
-	if last ~= nil then
-		print(last.type)
-	end
 
 As described in the layers, you can not only retrieve information about current query, but also push new ones or pop old ones.
 
diff --git a/lib/cache.c b/lib/cache.c
index 50347f7220d796f20bb6e2a01bd1813aca96bf4e..f9c9b62d35a0ff7331b2f8e7f6031ccd4db5f066 100644
--- a/lib/cache.c
+++ b/lib/cache.c
@@ -20,8 +20,7 @@
 #include <unistd.h>
 #include <errno.h>
 
-#include <libknot/internal/mempattern.h>
-#include <libknot/internal/namedb/namedb_lmdb.h>
+#include <libknot/db/db_lmdb.h>
 #include <libknot/errcode.h>
 #include <libknot/descriptor.h>
 #include <libknot/dname.h>
@@ -48,8 +47,8 @@ static void assert_right_version(struct kr_cache *cache)
 	if (ret != 0) {
 		return; /* N/A, doesn't work. */
 	}
-	namedb_val_t key = { KEY_VERSION, 2 };
-	namedb_val_t val = { NULL, 0 };
+	knot_db_val_t key = { KEY_VERSION, 2 };
+	knot_db_val_t val = { NULL, 0 };
 	ret = txn_api(&txn)->find(&txn.t, &key, &val, 0);
 	if (ret == 0) { /* Version is OK */
 		kr_cache_txn_abort(&txn);
@@ -70,13 +69,13 @@ static void assert_right_version(struct kr_cache *cache)
 	}
 }
 
-int kr_cache_open(struct kr_cache *cache, const namedb_api_t *api, void *opts, mm_ctx_t *mm)
+int kr_cache_open(struct kr_cache *cache, const knot_db_api_t *api, void *opts, knot_mm_t *mm)
 {
 	if (!cache) {
 		return kr_error(EINVAL);
 	}
 	/* Open cache */
-	cache->api = (api == NULL) ? namedb_lmdb_api() : api;
+	cache->api = (api == NULL) ? knot_db_lmdb_api() : api;
 	int ret = cache->api->init(&cache->db, mm, opts);
 	if (ret != 0) {
 		return ret;
@@ -109,7 +108,7 @@ int kr_cache_txn_begin(struct kr_cache *cache, struct kr_cache_txn *txn, unsigne
 	} else {
 		/* Count statistics */
 		txn->owner = cache;
-		if (flags & NAMEDB_RDONLY) {
+		if (flags & KNOT_DB_RDONLY) {
 			cache->stats.txn_read += 1;
 		} else {
 			cache->stats.txn_write += 1;
@@ -166,8 +165,8 @@ static struct kr_cache_entry *lookup(struct kr_cache_txn *txn, uint8_t tag, cons
 	}
 
 	/* Look up and return value */
-	namedb_val_t key = { keybuf, key_len };
-	namedb_val_t val = { NULL, 0 };
+	knot_db_val_t key = { keybuf, key_len };
+	knot_db_val_t val = { NULL, 0 };
 	int ret = txn_api(txn)->find(&txn->t, &key, &val, 0);
 	if (ret != KNOT_EOK) {
 		return NULL;
@@ -220,7 +219,7 @@ int kr_cache_peek(struct kr_cache_txn *txn, uint8_t tag, const knot_dname_t *nam
 	return ret;
 }
 
-static void entry_write(struct kr_cache_entry *dst, struct kr_cache_entry *header, namedb_val_t data)
+static void entry_write(struct kr_cache_entry *dst, struct kr_cache_entry *header, knot_db_val_t data)
 {
 	assert(dst && header);
 	memcpy(dst, header, sizeof(*header));
@@ -229,7 +228,7 @@ static void entry_write(struct kr_cache_entry *dst, struct kr_cache_entry *heade
 }
 
 int kr_cache_insert(struct kr_cache_txn *txn, uint8_t tag, const knot_dname_t *name, uint16_t type,
-                    struct kr_cache_entry *header, namedb_val_t data)
+                    struct kr_cache_entry *header, knot_db_val_t data)
 {
 	if (!txn_is_valid(txn) || !name || !header) {
 		return kr_error(EINVAL);
@@ -241,13 +240,13 @@ int kr_cache_insert(struct kr_cache_txn *txn, uint8_t tag, const knot_dname_t *n
 	if (key_len == 0) {
 		return kr_error(EILSEQ);
 	}
-	namedb_val_t key = { keybuf, key_len };
-	namedb_val_t entry = { NULL, sizeof(*header) + data.len };
-	const namedb_api_t *db_api = txn_api(txn);
+	knot_db_val_t key = { keybuf, key_len };
+	knot_db_val_t entry = { NULL, sizeof(*header) + data.len };
+	const knot_db_api_t *db_api = txn_api(txn);
 
 	/* LMDB can do late write and avoid copy */
 	txn->owner->stats.insert += 1;
-	if (db_api == namedb_lmdb_api()) {
+	if (db_api == knot_db_lmdb_api()) {
 		int ret = db_api->insert(&txn->t, &key, &entry, 0);
 		if (ret != 0) {
 			return ret;
@@ -281,7 +280,7 @@ int kr_cache_remove(struct kr_cache_txn *txn, uint8_t tag, const knot_dname_t *n
 	if (key_len == 0) {
 		return kr_error(EILSEQ);
 	}
-	namedb_val_t key = { keybuf, key_len };
+	knot_db_val_t key = { keybuf, key_len };
 	txn->owner->stats.delete += 1;
 	return txn_api(txn)->del(&txn->t, &key);
 }
@@ -331,7 +330,7 @@ int kr_cache_peek_rank(struct kr_cache_txn *txn, uint8_t tag, const knot_dname_t
 	return found->rank;
 }
 
-int kr_cache_materialize(knot_rrset_t *dst, const knot_rrset_t *src, uint32_t drift, mm_ctx_t *mm)
+int kr_cache_materialize(knot_rrset_t *dst, const knot_rrset_t *src, uint32_t drift, knot_mm_t *mm)
 {
 	if (!dst || !src) {
 		return kr_error(EINVAL);
@@ -391,7 +390,7 @@ int kr_cache_insert_rr(struct kr_cache_txn *txn, const knot_rrset_t *rr, uint16_
 		rd = kr_rdataset_next(rd);
 	}
 
-	namedb_val_t data = { rr->rrs.data, knot_rdataset_size(&rr->rrs) };
+	knot_db_val_t data = { rr->rrs.data, knot_rdataset_size(&rr->rrs) };
 	return kr_cache_insert(txn, KR_CACHE_RR, rr->owner, rr->type, &header, data);
 }
 
@@ -443,6 +442,6 @@ int kr_cache_insert_rrsig(struct kr_cache_txn *txn, const knot_rrset_t *rr, uint
 	}
 
 	uint16_t covered = knot_rrsig_type_covered(&rr->rrs, 0);
-	namedb_val_t data = { rr->rrs.data, knot_rdataset_size(&rr->rrs) };
+	knot_db_val_t data = { rr->rrs.data, knot_rdataset_size(&rr->rrs) };
 	return kr_cache_insert(txn, KR_CACHE_SIG, rr->owner, covered, &header, data);
 }
diff --git a/lib/cache.h b/lib/cache.h
index 3fabea7ced0d18e8764e2af1434a56d308d10607..89ce695f90345479fc126130dcb6eb23d3ab725b 100644
--- a/lib/cache.h
+++ b/lib/cache.h
@@ -17,7 +17,7 @@
 #pragma once
 
 #include <libknot/rrset.h>
-#include <libknot/internal/namedb/namedb.h>
+#include <libknot/db/db.h>
 #include "lib/defines.h"
 
 /** Cache entry tag */
@@ -61,8 +61,8 @@ struct kr_cache_entry
  */
 struct kr_cache
 {
-	namedb_t *db;		      /**< Storage instance */
-	const namedb_api_t *api;      /**< Storage engine */
+	knot_db_t *db;		      /**< Storage instance */
+	const knot_db_api_t *api;      /**< Storage engine */
 	struct {
 		uint32_t hit;         /**< Number of cache hits */
 		uint32_t miss;        /**< Number of cache misses */
@@ -75,7 +75,7 @@ struct kr_cache
 
 /** Cache transaction */
 struct kr_cache_txn {
-    namedb_txn_t t;          /**< Storage transaction */  
+    knot_db_txn_t t;          /**< Storage transaction */  
     struct kr_cache *owner;  /**< Transaction owner */
 };
 
@@ -88,7 +88,7 @@ struct kr_cache_txn {
  * @return 0 or an error code
  */
 KR_EXPORT
-int kr_cache_open(struct kr_cache *cache, const namedb_api_t *api, void *opts, mm_ctx_t *mm);
+int kr_cache_open(struct kr_cache *cache, const knot_db_api_t *api, void *opts, knot_mm_t *mm);
 
 /**
  * Close persistent cache.
@@ -153,7 +153,7 @@ int kr_cache_peek(struct kr_cache_txn *txn, uint8_t tag, const knot_dname_t *nam
  */
 KR_EXPORT
 int kr_cache_insert(struct kr_cache_txn *txn, uint8_t tag, const knot_dname_t *name, uint16_t type,
-                    struct kr_cache_entry *header, namedb_val_t data);
+                    struct kr_cache_entry *header, knot_db_val_t data);
 
 /**
  * Remove asset from cache.
@@ -207,7 +207,7 @@ int kr_cache_peek_rr(struct kr_cache_txn *txn, knot_rrset_t *rr, uint16_t *rank,
  * @return 0 or an errcode
  */
 KR_EXPORT
-int kr_cache_materialize(knot_rrset_t *dst, const knot_rrset_t *src, uint32_t drift, mm_ctx_t *mm);
+int kr_cache_materialize(knot_rrset_t *dst, const knot_rrset_t *src, uint32_t drift, knot_mm_t *mm);
 
 /**
  * Insert RRSet into cache, replacing any existing data.
diff --git a/lib/dnssec.c b/lib/dnssec.c
index 1577aaab234bea7ca834f0bc40584b1fadb099ac..91680adbf82880801de0ea39a64503c457821cee 100644
--- a/lib/dnssec.c
+++ b/lib/dnssec.c
@@ -27,7 +27,7 @@
 #include <libknot/rrtype/dnskey.h>
 #include <libknot/rrtype/nsec.h>
 #include <libknot/rrtype/rrsig.h>
-
+#include <contrib/wire.h>
 
 #include "lib/defines.h"
 #include "lib/dnssec/nsec.h"
diff --git a/lib/dnssec.h b/lib/dnssec.h
index 82ea7bccfa290a0e2970ed3091ea8b05c8d0072d..b6d41df969d326428155e4c0064ed20d03aea731 100644
--- a/lib/dnssec.h
+++ b/lib/dnssec.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include "lib/defines.h"
-#include <libknot/internal/consts.h>
 #include <libknot/packet/pkt.h>
 
 /**
diff --git a/lib/dnssec/nsec.c b/lib/dnssec/nsec.c
index 7106a968e54733242fb27427de1ff301120d2825..a9a0532366c90af6c92f7c8194e3fc85e0167735 100644
--- a/lib/dnssec/nsec.c
+++ b/lib/dnssec/nsec.c
@@ -138,7 +138,7 @@ static int name_error_response_check_rr(int *flags, const knot_rrset_t *nsec,
 }
 
 int kr_nsec_name_error_response_check(const knot_pkt_t *pkt, knot_section_t section_id,
-                                      const knot_dname_t *sname, mm_ctx_t *pool)
+                                      const knot_dname_t *sname)
 {
 	const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id);
 	if (!sec || !sname) {
diff --git a/lib/dnssec/nsec.h b/lib/dnssec/nsec.h
index 41bdc1a746824124ba673bc29ac0176ee3cc7609..21ac6e4d307073490a89d12a134351f4e905aa50 100644
--- a/lib/dnssec/nsec.h
+++ b/lib/dnssec/nsec.h
@@ -16,8 +16,6 @@
 
 #pragma once
 
-#include <libknot/internal/consts.h>
-#include <libknot/internal/mempattern.h>
 #include <libknot/packet/pkt.h>
 
 /**
@@ -35,11 +33,10 @@ bool kr_nsec_bitmap_contains_type(const uint8_t *bm, uint16_t bm_size, uint16_t
  * @param pkt        Packet structure to be processed.
  * @param section_id Packet section to be processed.
  * @param sname      Name to be checked.
- * @param pool
  * @return           0 or error code.
  */
 int kr_nsec_name_error_response_check(const knot_pkt_t *pkt, knot_section_t section_id,
-                                      const knot_dname_t *sname, mm_ctx_t *pool);
+                                      const knot_dname_t *sname);
 
 /**
  * No data response check (RFC4035 3.1.3.1; RFC4035 5.4, bullet 1).
diff --git a/lib/dnssec/nsec3.c b/lib/dnssec/nsec3.c
index dcfa4053e72353d1d76f1d8930e4d21de426973e..a0b22d46d3b219ad7772ac8619b022a6fab3c37d 100644
--- a/lib/dnssec/nsec3.c
+++ b/lib/dnssec/nsec3.c
@@ -21,7 +21,7 @@
 #include <dnssec/error.h>
 #include <dnssec/nsec.h>
 #include <libknot/descriptor.h>
-#include <libknot/internal/base32hex.h>
+#include <contrib/base32hex.h>
 #include <libknot/rrset.h>
 #include <libknot/rrtype/nsec3.h>
 
@@ -107,7 +107,7 @@ static int read_owner_hash(dnssec_binary_t *hash, size_t max_hash_size, const kn
 
 	int32_t ret = base32hex_decode(nsec3->owner + 1, nsec3->owner[0], hash->data, max_hash_size);
 	if (ret < 0) {
-		return ret;
+		return kr_error(EILSEQ);
 	}
 	hash->size = ret;
 
diff --git a/lib/dnssec/nsec3.h b/lib/dnssec/nsec3.h
index b4a50405c5121881a69f2052eb158e95b4150747..50e27e43172ae2db5ac7a362adbf57ec2df87bb4 100644
--- a/lib/dnssec/nsec3.h
+++ b/lib/dnssec/nsec3.h
@@ -16,8 +16,6 @@
 
 #pragma once
 
-#include <libknot/internal/consts.h>
-#include <libknot/internal/mempattern.h>
 #include <libknot/packet/pkt.h>
 
 /**
diff --git a/lib/dnssec/packet/pkt.c b/lib/dnssec/packet/pkt.c
deleted file mode 100644
index 91088aba197958da11ac5de5cf5072a351f048de..0000000000000000000000000000000000000000
--- a/lib/dnssec/packet/pkt.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/*  Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <libknot/internal/consts.h>
-
-#include "lib/dnssec/packet/pkt.h"
-
-/**
- * Search in section for given type.
- * @param sec  Packet section.
- * @param type Type to search for.
- * @return     True if found.
- */
-static bool section_has_type(const knot_pktsection_t *sec, uint16_t type)
-{
-	if (!sec) {
-		return false;
-	}
-
-	for (unsigned i = 0; i < sec->count; ++i) {
-		const knot_rrset_t *rr = knot_pkt_rr(sec, i);
-		if (rr->type == type) {
-			return true;
-		}
-	}
-
-	return false;
-}
-
-bool _knot_pkt_has_type(const knot_pkt_t *pkt, uint16_t type)
-{
-	if (!pkt) {
-		return false;
-	}
-
-	if (section_has_type(knot_pkt_section(pkt, KNOT_ANSWER), type)) {
-		return true;
-	}
-	if (section_has_type(knot_pkt_section(pkt, KNOT_AUTHORITY), type)) {
-		return true;
-	}
-	return section_has_type(knot_pkt_section(pkt, KNOT_ADDITIONAL), type);
-}
diff --git a/lib/dnssec/packet/pkt.h b/lib/dnssec/packet/pkt.h
deleted file mode 100644
index 429ad0fb882716aa216186208198c838e6f5aac7..0000000000000000000000000000000000000000
--- a/lib/dnssec/packet/pkt.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*  Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <libknot/packet/pkt.h>
-
-/**
- * Check whether packet contains given type.
- * @param pkt  Packet to seek through.
- * @param type RR type to search for.
- * @return     True if found.
- */
-bool _knot_pkt_has_type(const knot_pkt_t *pkt, uint16_t type);
diff --git a/lib/dnssec/rrtype/ds.h b/lib/dnssec/rrtype/ds.h
deleted file mode 100644
index 32e131c310679259e25e44a3066e8b40b80becd7..0000000000000000000000000000000000000000
--- a/lib/dnssec/rrtype/ds.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*  Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <libknot/rdataset.h>
-
-static inline
-uint16_t _knot_ds_ktag(const knot_rdataset_t *rrs, size_t pos)
-{
-	KNOT_RDATASET_CHECK(rrs, pos, return 0);
-	return wire_read_u16(knot_rdata_offset(rrs, pos, 0));
-}
-
-static inline
-uint8_t _knot_ds_alg(const knot_rdataset_t *rrs, size_t pos)
-{
-	KNOT_RDATASET_CHECK(rrs, pos, return 0);
-	return *knot_rdata_offset(rrs, pos, 2);
-}
-
-static inline
-uint8_t _knot_ds_dtype(const knot_rdataset_t *rrs, size_t pos)
-{
-	KNOT_RDATASET_CHECK(rrs, pos, return 0);
-	return *knot_rdata_offset(rrs, pos, 3);
-}
-
-static inline
-void _knot_ds_digest(const knot_rdataset_t *rrs, size_t pos,
-                    uint8_t **digest, uint16_t *digest_size)
-{
-	KNOT_RDATASET_CHECK(rrs, pos, return);
-	*digest = knot_rdata_offset(rrs, pos, 4);
-	const knot_rdata_t *rr = knot_rdataset_at(rrs, pos);
-	*digest_size = knot_rdata_rdlen(rr) - 4;
-}
diff --git a/lib/dnssec/signature.c b/lib/dnssec/signature.c
index 1570ab855b217d9da65c2ba9b78eb0a49cc34821..78af44c03e461b89bf9ae6be37b9bceeed9d16e7 100644
--- a/lib/dnssec/signature.c
+++ b/lib/dnssec/signature.c
@@ -26,10 +26,10 @@
 #include <libknot/packet/wire.h>
 #include <libknot/rrset.h>
 #include <libknot/rrtype/rrsig.h>
+#include <libknot/rrtype/ds.h>
 
 #include "lib/defines.h"
 #include "lib/utils.h"
-#include "lib/dnssec/rrtype/ds.h"
 #include "lib/dnssec/signature.h"
 
 static int authenticate_ds(const dnssec_key_t *key, dnssec_binary_t *ds_rdata, uint8_t digest_type)
@@ -69,7 +69,7 @@ int kr_authenticate_referral(const knot_rrset_t *ref, const dnssec_key_t *key)
 			.size = knot_rdata_rdlen(rd),
 			.data = knot_rdata_data(rd)
 		};
-		ret = authenticate_ds(key, &ds_rdata, _knot_ds_dtype(&ref->rrs, i));
+		ret = authenticate_ds(key, &ds_rdata, knot_ds_digest_type(&ref->rrs, i));
 		if (ret == 0) { /* Found a good DS */
 			break;
 		}
diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c
index 69ec3714c678c924d48eb7e1ed846ae844cae838..32485306fec133b19b9d68543b7578087f7f4dbf 100644
--- a/lib/layer/iterate.c
+++ b/lib/layer/iterate.c
@@ -15,6 +15,7 @@
  */
 
 #include <sys/time.h>
+#include <assert.h>
 
 #include <libknot/descriptor.h>
 #include <libknot/rrtype/rdname.h>
@@ -520,7 +521,7 @@ static int resolve(knot_layer_t *ctx, knot_pkt_t *pkt)
 	}
 
 #ifndef NDEBUG
-	lookup_table_t *rcode = lookup_by_id(knot_rcode_names, knot_wire_get_rcode(pkt->wire));
+	const knot_lookup_t *rcode = knot_lookup_by_id(knot_rcode_names, knot_wire_get_rcode(pkt->wire));
 #endif
 
 	/* Check response code. */
diff --git a/lib/layer/pktcache.c b/lib/layer/pktcache.c
index 3330e20f688945765bcb0fd94c25c92efd7702d8..b9b6db957fa3d64f6a72322dd633be8ed9fab543 100644
--- a/lib/layer/pktcache.c
+++ b/lib/layer/pktcache.c
@@ -110,7 +110,7 @@ static int pktcache_peek(knot_layer_t *ctx, knot_pkt_t *pkt)
 	/* Prepare read transaction */
 	struct kr_cache_txn txn;
 	struct kr_cache *cache = &req->ctx->cache;
-	if (kr_cache_txn_begin(cache, &txn, NAMEDB_RDONLY) != 0) {
+	if (kr_cache_txn_begin(cache, &txn, KNOT_DB_RDONLY) != 0) {
 		return ctx->state;
 	}
 
@@ -191,7 +191,7 @@ static int pktcache_stash(knot_layer_t *ctx, knot_pkt_t *pkt)
 	if (kr_cache_txn_begin(&req->ctx->cache, &txn, 0) != 0) {
 		return ctx->state; /* Couldn't acquire cache, ignore. */
 	}
-	namedb_val_t data = { pkt->wire, pkt->size };
+	knot_db_val_t data = { pkt->wire, pkt->size };
 	struct kr_cache_entry header = {
 		.timestamp = qry->timestamp.tv_sec,
 		.ttl = ttl,
diff --git a/lib/layer/rrcache.c b/lib/layer/rrcache.c
index eff217023c549ca4704311486a7971639bb37439..ca5bdedf19fb55a39318dc8b693b9bda43079039 100644
--- a/lib/layer/rrcache.c
+++ b/lib/layer/rrcache.c
@@ -80,7 +80,7 @@ static int loot_rr(struct kr_cache_txn *txn, knot_pkt_t *pkt, const knot_dname_t
 static int loot_rrcache(struct kr_cache *cache, knot_pkt_t *pkt, struct kr_query *qry, uint16_t rrtype, bool dobit)
 {
 	struct kr_cache_txn txn;
-	int ret = kr_cache_txn_begin(cache, &txn, NAMEDB_RDONLY);
+	int ret = kr_cache_txn_begin(cache, &txn, KNOT_DB_RDONLY);
 	if (ret != 0) {
 		return ret;
 	}
@@ -209,7 +209,7 @@ static int stash_commit(map_t *stash, struct kr_query *qry, struct kr_cache_txn
 	return map_walk(stash, &commit_rr, &baton);
 }
 
-static void stash_glue(map_t *stash, knot_pkt_t *pkt, const knot_dname_t *ns_name, mm_ctx_t *pool)
+static void stash_glue(map_t *stash, knot_pkt_t *pkt, const knot_dname_t *ns_name, knot_mm_t *pool)
 {
 	const knot_pktsection_t *additional = knot_pkt_section(pkt, KNOT_ADDITIONAL);
 	for (unsigned i = 0; i < additional->count; ++i) {
@@ -223,7 +223,7 @@ static void stash_glue(map_t *stash, knot_pkt_t *pkt, const knot_dname_t *ns_nam
 }
 
 /* @internal DS is special and is present only parent-side */
-static void stash_ds(struct kr_query *qry, knot_pkt_t *pkt, map_t *stash, mm_ctx_t *pool)
+static void stash_ds(struct kr_query *qry, knot_pkt_t *pkt, map_t *stash, knot_mm_t *pool)
 {
 	const knot_pktsection_t *authority = knot_pkt_section(pkt, KNOT_AUTHORITY);
 	for (unsigned i = 0; i < authority->count; ++i) {
@@ -234,7 +234,7 @@ static void stash_ds(struct kr_query *qry, knot_pkt_t *pkt, map_t *stash, mm_ctx
 	}
 }
 
-static int stash_authority(struct kr_query *qry, knot_pkt_t *pkt, map_t *stash, mm_ctx_t *pool)
+static int stash_authority(struct kr_query *qry, knot_pkt_t *pkt, map_t *stash, knot_mm_t *pool)
 {
 	const knot_pktsection_t *authority = knot_pkt_section(pkt, KNOT_AUTHORITY);
 	for (unsigned i = 0; i < authority->count; ++i) {
@@ -253,7 +253,7 @@ static int stash_authority(struct kr_query *qry, knot_pkt_t *pkt, map_t *stash,
 	return kr_ok();
 }
 
-static int stash_answer(struct kr_query *qry, knot_pkt_t *pkt, map_t *stash, mm_ctx_t *pool)
+static int stash_answer(struct kr_query *qry, knot_pkt_t *pkt, map_t *stash, knot_mm_t *pool)
 {
 	/* Work with QNAME, as minimised name data is cacheable. */
 	const knot_dname_t *cname_begin = knot_pkt_qname(pkt);
@@ -312,7 +312,7 @@ static int rrcache_stash(knot_layer_t *ctx, knot_pkt_t *pkt)
 		ret = stash_answer(qry, pkt, &stash, &req->pool);
 	}
 	/* Cache authority only if chasing referral/cname chain */
-	if (!is_auth || qry != TAIL(req->rplan.pending)) {
+	if (!is_auth || qry != array_tail(req->rplan.pending)) {
 		ret = stash_authority(qry, pkt, &stash, &req->pool);
 	}
 	/* Cache DS records in referrals */
diff --git a/lib/layer/validate.c b/lib/layer/validate.c
index 6f97bb620efc84b8885fd4ad7a4a8f2fa70273e9..88c50e11b3aa728afaf7cf2fd8c7d2b2d448ad09 100644
--- a/lib/layer/validate.c
+++ b/lib/layer/validate.c
@@ -26,7 +26,6 @@
 
 #include "lib/dnssec/nsec.h"
 #include "lib/dnssec/nsec3.h"
-#include "lib/dnssec/packet/pkt.h"
 #include "lib/dnssec.h"
 #include "lib/layer.h"
 #include "lib/resolve.h"
@@ -37,6 +36,44 @@
 
 #define DEBUG_MSG(qry, fmt...) QRDEBUG(qry, "vldr", fmt)
 
+/**
+ * Search in section for given type.
+ * @param sec  Packet section.
+ * @param type Type to search for.
+ * @return     True if found.
+ */
+static bool section_has_type(const knot_pktsection_t *sec, uint16_t type)
+{
+	if (!sec) {
+		return false;
+	}
+
+	for (unsigned i = 0; i < sec->count; ++i) {
+		const knot_rrset_t *rr = knot_pkt_rr(sec, i);
+		if (rr->type == type) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static bool pkt_has_type(const knot_pkt_t *pkt, uint16_t type)
+{
+	if (!pkt) {
+		return false;
+	}
+
+	if (section_has_type(knot_pkt_section(pkt, KNOT_ANSWER), type)) {
+		return true;
+	}
+	if (section_has_type(knot_pkt_section(pkt, KNOT_AUTHORITY), type)) {
+		return true;
+	}
+	return section_has_type(knot_pkt_section(pkt, KNOT_ADDITIONAL), type);
+}
+
+
 /** @internal Baton for validate_section */
 struct validate_baton {
 	const knot_pkt_t *pkt;
@@ -63,7 +100,7 @@ static int validate_rrset(const char *key, void *val, void *data)
 }
 
 static int validate_section(struct kr_query *qry, knot_pkt_t *answer,
-                            knot_section_t section_id, mm_ctx_t *pool,
+                            knot_section_t section_id, knot_mm_t *pool,
                             bool has_nsec3)
 {
 	const knot_pktsection_t *sec = knot_pkt_section(answer, section_id);
@@ -120,7 +157,7 @@ fail:
 	return ret;
 }
 
-static int validate_records(struct kr_query *qry, knot_pkt_t *answer, mm_ctx_t *pool, bool has_nsec3)
+static int validate_records(struct kr_query *qry, knot_pkt_t *answer, knot_mm_t *pool, bool has_nsec3)
 {
 	if (!qry->zone_cut.key) {
 		DEBUG_MSG(qry, "<= no DNSKEY, can't validate\n");
@@ -346,7 +383,7 @@ static int validate(knot_layer_t *ctx, knot_pkt_t *pkt)
 	/* Check if this is a DNSKEY answer, check trust chain and store. */
 	uint8_t pkt_rcode = knot_wire_get_rcode(pkt->wire);
 	uint16_t qtype = knot_pkt_qtype(pkt);
-	bool has_nsec3 = _knot_pkt_has_type(pkt, KNOT_RRTYPE_NSEC3);
+	bool has_nsec3 = pkt_has_type(pkt, KNOT_RRTYPE_NSEC3);
 	if (knot_wire_get_aa(pkt->wire) && qtype == KNOT_RRTYPE_DNSKEY) {
 		ret = validate_keyset(qry, pkt, has_nsec3);
 		if (ret != 0) {
@@ -360,7 +397,7 @@ static int validate(knot_layer_t *ctx, knot_pkt_t *pkt)
 	if (!(qry->flags & QUERY_CACHED) && pkt_rcode == KNOT_RCODE_NXDOMAIN) {
 		/* @todo If knot_pkt_qname(pkt) is used instead of qry->sname then the tests crash. */
 		if (!has_nsec3) {
-			ret = kr_nsec_name_error_response_check(pkt, KNOT_AUTHORITY, qry->sname, &req->pool);
+			ret = kr_nsec_name_error_response_check(pkt, KNOT_AUTHORITY, qry->sname);
 		} else {
 			ret = kr_nsec3_name_error_response_check(pkt, KNOT_AUTHORITY, qry->sname);
 		}
diff --git a/lib/lib.mk b/lib/lib.mk
index 7d89590fb25632431492691d41915340a74cb341..5ed3345113d10f18684dcfa3d6f64098e7e98c4c 100644
--- a/lib/lib.mk
+++ b/lib/lib.mk
@@ -6,7 +6,6 @@ libkres_SOURCES := \
 	lib/layer/pktcache.c   \
 	lib/dnssec/nsec.c      \
 	lib/dnssec/nsec3.c     \
-	lib/dnssec/packet/pkt.c \
 	lib/dnssec/signature.c \
 	lib/dnssec/ta.c        \
 	lib/dnssec.c           \
@@ -25,8 +24,6 @@ libkres_HEADERS := \
 	lib/layer.h            \
 	lib/dnssec/nsec.h      \
 	lib/dnssec/nsec3.h     \
-	lib/dnssec/packet/pkt.h \
-	lib/dnssec/rrtype/ds.h \
 	lib/dnssec/signature.h \
 	lib/dnssec/ta.h        \
 	lib/dnssec.h           \
diff --git a/lib/nsrep.c b/lib/nsrep.c
index b872c33e8bece03be4477f999e0cb69532db7efb..3fd91d7d0b4543d66f0d0bc14bfec15a94060160 100644
--- a/lib/nsrep.c
+++ b/lib/nsrep.c
@@ -18,7 +18,6 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netdb.h>
-#include <libknot/internal/sockaddr.h>
 
 #include "lib/nsrep.h"
 #include "lib/rplan.h"
diff --git a/lib/resolve.c b/lib/resolve.c
index df7c91ff9d427e4cdd8f6fb59e0ae78df1850d56..e104faf957cb1dbc15ef5823b569284de6493b00 100644
--- a/lib/resolve.c
+++ b/lib/resolve.c
@@ -17,6 +17,8 @@
 #include <ctype.h>
 #include <stdio.h>
 #include <fcntl.h>
+#include <assert.h>
+#include <arpa/inet.h>
 #include <libknot/rrtype/rdname.h>
 #include <libknot/descriptor.h>
 #include <ucw/mempool.h>
@@ -161,7 +163,7 @@ static int ns_fetch_cut(struct kr_query *qry, struct kr_request *req, knot_pkt_t
 
 	/* Find closest zone cut from cache */
 	struct kr_cache_txn txn;
-	if (kr_cache_txn_begin(&req->ctx->cache, &txn, NAMEDB_RDONLY) == 0) {
+	if (kr_cache_txn_begin(&req->ctx->cache, &txn, KNOT_DB_RDONLY) == 0) {
 		/* If at/subdomain of parent zone cut, start from its encloser.
 		 * This is for case when we get to a dead end (and need glue from parent), or DS refetch. */
 		struct kr_query *parent = qry->parent;
@@ -311,10 +313,17 @@ static int answer_finalize(struct kr_request *request, int state)
 			return ret;
 		}
 	}
-	/* Set AD=1 if succeeded and requested secured answer. */
 	struct kr_rplan *rplan = &request->rplan;
-	if (state == KNOT_STATE_DONE && !EMPTY_LIST(rplan->resolved)) {
-		struct kr_query *last = TAIL(rplan->resolved);
+	/* Always set SERVFAIL for bogus answers. */
+	if (state == KNOT_STATE_FAIL && rplan->pending.len > 0) {
+		struct kr_query *last = array_tail(rplan->pending);
+		if ((last->flags & QUERY_DNSSEC_WANT) && (last->flags & QUERY_DNSSEC_BOGUS)) {
+			knot_wire_set_rcode(answer->wire,KNOT_RCODE_SERVFAIL);
+		}
+	}
+	/* Set AD=1 if succeeded and requested secured answer. */
+	if (state == KNOT_STATE_DONE && rplan->resolved.len > 0) {
+		struct kr_query *last = array_tail(rplan->resolved);
 		/* Do not set AD for RRSIG query, as we can't validate it. */
 		if ((last->flags & QUERY_DNSSEC_WANT) && knot_pkt_has_dnssec(answer) &&
 			knot_pkt_qtype(answer) != KNOT_RRTYPE_RRSIG) {
@@ -360,8 +369,6 @@ int kr_resolve_begin(struct kr_request *request, struct kr_context *ctx, knot_pk
 	request->options = ctx->options;
 	request->state = KNOT_STATE_CONSUME;
 	request->current_query = NULL;
-	request->qsource.key = NULL;
-	request->qsource.addr = NULL;
 	array_init(request->authority);
 	array_init(request->additional);
 
@@ -420,7 +427,7 @@ int kr_resolve_consume(struct kr_request *request, const struct sockaddr *src, k
 	}
 
 	/* Different processing for network error */
-	struct kr_query *qry = TAIL(rplan->pending);
+	struct kr_query *qry = array_tail(rplan->pending);
 	bool tried_tcp = (qry->flags & QUERY_TCP);
 	if (!packet || packet->size == 0) {
 		if (tried_tcp)
@@ -447,7 +454,7 @@ int kr_resolve_consume(struct kr_request *request, const struct sockaddr *src, k
 				gettimeofday(&now, NULL);
 				kr_nsrep_update_rtt(&qry->ns, src, time_diff(&qry->timestamp, &now), ctx->cache_rtt);
 				WITH_DEBUG {
-					char addr_str[SOCKADDR_STRLEN];
+					char addr_str[INET6_ADDRSTRLEN];
 					inet_ntop(src->sa_family, kr_inaddr(src), addr_str, sizeof(addr_str));
 					DEBUG_MSG(qry, "<= server: '%s' rtt: %ld ms\n", addr_str, time_diff(&qry->timestamp, &now));
 				}
@@ -457,7 +464,7 @@ int kr_resolve_consume(struct kr_request *request, const struct sockaddr *src, k
 		} else if (!(qry->flags & QUERY_DNSSEC_BOGUS)) {
 			kr_nsrep_update_rtt(&qry->ns, src, KR_NS_TIMEOUT, ctx->cache_rtt);
 			WITH_DEBUG {
-				char addr_str[SOCKADDR_STRLEN];
+				char addr_str[INET6_ADDRSTRLEN];
 				inet_ntop(src->sa_family, kr_inaddr(src), addr_str, sizeof(addr_str));
 				DEBUG_MSG(qry, "=> server: '%s' flagged as 'bad'\n", addr_str);
 			}
@@ -624,7 +631,7 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t
 		return KNOT_STATE_FAIL;
 	}
 	/* If we have deferred answers, resume them. */
-	struct kr_query *qry = TAIL(rplan->pending);
+	struct kr_query *qry = array_tail(rplan->pending);
 	if (qry->deferred != NULL) {
 		/* @todo: Refactoring validator, check trust chain before resuming. */
 		switch(trust_chain_check(request, qry)) {
@@ -718,7 +725,7 @@ ns_election:
 	}
 
 	WITH_DEBUG {
-	char qname_str[KNOT_DNAME_MAXLEN], zonecut_str[KNOT_DNAME_MAXLEN], ns_str[SOCKADDR_STRLEN], type_str[16];
+	char qname_str[KNOT_DNAME_MAXLEN], zonecut_str[KNOT_DNAME_MAXLEN], ns_str[INET6_ADDRSTRLEN], type_str[16];
 	knot_dname_to_str(qname_str, knot_pkt_qname(packet), sizeof(qname_str));
 	knot_dname_to_str(zonecut_str, qry->zone_cut.name, sizeof(zonecut_str));
 	knot_rrtype_to_string(knot_pkt_qtype(packet), type_str, sizeof(type_str));
@@ -760,7 +767,7 @@ int kr_resolve_finish(struct kr_request *request, int state)
 	request->state = state;
 	ITERATE_LAYERS(request, NULL, finish);
 	DEBUG_MSG(NULL, "finished: %d, queries: %zu, mempool: %zu B\n",
-	          request->state, list_size(&rplan->resolved), (size_t) mp_total_size(request->pool.ctx));
+	          request->state, rplan->resolved.len, (size_t) mp_total_size(request->pool.ctx));
 	return KNOT_STATE_DONE;
 }
 
@@ -772,7 +779,7 @@ struct kr_rplan *kr_resolve_plan(struct kr_request *request)
 	return NULL;
 }
 
-mm_ctx_t *kr_resolve_pool(struct kr_request *request)
+knot_mm_t *kr_resolve_pool(struct kr_request *request)
 {
 	if (request) {
 		return &request->pool;
diff --git a/lib/resolve.h b/lib/resolve.h
index 905e134e003c123b5cd613871f6e695e8304219a..d79b58a6c9ef19ff989c0bb5c480f9d6572b029c 100644
--- a/lib/resolve.h
+++ b/lib/resolve.h
@@ -92,7 +92,7 @@ struct kr_context
 	kr_nsrep_lru_t *cache_rtt;
 	kr_nsrep_lru_t *cache_rep;
 	module_array_t *modules;
-	mm_ctx_t *pool;
+	knot_mm_t *pool;
 };
 
 /**
@@ -118,7 +118,7 @@ struct kr_request {
     rr_array_t authority;
     rr_array_t additional;
     struct kr_rplan rplan;
-    mm_ctx_t pool;
+    knot_mm_t pool;
 };
 
 /**
@@ -191,5 +191,5 @@ struct kr_rplan *kr_resolve_plan(struct kr_request *request);
  * @return         mempool
  */
 KR_EXPORT KR_PURE
-mm_ctx_t *kr_resolve_pool(struct kr_request *request);
+knot_mm_t *kr_resolve_pool(struct kr_request *request);
 
diff --git a/lib/rplan.c b/lib/rplan.c
index 8bae41cd1dab5e67bfab02c9b43c5a30db12ffbd..6eb52a0dca747debf0740200cf1dca6f3a0f9301 100644
--- a/lib/rplan.c
+++ b/lib/rplan.c
@@ -29,19 +29,19 @@
     ((q)->sclass == (cls) && (q)->stype == type && knot_dname_is_equal((q)->sname, name))
 
 /** @internal LUT of query flag names. */
-const lookup_table_t query_flag_names[] = {
+const knot_lookup_t query_flag_names[] = {
 	#define X(flag, _) { QUERY_ ## flag, #flag },
 	QUERY_FLAGS(X)
 	#undef X
 	{ 0, NULL }
 };
 
-const lookup_table_t *kr_query_flag_names(void)
+const knot_lookup_t *kr_query_flag_names(void)
 {
 	return query_flag_names;
 }
 
-static struct kr_query *query_create(mm_ctx_t *pool, const knot_dname_t *name)
+static struct kr_query *query_create(knot_mm_t *pool, const knot_dname_t *name)
 {
 	if (name == NULL) {
 		return NULL;
@@ -63,14 +63,14 @@ static struct kr_query *query_create(mm_ctx_t *pool, const knot_dname_t *name)
 	return qry;
 }
 
-static void query_free(mm_ctx_t *pool, struct kr_query *qry)
+static void query_free(knot_mm_t *pool, struct kr_query *qry)
 {
 	kr_zonecut_deinit(&qry->zone_cut);
 	mm_free(pool, qry->sname);
 	mm_free(pool, qry);
 }
 
-int kr_rplan_init(struct kr_rplan *rplan, struct kr_request *request, mm_ctx_t *pool)
+int kr_rplan_init(struct kr_rplan *rplan, struct kr_request *request, knot_mm_t *pool)
 {
 	if (rplan == NULL) {
 		return KNOT_EINVAL;
@@ -80,8 +80,8 @@ int kr_rplan_init(struct kr_rplan *rplan, struct kr_request *request, mm_ctx_t *
 
 	rplan->pool = pool;
 	rplan->request = request;
-	init_list(&rplan->pending);
-	init_list(&rplan->resolved);
+	array_init(rplan->pending);
+	array_init(rplan->resolved);
 	return KNOT_EOK;
 }
 
@@ -91,13 +91,14 @@ void kr_rplan_deinit(struct kr_rplan *rplan)
 		return;
 	}
 
-	struct kr_query *qry = NULL, *next = NULL;
-	WALK_LIST_DELSAFE(qry, next, rplan->pending) {
-		query_free(rplan->pool, qry);
+	for (size_t i = 0; i < rplan->pending.len; ++i) {
+		query_free(rplan->pool, rplan->pending.at[i]);
 	}
-	WALK_LIST_DELSAFE(qry, next, rplan->resolved) {
-		query_free(rplan->pool, qry);
+	for (size_t i = 0; i < rplan->resolved.len; ++i) {
+		query_free(rplan->pool, rplan->resolved.at[i]);
 	}
+	array_clear_mm(rplan->pending, mm_free, rplan->pool);
+	array_clear_mm(rplan->resolved, mm_free, rplan->pool);
 }
 
 bool kr_rplan_empty(struct kr_rplan *rplan)
@@ -106,7 +107,7 @@ bool kr_rplan_empty(struct kr_rplan *rplan)
 		return true;
 	}
 
-	return EMPTY_LIST(rplan->pending);
+	return rplan->pending.len == 0;
 }
 
 struct kr_query *kr_rplan_push(struct kr_rplan *rplan, struct kr_query *parent,
@@ -116,6 +117,12 @@ struct kr_query *kr_rplan_push(struct kr_rplan *rplan, struct kr_query *parent,
 		return NULL;
 	}
 
+	/* Make sure there's enough space */
+	int ret = array_reserve_mm(rplan->pending, rplan->pending.len + 1, kr_memreserve, rplan->pool);
+	if (ret != 0) {
+		return NULL;
+	}
+
 	struct kr_query *qry = query_create(rplan->pool, name);
 	if (qry == NULL) {
 		return NULL;
@@ -126,8 +133,8 @@ struct kr_query *kr_rplan_push(struct kr_rplan *rplan, struct kr_query *parent,
 	qry->parent = parent;
 	qry->ns.addr[0].ip.sa_family = AF_UNSPEC;
 	gettimeofday(&qry->timestamp, NULL);
-	add_tail(&rplan->pending, &qry->node);
 	kr_zonecut_init(&qry->zone_cut, (const uint8_t *)"", rplan->pool);
+	array_push(rplan->pending, qry);
 
 	WITH_DEBUG {
 	char name_str[KNOT_DNAME_MAXLEN], type_str[16];
@@ -144,16 +151,26 @@ int kr_rplan_pop(struct kr_rplan *rplan, struct kr_query *qry)
 		return KNOT_EINVAL;
 	}
 
-	rem_node(&qry->node);
-	add_tail(&rplan->resolved, &qry->node);
+	/* Make sure there's enough space */
+	int ret = array_reserve_mm(rplan->resolved, rplan->resolved.len + 1, kr_memreserve, rplan->pool);
+	if (ret != 0) {
+		return ret;
+	}
+
+	/* Find the query, it will likely be on top */
+	for (size_t i = rplan->pending.len; i --> 0;) {
+		if (rplan->pending.at[i] == qry) {
+			array_del(rplan->pending, i);
+			array_push(rplan->resolved, qry);
+			break;
+		}
+	}
 	return KNOT_EOK;
 }
 
 bool kr_rplan_satisfies(struct kr_query *closure, const knot_dname_t *name, uint16_t cls, uint16_t type)
 {
-	if (!name)
-		return false;
-	while (closure != NULL) {
+	while (name && closure) {
 		if (QUERY_PROVIDES(closure, name, cls, type)) {
 			return true;
 		}
@@ -164,18 +181,10 @@ bool kr_rplan_satisfies(struct kr_query *closure, const knot_dname_t *name, uint
 
 struct kr_query *kr_rplan_resolved(struct kr_rplan *rplan)
 {
-	if (EMPTY_LIST(rplan->resolved)) {
-		return NULL;
-	}
-	return TAIL(rplan->resolved);
-}
-
-struct kr_query *kr_rplan_next(struct kr_query *qry)
-{
-	if (!qry) {
+	if (rplan->resolved.len == 0) {
 		return NULL;
 	}
-	return (struct kr_query *)qry->node.prev; /* The lists are used as stack, TOP is the TAIL. */
+	return array_tail(rplan->resolved);
 }
 
 #undef DEBUG_MSG
diff --git a/lib/rplan.h b/lib/rplan.h
index 0d05269c755d4c7e1140675489af4a1a7ddd866a..f2e2cbaf33b0e3d5e942fb9424e826d0a2c6470b 100644
--- a/lib/rplan.h
+++ b/lib/rplan.h
@@ -18,9 +18,7 @@
 
 #include <sys/time.h>
 #include <libknot/dname.h>
-#include <libknot/internal/lists.h>
-#include <libknot/internal/namedb/namedb.h>
-#include <libknot/internal/sockaddr.h>
+#include <libknot/codes.h>
 
 #include "lib/cache.h"
 #include "lib/zonecut.h"
@@ -55,13 +53,12 @@ enum kr_query_flag {
 
 /** Query flag names table */
 KR_EXPORT KR_CONST
-const lookup_table_t *kr_query_flag_names(void);
+const knot_lookup_t *kr_query_flag_names(void);
 
 /**
  * Single query representation.
  */
 struct kr_query {
-	node_t node;
 	struct kr_query *parent;
 	knot_dname_t *sname;
 	uint16_t stype;
@@ -75,6 +72,9 @@ struct kr_query {
 	struct kr_layer_pickle *deferred;
 };
 
+/** @internal Array of queries. */
+typedef array_t(struct kr_query *) kr_qarray_t;
+
 /**
  * Query resolution plan structure.
  *
@@ -83,10 +83,10 @@ struct kr_query {
  * It also keeps a notion of current zone cut.
  */
 struct kr_rplan {
-	list_t pending;              /**< List of pending queries. */
-	list_t resolved;             /**< List of resolved queries. */
-	struct kr_request *request;  /**< Parent resolution request. */
-	mm_ctx_t *pool;              /**< Temporary memory pool. */
+	kr_qarray_t pending;        /**< List of pending queries. */
+	kr_qarray_t resolved;       /**< List of resolved queries. */
+	struct kr_request *request; /**< Parent resolution request. */
+	knot_mm_t *pool;             /**< Temporary memory pool. */
 };
 
 /**
@@ -96,7 +96,7 @@ struct kr_rplan {
  * @param pool ephemeral memory pool for whole resolution
  */
 KR_EXPORT
-int kr_rplan_init(struct kr_rplan *rplan, struct kr_request *request, mm_ctx_t *pool);
+int kr_rplan_init(struct kr_rplan *rplan, struct kr_request *request, knot_mm_t *pool);
 
 /**
  * Deinitialize resolution plan, aborting any uncommited transactions.
diff --git a/lib/utils.c b/lib/utils.c
index 4cad3f4ad7876f109feac5d8d04436adf0c08ec8..00e5d65fe86296529f60844fe64b04b3d86896cb 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -180,7 +180,7 @@ int kr_memreserve(void *baton, char **mem, size_t elm_size, size_t want, size_t
     if (*have >= want) {
         return 0;
     } else {
-        mm_ctx_t *pool = baton;
+        knot_mm_t *pool = baton;
         size_t next_size = array_next_count(want);
         void *mem_new = mm_alloc(pool, next_size * elm_size);
         if (mem_new != NULL) {
@@ -239,6 +239,13 @@ const char *kr_inaddr(const struct sockaddr *addr)
 	}
 }
 
+int kr_inaddr_family(const struct sockaddr *addr)
+{
+	if (!addr)
+		return AF_UNSPEC;
+	return addr->sa_family;
+}
+
 int kr_inaddr_len(const struct sockaddr *addr)
 {
 	if (!addr) {
@@ -337,7 +344,7 @@ int kr_rrkey(char *key, const knot_dname_t *owner, uint16_t type, uint8_t rank)
 	return (char *)&key_buf[ret] - key;
 }
 
-int kr_rrmap_add(map_t *stash, const knot_rrset_t *rr, uint8_t rank, mm_ctx_t *pool)
+int kr_rrmap_add(map_t *stash, const knot_rrset_t *rr, uint8_t rank, knot_mm_t *pool)
 {
 	if (!stash || !rr) {
 		return kr_error(EINVAL);
@@ -372,7 +379,7 @@ int kr_rrmap_add(map_t *stash, const knot_rrset_t *rr, uint8_t rank, mm_ctx_t *p
 	return knot_rdataset_merge(&stashed->rrs, &rr->rrs, pool);
 }
 
-int kr_rrarray_add(rr_array_t *array, const knot_rrset_t *rr, mm_ctx_t *pool)
+int kr_rrarray_add(rr_array_t *array, const knot_rrset_t *rr, knot_mm_t *pool)
 {
 	int ret = array_reserve_mm(*array, array->len + 1, kr_memreserve, pool);
 	if (ret != 0) {
diff --git a/lib/utils.h b/lib/utils.h
index d47dce3b6cec0814b9baf1610ce44abf7cd0ffa0..698bd6e0772c1bb17b2376da9238690fe1879637 100644
--- a/lib/utils.h
+++ b/lib/utils.h
@@ -17,11 +17,13 @@
 #pragma once
 
 #include <stdio.h>
+#include <stdbool.h>
 #include <sys/time.h>
 #include <netinet/in.h>
 #include <libknot/packet/pkt.h>
 #include "lib/generic/map.h"
 #include "lib/generic/array.h"
+#include "lib/defines.h"
 
 /*
  * Logging and debugging.
@@ -42,6 +44,22 @@ KR_EXPORT void kr_log_debug(const char *fmt, ...);
 #define WITH_DEBUG if(0)
 #endif
 
+/** @cond Memory alloc routines */
+static inline void *mm_alloc(knot_mm_t *mm, size_t size)
+{
+	if (mm) return mm->alloc(mm->ctx, size);
+	else return malloc(size);
+}
+static inline void mm_free(knot_mm_t *mm, void *what)
+{
+	if (mm) { 
+		if (mm->free)
+			mm->free(what);
+	}
+	else free(what);
+}
+/* @endcond */
+
 /** Return time difference in miliseconds.
   * @note based on the _BSD_SOURCE timersub() macro */
 static inline long time_diff(struct timeval *begin, struct timeval *end) {
@@ -77,7 +95,7 @@ int kr_rand_reseed(void);
 KR_EXPORT
 unsigned kr_rand_uint(unsigned max);
 
-/** Memory reservation routine for mm_ctx_t */
+/** Memory reservation routine for knot_mm_t */
 KR_EXPORT
 int kr_memreserve(void *baton, char **mem, size_t elm_size, size_t want, size_t *have);
 
@@ -93,6 +111,9 @@ int kr_pkt_put(knot_pkt_t *pkt, const knot_dname_t *name, uint32_t ttl,
 /** Address bytes for given family. */
 KR_EXPORT KR_PURE
 const char *kr_inaddr(const struct sockaddr *addr);
+/** Address family. */
+KR_EXPORT KR_PURE
+int kr_inaddr_family(const struct sockaddr *addr);
 /** Address length for given family. */
 KR_EXPORT KR_PURE
 int kr_inaddr_len(const struct sockaddr *addr);
@@ -131,10 +152,10 @@ int kr_rrkey(char *key, const knot_dname_t *owner, uint16_t type, uint8_t rank);
  * @note RRSIG RRSets are merged according the type covered fields.
  * @return 0 or an error
  */
-int kr_rrmap_add(map_t *stash, const knot_rrset_t *rr, uint8_t rank, mm_ctx_t *pool);
+int kr_rrmap_add(map_t *stash, const knot_rrset_t *rr, uint8_t rank, knot_mm_t *pool);
 
 /** @internal Add RRSet copy to RR array. */
-int kr_rrarray_add(rr_array_t *array, const knot_rrset_t *rr, mm_ctx_t *pool);
+int kr_rrarray_add(rr_array_t *array, const knot_rrset_t *rr, knot_mm_t *pool);
 
 /**
  * Call module property.
diff --git a/lib/zonecut.c b/lib/zonecut.c
index 05e18a9cf8eab42977541409152c211bb3fd3990..cab154ea3e873d5c05cd12cdb8bf6a486c306407 100644
--- a/lib/zonecut.c
+++ b/lib/zonecut.c
@@ -78,7 +78,7 @@ static void update_cut_name(struct kr_zonecut *cut, const knot_dname_t *name)
 	cut->name = next_name;
 }
 
-int kr_zonecut_init(struct kr_zonecut *cut, const knot_dname_t *name, mm_ctx_t *pool)
+int kr_zonecut_init(struct kr_zonecut *cut, const knot_dname_t *name, knot_mm_t *pool)
 {
 	if (!cut || !name) {
 		return kr_error(EINVAL);
@@ -349,7 +349,7 @@ static int fetch_ns(struct kr_context *ctx, struct kr_zonecut *cut, const knot_d
  * Fetch RRSet of given type.
  */
 static int fetch_rrset(knot_rrset_t **rr, const knot_dname_t *owner, uint16_t type,
-                       struct kr_cache_txn *txn, mm_ctx_t *pool, uint32_t timestamp)
+                       struct kr_cache_txn *txn, knot_mm_t *pool, uint32_t timestamp)
 {
 	if (!rr) {
 		return kr_error(ENOENT);
diff --git a/lib/zonecut.h b/lib/zonecut.h
index 64ebd7b5b998b325171ef1faa2f416138ed0e570..b87f80cda691012375bb3d8f0415ff6d429c1cce 100644
--- a/lib/zonecut.h
+++ b/lib/zonecut.h
@@ -33,7 +33,7 @@ struct kr_zonecut {
 	knot_rrset_t* key;  /**< Zone cut DNSKEY. */
 	knot_rrset_t* trust_anchor; /**< Current trust anchor. */
 	struct kr_zonecut *parent; /**< Parent zone cut. */
-	mm_ctx_t *pool;     /**< Memory pool. */
+	knot_mm_t *pool;     /**< Memory pool. */
 };
 
 /**
@@ -44,7 +44,7 @@ struct kr_zonecut {
  * @return 0 or error code
  */
 KR_EXPORT
-int kr_zonecut_init(struct kr_zonecut *cut, const knot_dname_t *name, mm_ctx_t *pool);
+int kr_zonecut_init(struct kr_zonecut *cut, const knot_dname_t *name, knot_mm_t *pool);
 
 /**
  * Clear the structure and free the address set.
diff --git a/modules/cachectl/cachectl.c b/modules/cachectl/cachectl.c
index ce7c407d4696ec01f8da89d679e08ec04f0f87b6..f47bc42520b7faa9a3fb9c1fecf5800cbd2336d7 100644
--- a/modules/cachectl/cachectl.c
+++ b/modules/cachectl/cachectl.c
@@ -27,6 +27,7 @@
 
 #include <time.h>
 #include <libknot/descriptor.h>
+#include <libknot/error.h>
 #include <ccan/json/json.h>
 
 #include "daemon/engine.h"
@@ -40,7 +41,7 @@
  * Properties.
  */
 
-typedef int (*cache_cb_t)(struct kr_cache_txn *txn, namedb_iter_t *it, namedb_val_t *key, void *baton);
+typedef int (*cache_cb_t)(struct kr_cache_txn *txn, knot_db_iter_t *it, knot_db_val_t *key, void *baton);
 
 /** @internal Prefix walk. */
 static int cache_prefixed(struct engine *engine, const char *args, unsigned txn_flags, cache_cb_t cb, void *baton)
@@ -76,7 +77,7 @@ static int cache_prefixed(struct engine *engine, const char *args, unsigned txn_
 
 	/* Start search transaction */
 	struct kr_cache *cache = &engine->resolver.cache;
-	const namedb_api_t *api = cache->api;
+	const knot_db_api_t *api = cache->api;
 	struct kr_cache_txn txn;
 	ret = kr_cache_txn_begin(cache, &txn, txn_flags);
 	if (ret != 0) {
@@ -86,10 +87,10 @@ static int cache_prefixed(struct engine *engine, const char *args, unsigned txn_
 	/* Walk through cache records matching given prefix.
 	 * Note that since the backend of the cache is opaque, there's no exactly efficient
 	 * way to do prefix search (i.e. Redis uses hashtable but offers SCAN, LMDB can do lexical closest match, ...). */
-	namedb_val_t key = { prefix, prefix_len };
-	namedb_iter_t *it = api->iter_begin(&txn.t, 0);
+	knot_db_val_t key = { prefix, prefix_len };
+	knot_db_iter_t *it = api->iter_begin(&txn.t, 0);
 	if (it) { /* Seek first key matching the prefix. */
-		it = api->iter_seek(it, &key, NAMEDB_GEQ);
+		it = api->iter_seek(it, &key, KNOT_DB_GEQ);
 	}
 	while (it != NULL) {
 		if (api->iter_key(it, &key) != 0) {
@@ -123,7 +124,7 @@ static bool is_expired(struct kr_cache_entry *entry, uint32_t drift)
 }
 
 /** @internal Delete iterated key. */
-static int cache_delete_cb(struct kr_cache_txn *txn, namedb_iter_t *it, namedb_val_t *key, void *baton)
+static int cache_delete_cb(struct kr_cache_txn *txn, knot_db_iter_t *it, knot_db_val_t *key, void *baton)
 {
 	struct kr_cache *cache = txn->owner;
 	return cache->api->del(&txn->t, key);
@@ -140,7 +141,7 @@ static char* prune(void *env, struct kr_module *module, const char *args)
 {
 	struct engine *engine = env;
 	struct kr_cache *cache = &engine->resolver.cache;
-	const namedb_api_t *storage = cache->api;
+	const knot_db_api_t *storage = cache->api;
 
 	struct kr_cache_txn txn;
 	int ret = kr_cache_txn_begin(cache, &txn, 0);
@@ -161,10 +162,10 @@ static char* prune(void *env, struct kr_module *module, const char *args)
 	/* Fetch current time and start iterating */
 	struct timeval now;
 	gettimeofday(&now, NULL);
-	namedb_iter_t *it = storage->iter_begin(&txn.t, 0);
+	knot_db_iter_t *it = storage->iter_begin(&txn.t, 0);
 	while (it && pruned < prune_max) {
 		/* Fetch RR from cache */
-		namedb_val_t key, val;
+		knot_db_val_t key, val;
 		if (storage->iter_key(it, &key) != 0 ||
 		    storage->iter_val(it, &val) != 0) {
 			break;
@@ -242,7 +243,7 @@ static char* clear(void *env, struct kr_module *module, const char *args)
 }
 
 /** @internal Serialize cached record name into JSON. */
-static int cache_dump_cb(struct kr_cache_txn *txn, namedb_iter_t *it, namedb_val_t *key, void *baton)
+static int cache_dump_cb(struct kr_cache_txn *txn, knot_db_iter_t *it, knot_db_val_t *key, void *baton)
 {
 	JsonNode* json_records = baton;
 	char buf[KNOT_DNAME_MAXLEN];
@@ -291,7 +292,7 @@ static char* get(void *env, struct kr_module *module, const char *args)
 	char *result = NULL;
 	JsonNode *json_records = json_mkobject();
 	if (json_records) {
-		int ret = cache_prefixed(env, args, NAMEDB_RDONLY, &cache_dump_cb, json_records);
+		int ret = cache_prefixed(env, args, KNOT_DB_RDONLY, &cache_dump_cb, json_records);
 		if (ret == 0) {
 			result = json_encode(json_records);
 		}
diff --git a/modules/hints/hints.c b/modules/hints/hints.c
index 03c13ce0699e6bea705f9731d815ad9bab4866bb..ff7eb922a190d634905c26973c66917d50ce3612 100644
--- a/modules/hints/hints.c
+++ b/modules/hints/hints.c
@@ -214,7 +214,13 @@ static int query(knot_layer_t *ctx, knot_pkt_t *pkt)
 static int parse_addr_str(struct sockaddr_storage *sa, const char *addr)
 {
 	int family = strchr(addr, ':') ? AF_INET6 : AF_INET;
-	return sockaddr_set(sa, family, addr, 0);
+	memset(sa, 0, sizeof(struct sockaddr_storage));
+	sa->ss_family = family;
+	char *addr_bytes = (char *)kr_inaddr((struct sockaddr *)sa);
+	if (inet_pton(family, addr, addr_bytes) < 1) {
+		return kr_error(EILSEQ);
+	}
+	return 0;
 }
 
 static int add_pair(struct kr_zonecut *hints, const char *name, const char *addr)
@@ -276,11 +282,11 @@ static int load(struct kr_module *module, const char *path)
 	}
 
 	/* Create pool and copy itself */
-	mm_ctx_t _pool = {
+	knot_mm_t _pool = {
 		.ctx = mp_new(4096),
-		.alloc = (mm_alloc_t) mp_alloc
+		.alloc = (knot_mm_alloc_t) mp_alloc
 	};
-	mm_ctx_t *pool = mm_alloc(&_pool, sizeof(*pool));
+	knot_mm_t *pool = mm_alloc(&_pool, sizeof(*pool));
 	if (!pool) {
 		return kr_error(ENOMEM);
 	}
@@ -330,7 +336,7 @@ static char* hint_set(void *env, struct kr_module *module, const char *args)
 /** @internal Pack address list into JSON array. */
 static JsonNode *pack_addrs(pack_t *pack)
 {
-	char buf[SOCKADDR_STRLEN];
+	char buf[INET6_ADDRSTRLEN];
 	JsonNode *root = json_mkarray();
 	uint8_t *addr = pack_head(*pack);
 	while (addr != pack_tail(*pack)) {
diff --git a/modules/kmemcached/kmemcached.c b/modules/kmemcached/kmemcached.c
index 367c31c8585b9046d8d34744365eafc8ce813747..d1979818ac9766e126205c78ef0d933e5eb05347 100644
--- a/modules/kmemcached/kmemcached.c
+++ b/modules/kmemcached/kmemcached.c
@@ -14,7 +14,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <libknot/internal/namedb/namedb.h>
+#include <libknot/db/db.h>
 #include <contrib/cleanup.h>
 
 #include "daemon/engine.h"
@@ -22,7 +22,7 @@
 #include "lib/cache.h"
 
 /** @internal Memcached API */
-extern const namedb_api_t *namedb_memcached_api(void);
+extern const knot_db_api_t *namedb_memcached_api(void);
 
 /** @internal Make memcached options. */
 void *namedb_memcached_mkopts(const char *conf, size_t maxsize)
diff --git a/modules/kmemcached/namedb_memcached.c b/modules/kmemcached/namedb_memcached.c
index 3aefe1b72618d07efec9c8d45695b278eb60f26e..5d764565eaad84adfbe23ddc82509ca7d023eab9 100644
--- a/modules/kmemcached/namedb_memcached.c
+++ b/modules/kmemcached/namedb_memcached.c
@@ -26,7 +26,7 @@
 #include <assert.h>
 #include <string.h>
 #include <libmemcached/memcached.h>
-#include <libknot/internal/namedb/namedb.h>
+#include <libknot/db/db.h>
 #include <libknot/errcode.h>
 #include <contrib/cleanup.h>
 
@@ -37,7 +37,7 @@
 /* Oh, the irony... */
 typedef array_t(char *) freelist_t;
 
-static int init(namedb_t **db, mm_ctx_t *mm, void *arg)
+static int init(knot_db_t **db, knot_mm_t *mm, void *arg)
 {
 	if (!db || !arg) {
 		return KNOT_EINVAL;
@@ -55,12 +55,12 @@ static int init(namedb_t **db, mm_ctx_t *mm, void *arg)
 	return KNOT_EOK;
 }
 
-static void deinit(namedb_t *db)
+static void deinit(knot_db_t *db)
 {
 	memcached_free((memcached_st *)db);
 }
 
-static int txn_begin(namedb_t *db, namedb_txn_t *txn, unsigned flags)
+static int txn_begin(knot_db_t *db, knot_db_txn_t *txn, unsigned flags)
 {
 	freelist_t *freelist = malloc(sizeof(*freelist));
 	if (!freelist) {
@@ -72,7 +72,7 @@ static int txn_begin(namedb_t *db, namedb_txn_t *txn, unsigned flags)
 	return KNOT_EOK;
 }
 
-static int txn_commit(namedb_txn_t *txn)
+static int txn_commit(knot_db_txn_t *txn)
 {
 	freelist_t *freelist = txn->txn;
 	if (freelist) {
@@ -86,7 +86,7 @@ static int txn_commit(namedb_txn_t *txn)
 	return KNOT_EOK;
 }
 
-static void txn_abort(namedb_txn_t *txn)
+static void txn_abort(knot_db_txn_t *txn)
 {
 	/** @warning No real transactions here,
 	  *          all the reads/writes are done synchronously.
@@ -96,7 +96,7 @@ static void txn_abort(namedb_txn_t *txn)
 	txn_commit(txn);
 }
 
-static int count(namedb_txn_t *txn)
+static int count(knot_db_txn_t *txn)
 {
 	memcached_return_t error = 0;
 	memcached_stat_st *stats = memcached_stat(txn->db, NULL, &error);
@@ -108,7 +108,7 @@ static int count(namedb_txn_t *txn)
 	return ret;
 }
 
-static int clear(namedb_txn_t *txn)
+static int clear(knot_db_txn_t *txn)
 {
 	memcached_return_t ret = memcached_flush(txn->db, 0);
 	if (ret != 0) {
@@ -117,7 +117,7 @@ static int clear(namedb_txn_t *txn)
 	return KNOT_EOK;
 }
 
-static int find(namedb_txn_t *txn, namedb_val_t *key, namedb_val_t *val, unsigned flags)
+static int find(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags)
 {
 	uint32_t mc_flags = 0;
 	memcached_return_t error = 0;
@@ -134,7 +134,7 @@ static int find(namedb_txn_t *txn, namedb_val_t *key, namedb_val_t *val, unsigne
 	return KNOT_EOK;
 }
 
-static int insert(namedb_txn_t *txn, namedb_val_t *key, namedb_val_t *val, unsigned flags)
+static int insert(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags)
 {
 	if (!txn || !key || !val) {
 		return KNOT_EINVAL;
@@ -150,7 +150,7 @@ static int insert(namedb_txn_t *txn, namedb_val_t *key, namedb_val_t *val, unsig
 	return KNOT_EOK;
 }
 
-static int del(namedb_txn_t *txn, namedb_val_t *key)
+static int del(knot_db_txn_t *txn, knot_db_val_t *key)
 {
 	memcached_return_t ret = memcached_delete(txn->db, key->data, key->len, 0);
 	if (ret != 0) {
@@ -159,43 +159,43 @@ static int del(namedb_txn_t *txn, namedb_val_t *key)
 	return KNOT_EOK;
 }
 
-static namedb_iter_t *iter_begin(namedb_txn_t *txn, unsigned flags)
+static knot_db_iter_t *iter_begin(knot_db_txn_t *txn, unsigned flags)
 {
 	/* Iteration is not supported, pruning should be
 	 * left on the memcached server */
 	return NULL;
 }
 
-static namedb_iter_t *iter_seek(namedb_iter_t *iter, namedb_val_t *key, unsigned flags)
+static knot_db_iter_t *iter_seek(knot_db_iter_t *iter, knot_db_val_t *key, unsigned flags)
 {
 	assert(0);
 	return NULL; /* ENOTSUP */
 }
 
-static namedb_iter_t *iter_next(namedb_iter_t *iter)
+static knot_db_iter_t *iter_next(knot_db_iter_t *iter)
 {
 	assert(0);
 	return NULL;
 }
 
-static int iter_key(namedb_iter_t *iter, namedb_val_t *val)
+static int iter_key(knot_db_iter_t *iter, knot_db_val_t *val)
 {
 	return KNOT_ENOTSUP;
 }
 
-static int iter_val(namedb_iter_t *iter, namedb_val_t *val)
+static int iter_val(knot_db_iter_t *iter, knot_db_val_t *val)
 {
 	return KNOT_ENOTSUP;
 }
 
-static void iter_finish(namedb_iter_t *iter)
+static void iter_finish(knot_db_iter_t *iter)
 {
 	assert(0);
 }
 
-const namedb_api_t *namedb_memcached_api(void)
+const knot_db_api_t *namedb_memcached_api(void)
 {
-	static const namedb_api_t api = {
+	static const knot_db_api_t api = {
 		"memcached",
 		init, deinit,
 		txn_begin, txn_commit, txn_abort,
diff --git a/modules/policy/policy.lua b/modules/policy/policy.lua
index 8fcc089fbfd855c0813aa1516c7627e73caabc1e..57ae53ee6a2737d0d72e59532598b0982ad9666f 100644
--- a/modules/policy/policy.lua
+++ b/modules/policy/policy.lua
@@ -81,15 +81,13 @@ local function rpz_parse(action, path)
 		['\012rpz-tcp-only\0'] = policy.TC,
 		-- Policy triggers @NYI@
 	}
-	local parser = require('zonefile').parser(function (p)
-		local name = ffi.string(p.r_owner, p.r_owner_length)
-		local action = ffi.string(p.r_data, p.r_data_length)
+	local parser = require('zonefile').new()
+	if not parser:open(path) then error(string.format('failed to parse "%s"', path)) end
+	while parser:parse() do
+		local name = ffi.string(parser.r_owner, parser.r_owner_length)
+		local action = ffi.string(parser.r_data, parser.r_data_length)
 		rules[name] = action_map[action]
-	end, function (p)
-		print(string.format('[policy.rpz] %s: line %d: %s', path,
-			tonumber(p.line_counter), p:last_error()))
-	end)
-	parser:parse_file(path)
+	end
 	return rules
 end
 
diff --git a/modules/policy/zonefile.lua b/modules/policy/zonefile.lua
index 0c090d5f383dab9ef0a04ec9e73aa3678d2e23ef..c2f6fcac385e2889ec5d6173b9487940bbb1818c 100644
--- a/modules/policy/zonefile.lua
+++ b/modules/policy/zonefile.lua
@@ -3,8 +3,11 @@
 --
 
 local ffi = require('ffi')
-local libzscanner = ffi.load(libpath('libzscanner', '0'))
+local utils = require('kdns.utils')
+local libzscanner = ffi.load(libpath('libzscanner', '1'))
 ffi.cdef[[
+void free(void *ptr);
+void *realloc(void *ptr, size_t size);
 
 /*
  * Data structures
@@ -38,9 +41,16 @@ typedef struct {
 	uint64_t siz, hp, vp;
 	int8_t   lat_sign, long_sign, alt_sign;
 } loc_t;
+typedef struct zs_state {
+	static const int NONE    = 0;
+	static const int DATA    = 1;
+	static const int ERROR   = 2;
+	static const int INCLUDE = 3;
+	static const int EOF     = 4;
+	static const int STOP    = 5;
+} zs_state_t;
 
-typedef struct scanner zs_scanner_t;
-struct scanner {
+typedef struct scanner {
 	int      cs;
 	int      top;
 	int      stack[RAGEL_STACK_SIZE];
@@ -54,7 +64,8 @@ struct scanner {
 	uint8_t *item_length_location;
 	uint32_t buffer_length;
 	uint8_t  buffer[MAX_RDATA_LENGTH];
-	char     include_filename[MAX_RDATA_LENGTH + 1];
+	char     include_filename[MAX_RDATA_LENGTH];
+	char     *path;
 	window_t windows[BITMAP_WINDOWS];
 	int16_t  last_window;
 	apl_t    apl;
@@ -68,18 +79,29 @@ struct scanner {
 	uint8_t  zone_origin[MAX_DNAME_LENGTH + MAX_LABEL_LENGTH];
 	uint16_t default_class;
 	uint32_t default_ttl;
-	void (*process_record)(zs_scanner_t *);
-	void (*process_error)(zs_scanner_t *);
-	void *data;
-	char     *path;
-	uint64_t line_counter;
-	int      error_code;
-	uint64_t error_counter;
-	bool     stop;
+	int state;
+	struct {
+		bool automatic;
+		void (*record)(struct zs_scanner *);
+		void (*error)(struct zs_scanner *);
+		void *data;
+	} process;
+	struct {
+		const char *start;
+		const char *current;
+		const char *end;
+		bool eof;
+	} input;
 	struct {
 		char *name;
 		int  descriptor;
 	} file;
+	struct {
+		int code;
+		uint64_t counter;
+		bool fatal;
+	} error;
+	uint64_t line_counter;
 	uint32_t r_owner_length;
 	uint8_t  r_owner[MAX_DNAME_LENGTH + MAX_LABEL_LENGTH];
 	uint16_t r_class;
@@ -87,72 +109,115 @@ struct scanner {
 	uint16_t r_type;
 	uint32_t r_data_length;
 	uint8_t  r_data[MAX_RDATA_LENGTH];
-};
+} zs_scanner_t;
 
 /*
  * Function signatures
  */
-
-zs_scanner_t* zs_scanner_create(const char     *origin,
-                                const uint16_t rclass,
-                                const uint32_t ttl,
-                                void (*process_record)(zs_scanner_t *),
-                                void (*process_error)(zs_scanner_t *),
-                                void *data);
-void zs_scanner_free(zs_scanner_t *scanner);
-int zs_scanner_parse(zs_scanner_t *scanner,
-                     const char   *start,
-                     const char   *end,
-                     const bool   final_block);
-int zs_scanner_parse_file(zs_scanner_t *scanner,
-                          const char   *file_name);
+int zs_init(zs_scanner_t *scanner, const char *origin, const uint16_t rclass, const uint32_t ttl);
+void zs_deinit(zs_scanner_t *scanner);
+int zs_set_input_string(zs_scanner_t *scanner, const char *input, size_t size);
+int zs_set_input_file(zs_scanner_t *scanner, const char *file_name);
+int zs_parse_record(zs_scanner_t *scanner);
 const char* zs_strerror(const int code);
 ]]
 
+-- Constant table
+local zs_state = ffi.new('struct zs_state')
+
+-- Sorted set of records
+local bsearch = utils.bsearch
+sortedset_t = ffi.typeof('struct { knot_rrset_t *at; int32_t len; int32_t cap; }')
+ffi.metatype(sortedset_t, {
+	__gc = function (set)
+		for i = 0, tonumber(set.len) - 1 do set.at[i]:init(nil, 0) end
+		ffi.C.free(set.at)
+	end,
+	__len = function (set) return tonumber(set.len) end,
+	__index = {
+		newrr = function (set, noinit)
+			-- Reserve enough memory in buffer
+			if set.cap == set.len then assert(utils.buffer_grow(set)) end
+			local nid = set.len
+			set.len = nid + 1
+			-- Don't initialize if caller is going to do it immediately
+			if not noinit then
+				ffi.fill(set.at + nid, ffi.sizeof(set.at[0]))
+			end
+			return set.at[nid]
+		end,
+		sort = function (set)
+			-- Prefetch RR set comparison function
+			return utils.sort(set.at, tonumber(set.len))
+		end,
+		search = function (set, owner)
+			return bsearch(set.at, tonumber(set.len), owner)
+		end,
+		searcher = function (set)
+			return utils.bsearcher(set.at, tonumber(set.len))
+		end,
+	}
+})
+
 -- Wrap scanner context
-local zs_scanner_t = ffi.typeof('zs_scanner_t')
+local const_char_t = ffi.typeof('const char *')
+local zs_scanner_t = ffi.typeof('struct scanner')
 ffi.metatype( zs_scanner_t, {
-	__new = function(zs, on_record, on_error)
-		return ffi.gc(libzscanner.zs_scanner_create('.', 1, 3600, on_record, on_error, nil),
-		              libzscanner.zs_scanner_free)
+	__gc = function(zs) return libzscanner.zs_deinit(zs) end,
+	__new = function(ct, origin, class, ttl)
+		if not class then class = 1 end
+		if not ttl then ttl = 3600 end
+		local parser = ffi.new(ct)
+		libzscanner.zs_init(parser, origin, class, ttl)
+		return parser
 	end,
 	__index = {
-		parse_file = function(zs, file)
-			return libzscanner.zs_scanner_parse_file(zs, file)
+		open = function (zs, file)
+			assert(ffi.istype(zs, zs_scanner_t))
+			local ret = libzscanner.zs_set_input_file(zs, file)
+			if ret ~= 0 then return false, zs:strerr() end
+			return true
 		end,
-		read = function(zs, str)
-			local buf = ffi.cast(ffi.typeof('const char *'), str)
-			return libzscanner.zs_scanner_parse(zs, buf, buf + #str, false)
+		parse = function(zs, input)
+			assert(ffi.istype(zs, zs_scanner_t))
+			if input ~= nil then libzscanner.zs_set_input_string(zs, input, #input) end
+			local ret = libzscanner.zs_parse_record(zs)
+			-- Return current state only when parsed correctly, otherwise return error
+			if ret == 0 and zs.state ~= zs_state.ERROR then
+				return zs.state == zs_state.DATA
+			else
+				return false, zs:strerr()
+			end
 		end,
 		current_rr = function(zs)
+			assert(ffi.istype(zs, zs_scanner_t))
 			return {owner = ffi.string(zs.r_owner, zs.r_owner_length),
 			        ttl = tonumber(zs.r_ttl),
 			        class = tonumber(zs.r_class),
 			        type = tonumber(zs.r_type), 
 			        rdata = ffi.string(zs.r_data, zs.r_data_length)}
 		end,
-		last_error = function(zs)
-			return ffi.string(libzscanner.zs_strerror(zs.error_code))
+		strerr = function(zs)
+			assert(ffi.istype(zs, zs_scanner_t))
+			return ffi.string(libzscanner.zs_strerror(zs.error.code))
 		end,
 	},
 })
 
 -- Module API
-local zonefile = {}
-
-function zonefile.parser(on_record, on_error)
-	return zs_scanner_t(on_record, on_error)
-end
-
-function zonefile.parse_file(file)
-	local records = {}
-	local context = zonefile.parser(function (parser)
-		table.insert(records, parser:current_rr())
-	end)
-	if context:parse_file(file) ~= 0 then
-		return nil
-	end
-	return records
-end
-
-return zonefile
+local rrparser = {
+	new = zs_scanner_t,
+	file = function (path)
+		local zs = zs_scanner_t()
+		local ok, err = zs:open(path)
+		if not ok then error(err) end
+		local results = {}
+		while zs:parse() do
+			table.insert(results, zs:current_rr())
+		end
+		return results
+	end,
+	state = zs_state,
+	set = sortedset_t,
+}
+return rrparser
diff --git a/modules/redis/namedb_redis.c b/modules/redis/namedb_redis.c
index 6f95fdc7e3c2720cd4a4686408f7d689a94d620e..db10c5d517c2a79ebd77173c8180eb2c9d23f69d 100644
--- a/modules/redis/namedb_redis.c
+++ b/modules/redis/namedb_redis.c
@@ -22,7 +22,7 @@
 
 #include <assert.h>
 #include <string.h>
-#include <libknot/internal/namedb/namedb.h>
+#include <libknot/db/db.h>
 
 #include "modules/redis/redis.h"
 
@@ -79,7 +79,7 @@ static void cli_free(struct redis_cli *cli)
 	free(cli);
 }
 
-static int init(namedb_t **db, mm_ctx_t *mm, void *arg)
+static int init(knot_db_t **db, knot_mm_t *mm, void *arg)
 {
 	if (!db || !arg) {
 		return kr_error(EINVAL);
@@ -99,13 +99,13 @@ static int init(namedb_t **db, mm_ctx_t *mm, void *arg)
 	return ret;
 }
 
-static void deinit(namedb_t *db)
+static void deinit(knot_db_t *db)
 {
 	struct redis_cli *cli = db;
 	cli_free(cli);
 }
 
-static int txn_begin(namedb_t *db, namedb_txn_t *txn, unsigned flags)
+static int txn_begin(knot_db_t *db, knot_db_txn_t *txn, unsigned flags)
 {
 	if (!db || !txn) {
 		return kr_error(EINVAL);
@@ -114,7 +114,7 @@ static int txn_begin(namedb_t *db, namedb_txn_t *txn, unsigned flags)
 	return kr_ok();
 }
 
-static int txn_commit(namedb_txn_t *txn)
+static int txn_commit(knot_db_txn_t *txn)
 {
 	if (!txn || !txn->db) {
 		return kr_error(EINVAL);
@@ -124,7 +124,7 @@ static int txn_commit(namedb_txn_t *txn)
 	return kr_ok();
 }
 
-static void txn_abort(namedb_txn_t *txn)
+static void txn_abort(knot_db_txn_t *txn)
 {
 	/** @warning No real transactions here. */
 	txn_commit(txn);
@@ -145,7 +145,7 @@ static void txn_abort(namedb_txn_t *txn)
 		} \
 	}
 
-static int count(namedb_txn_t *txn)
+static int count(knot_db_txn_t *txn)
 {
 	if (!txn || !txn->db) {
 		return kr_error(EINVAL);
@@ -165,7 +165,7 @@ static int count(namedb_txn_t *txn)
 	return ret;
 }
 
-static int clear(namedb_txn_t *txn)
+static int clear(knot_db_txn_t *txn)
 {
 	if (!txn || !txn->db) {
 		return kr_error(EINVAL);
@@ -181,7 +181,7 @@ static int clear(namedb_txn_t *txn)
 	return kr_ok();
 }
 
-static int find(namedb_txn_t *txn, namedb_val_t *key, namedb_val_t *val, unsigned flags)
+static int find(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags)
 {
 	if (!txn || !key || !val) {
 		return kr_error(EINVAL);
@@ -207,7 +207,7 @@ static int find(namedb_txn_t *txn, namedb_val_t *key, namedb_val_t *val, unsigne
 	return kr_ok();
 }
 
-static int insert(namedb_txn_t *txn, namedb_val_t *key, namedb_val_t *val, unsigned flags)
+static int insert(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags)
 {
 	if (!txn || !key || !val) {
 		return kr_error(EINVAL);
@@ -228,48 +228,48 @@ static int insert(namedb_txn_t *txn, namedb_val_t *key, namedb_val_t *val, unsig
 	return kr_ok();
 }
 
-static int del(namedb_txn_t *txn, namedb_val_t *key)
+static int del(knot_db_txn_t *txn, knot_db_val_t *key)
 {
 	return kr_error(ENOSYS);
 }
 
-static namedb_iter_t *iter_begin(namedb_txn_t *txn, unsigned flags)
+static knot_db_iter_t *iter_begin(knot_db_txn_t *txn, unsigned flags)
 {
 	/* Iteration is not supported, pruning should be
 	 * left on the Redis server setting */
 	return NULL;
 }
 
-static namedb_iter_t *iter_seek(namedb_iter_t *iter, namedb_val_t *key, unsigned flags)
+static knot_db_iter_t *iter_seek(knot_db_iter_t *iter, knot_db_val_t *key, unsigned flags)
 {
 	assert(0);
 	return NULL; /* ENOSYS */
 }
 
-static namedb_iter_t *iter_next(namedb_iter_t *iter)
+static knot_db_iter_t *iter_next(knot_db_iter_t *iter)
 {
 	assert(0);
 	return NULL;
 }
 
-static int iter_key(namedb_iter_t *iter, namedb_val_t *val)
+static int iter_key(knot_db_iter_t *iter, knot_db_val_t *val)
 {
 	return kr_error(ENOSYS);
 }
 
-static int iter_val(namedb_iter_t *iter, namedb_val_t *val)
+static int iter_val(knot_db_iter_t *iter, knot_db_val_t *val)
 {
 	return kr_error(ENOSYS);
 }
 
-static void iter_finish(namedb_iter_t *iter)
+static void iter_finish(knot_db_iter_t *iter)
 {
 	assert(0);
 }
 
-const namedb_api_t *namedb_redis_api(void)
+const knot_db_api_t *namedb_redis_api(void)
 {
-	static const namedb_api_t api = {
+	static const knot_db_api_t api = {
 		"redis",
 		init, deinit,
 		txn_begin, txn_commit, txn_abort,
diff --git a/modules/redis/redis.c b/modules/redis/redis.c
index c38cbc5ccf633fba34a91aa44f72af3736962778..79f7c66cbe36e8fb855d3bedaf582890a648f771 100644
--- a/modules/redis/redis.c
+++ b/modules/redis/redis.c
@@ -14,7 +14,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <libknot/internal/namedb/namedb.h>
+#include <libknot/db/db.h>
 #include <contrib/cleanup.h>
 #include <uv.h>
 
@@ -24,7 +24,7 @@
 #include "lib/cache.h"
 
 /** @internal Redis API */
-extern const namedb_api_t *namedb_redis_api(void);
+extern const knot_db_api_t *namedb_redis_api(void);
 
 /** @internal Make redis options. */
 void *namedb_redis_mkopts(const char *conf_, size_t maxsize)
diff --git a/modules/stats/stats.c b/modules/stats/stats.c
index 2b3e6219e7a4f25747a18cddbd3c0793a6528184..884098da2b9da98c4bf9a57df32698feb8551313 100644
--- a/modules/stats/stats.c
+++ b/modules/stats/stats.c
@@ -115,9 +115,9 @@ static void collect_sample(struct stat_data *data, struct kr_rplan *rplan, knot_
 {
 	/* Sample key = {[2] type, [1-255] owner} */
 	char key[sizeof(uint16_t) + KNOT_DNAME_MAXLEN];
-	struct kr_query *qry = NULL;
-	WALK_LIST(qry, rplan->resolved) {
+	for (size_t i = 0; i < rplan->resolved.len; ++i) {
 		/* Sample queries leading to iteration or expiring */
+		struct kr_query *qry = rplan->resolved.at[i];
 		if ((qry->flags & QUERY_CACHED) && !(qry->flags & QUERY_EXPIRING)) {
 			continue;
 		}
@@ -146,10 +146,10 @@ static int collect(knot_layer_t *ctx)
 	collect_answer(data, param->answer);
 	collect_sample(data, rplan, param->answer);
 	/* Count cached and unresolved */
-	if (!EMPTY_LIST(rplan->resolved)) {
+	if (rplan->resolved.len > 0) {
 		/* Histogram of answer latency. */
-		struct kr_query *first = HEAD(rplan->resolved);
-		struct kr_query *last = TAIL(rplan->resolved);
+		struct kr_query *first = rplan->resolved.at[0];
+		struct kr_query *last = array_tail(rplan->resolved);
 		struct timeval now;
 		gettimeofday(&now, NULL);
 		long elapsed = time_diff(&first->timestamp, &now);
diff --git a/modules/view/view.lua b/modules/view/view.lua
index 1efd5fbcd0569e39208c8c6c447c1b2f34f4fda9..2550112d0db77f0df5018fe15622450afc9f7879 100644
--- a/modules/view/view.lua
+++ b/modules/view/view.lua
@@ -2,11 +2,6 @@ local kres = require('kres')
 local policy = require('policy')
 local ffi = require('ffi')
 local C = ffi.C
-ffi.cdef[[
-int kr_straddr_family(const char *addr);
-int kr_straddr_subnet(void *dst, const char *addr);
-int kr_bitcmp(const char *a, const char *b, int bits);
-]]
 
 -- Module declaration
 local view = {
@@ -29,7 +24,7 @@ end
 
 -- @function Match IP against given subnet
 local function match_subnet(family, subnet, bitlen, addr)
-	return (family == addr.sa_family) and (C.kr_bitcmp(subnet, addr:ip(), bitlen) == 0)
+	return (family == addr:family()) and (C.kr_bitcmp(subnet, addr:ip(), bitlen) == 0)
 end
 
 -- @function Find view for given request
@@ -64,4 +59,4 @@ view.layer = {
 	end
 }
 
-return view
\ No newline at end of file
+return view
diff --git a/scripts/bootstrap-depends.sh b/scripts/bootstrap-depends.sh
index 3631130a27e07fffdcab2104b4ffb557d09742f3..6d7a90fc5eff23962f47ad00d03501425cca3877 100755
--- a/scripts/bootstrap-depends.sh
+++ b/scripts/bootstrap-depends.sh
@@ -1,11 +1,11 @@
 #!/bin/bash  
-set -e
+#set -e
 
 CMOCKA_TAG="cmocka-0.4.1"
 CMOCKA_URL="git://git.cryptomilk.org/projects/cmocka.git"
 LIBUV_TAG="v1.x"
 LIBUV_URL="https://github.com/libuv/libuv.git"
-KNOT_TAG="c1353efc"
+KNOT_TAG="v2.1.0-rc1"
 KNOT_URL="https://github.com/CZ-NIC/knot.git"
 GMP_TAG="6.0.0"
 GMP_URL="https://gmplib.org/download/gmp/gmp-${GMP_TAG}.tar.xz"
@@ -15,8 +15,8 @@ NETTLE_TAG="2.7.1"
 NETTLE_URL="https://ftp.gnu.org/gnu/nettle/nettle-${NETTLE_TAG}.tar.gz"
 GNUTLS_TAG="3.3.12"
 GNUTLS_URL="ftp://ftp.gnutls.org/gcrypt/gnutls/v3.3/gnutls-${GNUTLS_TAG}.tar.xz"
-LUA_TAG="v2.1"
-LUA_URL="http://luajit.org/git/luajit-2.0.git"
+LUA_TAG="2.1.0-beta1"
+LUA_URL="http://luajit.org/download/LuaJIT-${LUA_TAG}.tar.gz"
 HIREDIS_TAG="v0.13.3"
 HIREDIS_URL="https://github.com/redis/hiredis.git"
 LIBMEMCACHED_TAG="1.0.18"
@@ -109,7 +109,8 @@ pkg libknot ${KNOT_URL} ${KNOT_TAG} include/libknot \
 	--disable-static --with-lmdb=no --disable-fastparser --disable-daemon --disable-utilities --disable-documentation
 pkg cmocka ${CMOCKA_URL} ${CMOCKA_TAG} include/cmocka.h
 pkg libuv ${LIBUV_URL} ${LIBUV_TAG} include/uv.h --disable-static
-pkg lua ${LUA_URL} ${LUA_TAG} lib/pkgconfig/luajit.pc amalg install BUILDMODE=dynamic LDFLAGS=-lm PREFIX=${PREFIX}
+pkg lua ${LUA_URL} ${LUA_TAG} lib/pkgconfig/luajit.pc install BUILDMODE=dynamic LDFLAGS=-lm PREFIX=${PREFIX}
+cat build.log
 
 # remove on successful build
 rm -rf ${BUILD_DIR}
diff --git a/tests/deckard b/tests/deckard
index 9d65d378b0b5e81b692ffe088a0a282f086a9efc..2e2feeb910f2092f4e24413405bb8f7e90fbb5d2 160000
--- a/tests/deckard
+++ b/tests/deckard
@@ -1 +1 @@
-Subproject commit 9d65d378b0b5e81b692ffe088a0a282f086a9efc
+Subproject commit 2e2feeb910f2092f4e24413405bb8f7e90fbb5d2
diff --git a/tests/test.h b/tests/test.h
index ceb43e0f95784eb06415b7c7113c171605f7cb59..b0e9c2ce49316a1b257f406a33180c329cea61b6 100644
--- a/tests/test.h
+++ b/tests/test.h
@@ -31,7 +31,7 @@
 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 
 #include "lib/defines.h"
-#include <libknot/internal/mempattern.h>
+#include "lib/utils.h"
 #include <libknot/descriptor.h>
 #include <libknot/rrset.h>
 
@@ -42,7 +42,7 @@ static inline void mm_test_free(void *p)
 { if (p) test_free(p); }
 
 /** Memory context using CMocka allocator. */
-static inline void test_mm_ctx_init(mm_ctx_t *mm)
+static inline void test_mm_ctx_init(knot_mm_t *mm)
 {
 	mm->alloc = &mm_test_malloc;
 	mm->free = &mm_test_free;
diff --git a/tests/test_array.c b/tests/test_array.c
index decac5b2c088205236e4a9d594bea3977dec65c7..d8ed5e3fc8219e74dd0ecf99ec8639a766a8e11d 100644
--- a/tests/test_array.c
+++ b/tests/test_array.c
@@ -17,7 +17,7 @@
 #include "tests/test.h"
 #include "lib/generic/array.h"
 
-mm_ctx_t global_mm;
+knot_mm_t global_mm;
 
 static void test_array(void **state)
 {
diff --git a/tests/test_cache.c b/tests/test_cache.c
index 3bf0c41a56cd230a9071b720e0e998224928c086..19c621bf733d15b784a0019c73c1eee2aa301232 100644
--- a/tests/test_cache.c
+++ b/tests/test_cache.c
@@ -14,7 +14,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <libknot/internal/namedb/namedb_lmdb.h>
+#include <libknot/db/db_lmdb.h>
 #include <ucw/mempool.h>
 
 #include "tests/test.h"
@@ -24,7 +24,7 @@
 #include <time.h>
 #include <dlfcn.h>
 
-mm_ctx_t global_mm;
+knot_mm_t global_mm;
 struct kr_cache_txn global_txn;
 knot_rrset_t global_rr;
 const char *global_env;
@@ -33,32 +33,15 @@ struct kr_cache_entry global_fake_ce;
 #define NAMEDB_INTS 256
 #define NAMEDB_DATA_SIZE (NAMEDB_INTS * sizeof(int))
 uint8_t namedb_data[NAMEDB_DATA_SIZE];
-namedb_val_t global_namedb_data = {namedb_data, NAMEDB_DATA_SIZE};
-bool is_malloc_mocked = false;
+knot_db_val_t global_namedb_data = {namedb_data, NAMEDB_DATA_SIZE};
 
 #define CACHE_SIZE 10 * 4096
 #define CACHE_TTL 10
 #define CACHE_TIME 0
 
-void * (*original_malloc) (size_t __size);
-int (*original_knot_rdataset_add)(knot_rdataset_t *rrs, const knot_rdata_t *rr, mm_ctx_t *mm) = NULL;
+int (*original_knot_rdataset_add)(knot_rdataset_t *rrs, const knot_rdata_t *rr, knot_mm_t *mm) = NULL;
 
-void *malloc(size_t __size)
-{
-	int err_mock = KNOT_EOK;
-	if (original_malloc == NULL)
-	{
-		original_malloc = dlsym(RTLD_NEXT,"malloc");
-		assert_non_null (malloc);
-	}
-	if (is_malloc_mocked)
-	{
-		err_mock = mock();
-	}
-	return (err_mock != KNOT_EOK) ? NULL : original_malloc (__size);
-}
-
-int knot_rdataset_add(knot_rdataset_t *rrs, const knot_rdata_t *rr, mm_ctx_t *mm)
+int knot_rdataset_add(knot_rdataset_t *rrs, const knot_rdata_t *rr, knot_mm_t *mm)
 {
 	int err, err_mock;
 	err_mock = (int)mock();
@@ -74,39 +57,39 @@ int knot_rdataset_add(knot_rdataset_t *rrs, const knot_rdata_t *rr, mm_ctx_t *mm
 }
 
 /* Simulate init failure */
-static int fake_test_init(namedb_t **db_ptr, mm_ctx_t *mm, void *arg)
+static int fake_test_init(knot_db_t **db_ptr, knot_mm_t *mm, void *arg)
 {
 	static char db[1024];
 	*db_ptr = db;
 	return mock();
 }
 
-static void fake_test_deinit(namedb_t *db)
+static void fake_test_deinit(knot_db_t *db)
 {
     return;
 }
 
 /* Simulate commit failure */
-static int fake_test_commit(namedb_txn_t *txn)
+static int fake_test_commit(knot_db_txn_t *txn)
 {
 	return KNOT_ESPACE;
 }
 
 /* Dummy abort */
-static void fake_test_abort(namedb_txn_t *txn)
+static void fake_test_abort(knot_db_txn_t *txn)
 {
 	return;
 }
 
 /* Stub for find */
-static int fake_test_find(namedb_txn_t *txn, namedb_val_t *key, namedb_val_t *val, unsigned flags)
+static int fake_test_find(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags)
 {
 	val->data = &global_fake_ce;
 	return KNOT_EOK;
 }
 
 /* Stub for insert */
-static int fake_test_ins(namedb_txn_t *txn, namedb_val_t *key, namedb_val_t *val, unsigned flags)
+static int fake_test_ins(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags)
 {
 	struct kr_cache_entry *header = val->data;
 	int  res_cmp, err = (int)mock();
@@ -125,15 +108,15 @@ static int fake_test_ins(namedb_txn_t *txn, namedb_val_t *key, namedb_val_t *val
 	return err;
 }
 
-static int fake_test_txn_begin(namedb_t *db, namedb_txn_t *txn, unsigned flags)
+static int fake_test_txn_begin(knot_db_t *db, knot_db_txn_t *txn, unsigned flags)
 {
     return KNOT_EOK;
 }
 
 /* Fake api */
-static namedb_api_t *fake_namedb_lmdb_api(void)
+static knot_db_api_t *fake_knot_db_lmdb_api(void)
 {
-	static namedb_api_t fake_api = {
+	static knot_db_api_t fake_api = {
 		"lmdb_fake_api",
 		fake_test_init, fake_test_deinit,
 		fake_test_txn_begin, fake_test_commit, fake_test_abort,
@@ -145,10 +128,10 @@ static namedb_api_t *fake_namedb_lmdb_api(void)
 }
 
 /* Test cache open */
-static int test_open(void **state, namedb_api_t *api)
+static int test_open(void **state, knot_db_api_t *api)
 {
 	static struct kr_cache cache;
-	struct namedb_lmdb_opts opts;
+	struct knot_db_lmdb_opts opts;
 	memset(&cache, 0, sizeof(cache));
 	memset(&opts, 0, sizeof(opts));
 	opts.path = global_env;
@@ -162,10 +145,10 @@ static void test_open_fake_api(void **state)
 {
 	bool res;
 	will_return(fake_test_init,KNOT_EINVAL);
-	assert_int_equal(test_open(state, fake_namedb_lmdb_api()),KNOT_EINVAL);
+	assert_int_equal(test_open(state, fake_knot_db_lmdb_api()),KNOT_EINVAL);
 	will_return(fake_test_init,KNOT_EOK);
-	assert_int_equal(test_open(state, fake_namedb_lmdb_api()),KNOT_EOK);
-	res = (((struct kr_cache *)(*state))->api == fake_namedb_lmdb_api());
+	assert_int_equal(test_open(state, fake_knot_db_lmdb_api()),KNOT_EOK);
+	res = (((struct kr_cache *)(*state))->api == fake_knot_db_lmdb_api());
 	assert_true(res);
 }
 
@@ -173,7 +156,7 @@ static void test_open_conventional_api(void **state)
 {
 	bool res;
 	assert_int_equal(test_open(state, NULL),KNOT_EOK);
-	res = (((struct kr_cache *)(*state))->api == namedb_lmdb_api());
+	res = (((struct kr_cache *)(*state))->api == knot_db_lmdb_api());
 	assert_true(res);
 }
 
@@ -197,7 +180,7 @@ static struct kr_cache_txn *test_txn_write(void **state)
 static struct kr_cache_txn *test_txn_rdonly(void **state)
 {
 	assert_non_null(*state);
-	assert_int_equal(kr_cache_txn_begin(*state, &global_txn, NAMEDB_RDONLY), 0);
+	assert_int_equal(kr_cache_txn_begin(*state, &global_txn, KNOT_DB_RDONLY), 0);
 	return &global_txn;
 }
 
@@ -205,7 +188,7 @@ static struct kr_cache_txn *test_txn_rdonly(void **state)
 static void test_fake_invalid (void **state)
 {
 	struct kr_cache_txn *txn = NULL;
-	const namedb_api_t *api_saved = NULL;
+	const knot_db_api_t *api_saved = NULL;
 	knot_dname_t dname[] = "";
 	struct kr_cache_entry *entry = NULL;
 	int ret = 0;
@@ -224,24 +207,18 @@ static void test_fake_invalid (void **state)
 
 static void test_fake_insert(void **state)
 {
-	int ret_cache_ins_ok, ret_cache_lowmem, ret_cache_ins_inval;
+	int ret_cache_ins_ok, ret_cache_ins_inval;
 	knot_dname_t dname[] = "";
 	struct kr_cache_txn *txn = test_txn_write(state);
 	test_randstr((char *)&global_fake_ce,sizeof(global_fake_ce));
 	test_randstr((char *)namedb_data,NAMEDB_DATA_SIZE);
 
-	will_return(malloc,KNOT_EINVAL);
-	is_malloc_mocked = true;
-	ret_cache_lowmem = kr_cache_insert(txn, KR_CACHE_USER, dname,
-		KNOT_RRTYPE_TSIG, &global_fake_ce, global_namedb_data);
-	is_malloc_mocked = false;
 	will_return(fake_test_ins,KNOT_EOK);
 	ret_cache_ins_ok = kr_cache_insert(txn, KR_CACHE_USER, dname,
 		KNOT_RRTYPE_TSIG, &global_fake_ce, global_namedb_data);
 	will_return(fake_test_ins,KNOT_EINVAL);
 	ret_cache_ins_inval = kr_cache_insert(txn, KR_CACHE_USER, dname,
 		KNOT_RRTYPE_TSIG, &global_fake_ce, global_namedb_data);
-	assert_int_equal(ret_cache_lowmem, KNOT_ENOMEM);
 	assert_int_equal(ret_cache_ins_ok, KNOT_EOK);
 	assert_int_equal(ret_cache_ins_inval, KNOT_EINVAL);
 }
@@ -251,7 +228,7 @@ static void test_invalid(void **state)
 {
 	knot_dname_t dname[] = "";
 	uint32_t timestamp = CACHE_TIME;
-	struct namedb_lmdb_opts opts;
+	struct knot_db_lmdb_opts opts;
 	struct kr_cache_entry *entry = NULL;
 
 	memset(&opts, 0, sizeof(opts));
diff --git a/tests/test_pack.c b/tests/test_pack.c
index c2a470b4a46719adeb04fcdaada5c9dd5c2f957b..b55c15cc2ea12ed2095273abbb8000eea4f0ab32 100644
--- a/tests/test_pack.c
+++ b/tests/test_pack.c
@@ -18,7 +18,7 @@
 #include "lib/generic/pack.h"
 
 #define U8(x) (const uint8_t *)(x)
-mm_ctx_t global_mm;
+knot_mm_t global_mm;
 
 static void test_pack_std(void **state)
 {
diff --git a/tests/test_rplan.c b/tests/test_rplan.c
index 53d22f92bd43e536fe1e10b24e4a488f9bb6b586..f05cdde21a263bcdac84b4ae8ccc410fbb7e31e6 100644
--- a/tests/test_rplan.c
+++ b/tests/test_rplan.c
@@ -40,7 +40,7 @@ static void test_rplan_params(void **state)
 
 static void test_rplan_push(void **state)
 {
-	mm_ctx_t mm;
+	knot_mm_t mm;
 	test_mm_ctx_init(&mm);
 	struct kr_request request = {
 		.pool = mm,