diff --git a/daemon/engine.c b/daemon/engine.c
index 47de46590fc19b80deef3a2b16e7801f175c453d..b2e2bcd81aaa0c6b8b8c498a5412d8aeebe828dc 100644
--- a/daemon/engine.c
+++ b/daemon/engine.c
@@ -27,6 +27,7 @@
 #include "lib/nsrep.h"
 #include "lib/cache.h"
 #include "lib/defines.h"
+#include "lib/dnssec/ta.h"
 
 /** @internal Compatibility wrapper for Lua < 5.2 */
 #if LUA_VERSION_NUM < 502
@@ -243,6 +244,7 @@ void *namedb_lmdb_mkopts(const char *conf, size_t maxsize)
 static int init_resolver(struct engine *engine)
 {
 	/* Open resolution context */
+	engine->resolver.trust_anchors = map_make();
 	engine->resolver.pool = engine->pool;
 	engine->resolver.modules = &engine->modules;
 	/* Create OPT RR */
@@ -363,6 +365,7 @@ void engine_deinit(struct engine *engine)
 	}
 	array_clear(engine->modules);
 	array_clear(engine->storage_registry);
+	kr_ta_clear(&engine->resolver.trust_anchors);
 
 	if (engine->L) {
 		lua_close(engine->L);
@@ -402,6 +405,12 @@ int engine_cmd(struct engine *engine, const char *str)
 
 static int engine_loadconf(struct engine *engine)
 {
+	/* Use module path for including Lua scripts */
+	static const char l_paths[] = "package.path = package.path..';" PREFIX MODULEDIR "/?.lua'";
+	int ret = l_dobytecode(engine->L, l_paths, sizeof(l_paths) - 1, "");
+	if (ret != 0) {
+		lua_pop(engine->L, 1);
+	}
 	/* Init environment */
 	static const char sandbox_bytecode[] = {
 		#include "daemon/lua/sandbox.inc"
@@ -411,12 +420,6 @@ static int engine_loadconf(struct engine *engine)
 		lua_pop(engine->L, 1);
 		return kr_error(ENOEXEC);
 	}
-	/* Use module path for including Lua scripts */
-	int ret = engine_cmd(engine, "package.path = package.path..';" PREFIX MODULEDIR "/?.lua'");
-	if (ret > 0) {
-		lua_pop(engine->L, 1);
-	}
-
 	/* Load config file */
 	if(access("config", F_OK ) != -1 ) {
 		ret = l_dosandboxfile(engine->L, "config");
diff --git a/daemon/lua/kres.lua b/daemon/lua/kres.lua
index e548ad239802b014576e135e78b5646b7a4d9dc6..649224a1fdc9b5aefb4dd754abf9628c89718454 100644
--- a/daemon/lua/kres.lua
+++ b/daemon/lua/kres.lua
@@ -125,6 +125,16 @@ typedef struct {
 	uint8_t _stub[]; /* Do not touch */
 } knot_pkt_t;
 
+/* generics */
+typedef void *(*map_alloc_f)(void *, size_t);
+typedef void (*map_free_f)(void *baton, void *ptr);
+typedef struct {
+	void *root;
+	map_alloc_f malloc;
+	map_free_f free;
+	void *baton;
+} map_t;
+
 /* libkres */
 struct kr_query {
 	node_t _node;
@@ -141,16 +151,23 @@ struct kr_rplan {
 	uint8_t _stub[]; /* Do not touch */
 };
 struct kr_request {
-	struct kr_context *_ctx;
+	struct kr_context *ctx;
 	knot_pkt_t *answer;
-    struct {
-        const knot_rrset_t *key;
-        const struct sockaddr *addr;
-    } qsource;
+	struct {
+		const knot_rrset_t *key;
+		const struct sockaddr *addr;
+	} qsource;
 	uint32_t options;
 	int state;
 	uint8_t _stub[]; /* Do not touch */
 };
+struct kr_context
+{	
+	uint32_t options;
+	knot_rrset_t *opt_rr;
+	map_t trust_anchors;
+	uint8_t _stub[]; /* Do not touch */
+};
 
 /* libknot API
  */
@@ -172,6 +189,12 @@ struct kr_rplan *kr_resolve_plan(struct kr_request *request);
 /* Resolution plan */
 struct kr_query *kr_rplan_current(struct kr_rplan *rplan);
 /* Query */
+/* Trust anchors */
+knot_rrset_t *kr_ta_get(map_t *trust_anchors, const knot_dname_t *name);
+int kr_ta_add(map_t *trust_anchors, const knot_dname_t *name, uint16_t type,
+               uint32_t ttl, const uint8_t *rdata, uint16_t rdlen);
+int kr_ta_del(map_t *trust_anchors, const knot_dname_t *name);
+void kr_ta_clear(map_t *trust_anchors);
 /* Utils */
 unsigned kr_rand_uint(unsigned max);
 int kr_pkt_put(knot_pkt_t *pkt, const knot_dname_t *name, uint32_t ttl,
@@ -236,6 +259,33 @@ ffi.metatype( kr_request_t, {
 	},
 })
 
+-- Return DS/DNSKEY parser that adds keys to TA store
+local function ta_parser(store)
+	local parser = require('zonefile').parser(function (p)
+		C.kr_ta_add(store, p.r_owner, p.r_type, p.r_ttl, p.r_data, p.r_data_length)
+	end)
+	return parser
+end
+
+-- TA store management
+local trust_anchors = {
+	current_file = "",
+	is_auto = false,
+	store = ffi.cast('struct kr_context *', __engine).trust_anchors,
+	-- Load keys from a file
+	config = function (path)
+		ta_parser(trust_anchors.store):parse_file(path)
+		trust_anchors.current_file = path
+	end,
+	-- Add DS/DNSKEY record
+	add = function (ds) ta_parser(trust_anchors.store):read(ds..'\n') end,
+	clear = function() C.kr_ta_clear(trust_anchors.store) end,
+	-- Set/disable RFC5011 TA management
+	set_auto = function (enable)
+		error("not supported")
+	end,
+}
+
 -- Module API
 local kres = {
 	-- Constants
@@ -248,6 +298,8 @@ local kres = {
 	pkt_t = function (udata) return ffi.cast('knot_pkt_t *', udata) end,
 	request_t = function (udata) return ffi.cast('struct kr_request *', udata) end,
 	-- Global API functions
+	context = function () return ffi.cast('struct kr_context *', __engine) end,
+	trust_anchors = trust_anchors,
 }
 
 return kres
\ No newline at end of file
diff --git a/daemon/lua/sandbox.lua b/daemon/lua/sandbox.lua
index 53559156ea1007b30c511a4cec6c239551a02266..a918bf75ec89bc7fbe6cddf87afc5b173dff9219 100644
--- a/daemon/lua/sandbox.lua
+++ b/daemon/lua/sandbox.lua
@@ -64,6 +64,16 @@ setmetatable(cache, {
 	end
 })
 
+-- Syntactic sugar for TA store
+trust_anchors = require('kres').trust_anchors
+setmetatable(trust_anchors, {
+	__newindex = function (t,k,v)
+	if     k == 'file' then t.config(v)
+	elseif k == 'auto' then t.set_auto(v)
+	else   rawset(t, k, v) end
+	end,
+})
+
 -- Register module in Lua environment
 function modules_register(module)
 	-- Syntactic sugar for get() and set() properties
@@ -85,7 +95,7 @@ end
 
 -- Make sandboxed environment
 local function make_sandbox(defined)
-	local __protected = { modules = true, cache = true, net = true }
+	local __protected = { modules = true, cache = true, net = true, trust_anchors = true }
 	return setmetatable({}, {
 		__index = defined,
 		__newindex = function (t, k, v)
diff --git a/lib/dnssec/ta.c b/lib/dnssec/ta.c
index f9fd2bd9630d26900cf64ecc09b0aaefda66e5d6..e39eef8045b6bb35cfc0512e666382f847cc679f 100644
--- a/lib/dnssec/ta.c
+++ b/lib/dnssec/ta.c
@@ -14,641 +14,60 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <arpa/inet.h>
-#include <assert.h>
-#include <ctype.h>
-#include <pthread.h>
-
-#include <contrib/ucw/mempool.h>
 #include <libknot/descriptor.h>
-#include <libknot/dname.h>
-#include <libknot/internal/base64.h>
 #include <libknot/rdataset.h>
 #include <libknot/rrset.h>
-#include <libknot/rrtype/rdname.h>
 #include <libknot/packet/wire.h>
 
 #include "lib/defines.h"
 #include "lib/dnssec/ta.h"
 
-static int dname_parse(knot_dname_t **dname, const char *dname_str, mm_ctx_t *pool)
-{
-	if (!dname) {
-		return kr_error(EINVAL);
-	}
-
-	knot_dname_t *owner = mm_alloc(pool, KNOT_DNAME_MAXLEN);
-	if (owner == NULL) {
-		return kr_error(ENOMEM);
-	}
-	knot_dname_t *aux = knot_dname_from_str(owner, dname_str, KNOT_DNAME_MAXLEN);
-	if (aux == NULL) {
-		mm_free(pool, owner);
-		return kr_error(ENOMEM);
-	}
-
-	assert(!*dname);
-	*dname = owner;
-	return 0;
-}
-
-static int uint_parse(const char *str, unsigned *u)
-{
-	char *err_pos;
-	long num = strtol(str, &err_pos, 10);
-	if ((*err_pos != '\0') || (num < 0)) {
-		return kr_error(EINVAL);
-	}
-	*u = (unsigned) num;
-	return 0;
-}
-
-static int strcicmp(char const *a, char const *b)
-{
-	if (!a && !b) {
-		return 0;
-	}
-	if (!a) {
-		return -1;
-	}
-	if (!b) {
-		return 1;
-	}
-	for ( ; ; ++a, ++b) {
-		int d = tolower(*a) - tolower(*b);
-		if ((d != 0) || (*a == '\0')) {
-			return d;
-		}
-	}
-}
-
-static int algorithm_parse(const char *str, unsigned *u)
-{
-	int ret = uint_parse(str, u);
-	if (ret == 0) {
-		return 0;
-	}
-
-	const lookup_table_t *item = knot_dnssec_alg_names;
-	while (item->id) {
-		if (strcicmp(str, item->name) == 0) {
-			break;
-		}
-		++item;
-	}
-
-	if (!item->id) {
-		return kr_error(ENOENT);
-	}
-
-	*u = (unsigned) item->id;
-	return 0;
-}
-
-static int hex2value(const char hex)
-{
-	if ((hex >= '0') && (hex <= '9')) {
-		return hex - '0';
-	} else if ((hex >= 'a') && (hex <= 'f')) {
-		return hex - 'a' + 10;
-	} else if ((hex >= 'A') && (hex <= 'F')) {
-		return hex - 'A' + 10;
-	} else {
-		return -1;
-	}
-}
-
-static int hex2byte(const char hex[2], uint8_t *u)
-{
-	int d0, d1;
-	d0 = hex2value(hex[0]);
-	d1 = hex2value(hex[1]);
-
-	if ((d0 == -1) || (d1 == -1)) {
-		return kr_error(EINVAL);
-	}
-
-	*u = ((d0 & 0x0f) << 4) | (d1 & 0x0f);
-	return 0;
-}
-
-static int ta_ds_parse(uint8_t *rd, size_t *rd_written, size_t rd_maxsize, const char *seps, char **saveptr)
-{
-	if (!rd || !rd_written || !seps || !saveptr) {
-		return kr_error(EINVAL);
-	}
-
-	int ret = 0;
-	const char *token;
-	unsigned aux;
-
-	/* Key tag. */
-	token = strtok_r(NULL, seps, saveptr);
-	if (!token) {
-		return kr_error(EINVAL);
-	}
-	ret = uint_parse(token, &aux);
-	if (ret != 0) {
-		return ret;
-	}
-	uint16_t key_tag = aux;
-
-	/* Algorithm. */
-	token = strtok_r(NULL, seps, saveptr);
-	if (!token) {
-		return kr_error(EINVAL);
-	}
-	ret = algorithm_parse(token, &aux);
-	if (ret != 0) {
-		return ret;
-	}
-	uint8_t algorithm = aux;
-
-	/* Digest type. */
-	token = strtok_r(NULL, seps, saveptr);
-	if (!token) {
-		return kr_error(EINVAL);
-	}
-	ret = uint_parse(token, &aux);
-	if (ret != 0) {
-		return ret;
-	}
-	uint8_t digest_type = aux;
-
-	size_t rd_pos = 0;
-	if (rd_maxsize >= 4) {
-		* (uint16_t *) (rd + rd_pos) = htons(key_tag); rd_pos += 2;
-		*(rd + rd_pos++) = algorithm;
-		*(rd + rd_pos++) = digest_type;
-	} else {
-		return kr_error(EINVAL);
-	}
-
-	char hexbuf[2];
-	int i = 0;
-	while ((token = strtok_r(NULL, seps, saveptr)) != NULL) {
-		for (int j = 0; j < strlen(token); ++j) {
-			hexbuf[i++] = token[j];
-			if (i == 2) {
-				uint8_t byte;
-				ret = hex2byte(hexbuf, &byte);
-				if (ret != 0) {
-					return ret;
-				}
-				i = 0;
-
-				if (rd_pos < rd_maxsize) {
-					*(rd + rd_pos++) = byte;
-				} else {
-					return kr_error(ENOMEM);
-				}
-			}
-		}
-	}
-
-	if (i != 0) {
-		return kr_error(EINVAL);
-	}
-
-	*rd_written = rd_pos;
-	return 0;
-}
-
-static int base2bytes(const uint8_t base[4], uint8_t bytes[3], unsigned *valid)
+knot_rrset_t *kr_ta_get(map_t *trust_anchors, const knot_dname_t *name)
 {
-	int32_t decoded = base64_decode(base, 4, bytes, 3);
-	if (decoded < 0) {
-		return kr_error(EINVAL);
-	}
-	*valid = decoded;
-	return 0;
+	return map_get(trust_anchors, (const char *)name);
 }
 
-static int ta_dnskey_parse(uint8_t *rd, size_t *rd_written, size_t rd_maxsize, const char *seps, char **saveptr)
+int kr_ta_add(map_t *trust_anchors, const knot_dname_t *name, uint16_t type,
+               uint32_t ttl, const uint8_t *rdata, uint16_t rdlen)
 {
-	fprintf(stderr, "%s()\n", __func__);
-
-	if (!rd || !rd_written || !seps || !saveptr) {
-		return kr_error(EINVAL);
-	}
-
-	int ret = 0;
-	const char *token;
-	unsigned aux;
-
-	/* Flags. */
-	token = strtok_r(NULL, seps, saveptr);
-	if (!token) {
-		return kr_error(EINVAL);
-	}
-	ret = uint_parse(token, &aux);
-	if (ret != 0) {
-		return ret;
-	}
-	uint16_t flags = aux;
-
-	/* Protocol. */
-	token = strtok_r(NULL, seps, saveptr);
-	if (!token) {
-		return kr_error(EINVAL);
-	}
-	ret = uint_parse(token, &aux);
-	if (ret != 0) {
-		return ret;
-	}
-	uint8_t protocol = aux;
-	if (protocol != 3) {
-		return kr_error(EINVAL);
-	}
-
-	/* Algorithm. */
-	token = strtok_r(NULL, seps, saveptr);
-	if (!token) {
-		return kr_error(EINVAL);
-	}
-	ret = algorithm_parse(token, &aux);
-	if (ret != 0) {
-		return ret;
-	}
-	uint8_t algorithm = aux;
-
-	size_t rd_pos = 0;
-	if (rd_maxsize >= 4) {
-		* (uint16_t *) (rd + rd_pos) = htons(flags); rd_pos += 2;
-		*(rd + rd_pos++) = protocol;
-		*(rd + rd_pos++) = algorithm;
-	} else {
+	if (!trust_anchors || !name || !rdata) {
 		return kr_error(EINVAL);
 	}
 
-	uint8_t basebuf[4];
-	uint8_t databuf[3];
-	int i = 0;
-	while ((token = strtok_r(NULL, seps, saveptr)) != NULL) {
-		for (int j = 0; j < strlen(token); ++j) {
-			basebuf[i++] = token[j];
-			if (i == 4) {
-				unsigned written;
-				ret = base2bytes(basebuf, databuf, &written);
-				if (ret != 0) {
-					return ret;
-				}
-				i = 0;
-
-				if ((rd_pos + written) < rd_maxsize) {
-					memcpy(rd + rd_pos, databuf, written);
-					rd_pos += written;
-				} else {
-					return kr_error(ENOMEM);
-				}
-			}
-		}
-	}
-
-	if (i != 0) {
-		return kr_error(EINVAL);
-	}
-
-	*rd_written = rd_pos;
-	return 0;
-}
-
-int kr_ta_parse(knot_rrset_t **rr, const char *ds_str, mm_ctx_t *pool)
-{
-#define SEPARATORS " \t\n\r"
-#define RDATA_MAXSIZE 640
-	int ret = 0;
-
-	if (!rr || !ds_str || !pool) {
-		ret = kr_error(EINVAL);
-		goto fail;
-	}
-
-	char *ds_cpy = NULL;
-	knot_dname_t *owner = NULL;
-	knot_rdata_t *rdata = NULL;
-	knot_rrset_t *ds_set = NULL;
-
-	size_t ds_len = strlen(ds_str) + 1;
-	ds_cpy = mm_alloc(pool, ds_len);
-	if (!ds_cpy) {
-		ret = kr_error(ENOMEM);
-		goto fail;
-	}
-	memcpy(ds_cpy, ds_str, ds_len);
-	char *saveptr = NULL, *token;
-
-	/* Owner name. */
-	token = strtok_r(ds_cpy, SEPARATORS, &saveptr);
-	if (!token) {
-		ret = kr_error(EINVAL);
-		goto fail;
-	}
-	ret = dname_parse(&owner, token, pool);
-	if (ret != 0) {
-		goto fail;
-	}
-
-	/* TTL may be missing. */
-	uint32_t ttl = 0;
-	token = strtok_r(NULL, SEPARATORS, &saveptr);
-	if (isdigit(token[0])) {
-		unsigned aux;
-		ret = uint_parse(token, &aux);
-		if (ret != 0) {
-			goto fail;
-		}
-		ttl = aux;
-		/* Read class token. */
-		token = strtok_r(NULL, SEPARATORS, &saveptr);
-	}
-
-	/* Class. */
-	uint16_t class;
-	/* Token already read. */
-	if (!token) {
-		ret = kr_error(EINVAL);
-		goto fail;
-	}
-	ret = knot_rrclass_from_string(token, &class);
-	if (ret != 0) {
-		ret = kr_error(EINVAL);
-		goto fail;
-	}
-
-	/* Type. */
-	uint16_t type;
-	token = strtok_r(NULL, SEPARATORS, &saveptr);
-	if (!token) {
-		ret = kr_error(EINVAL);
-		goto fail;
-	}
-	ret = knot_rrtype_from_string(token, &type);
-	if ((ret != 0) ||
-	    ((type != KNOT_RRTYPE_DS) && (type != KNOT_RRTYPE_DNSKEY))) {
-		ret = kr_error(EINVAL);
-		goto fail;
-	}
-
-	/* Construct RDATA. */
-	rdata = mm_alloc(pool, RDATA_MAXSIZE);
-	if (!rdata) {
-		ret = kr_error(ENOMEM);
-		goto fail;
-	}
-	size_t rd_written = 0;
-
+	/* Convert DNSKEY records to DS */
 	switch (type) {
-	case KNOT_RRTYPE_DS:
-		ret = ta_ds_parse(rdata, &rd_written, RDATA_MAXSIZE, SEPARATORS, &saveptr);
-		break;
+	case KNOT_RRTYPE_DS: break; /* OK */
 	case KNOT_RRTYPE_DNSKEY:
-		ret = ta_dnskey_parse(rdata, &rd_written, RDATA_MAXSIZE, SEPARATORS, &saveptr);
-		break;
-	default:
-		assert(0);
-		ret = kr_error(EINVAL);
+#warning TODO: convert DNSKEY -> DS here
+		return kr_error(ENOSYS);
 		break;
+	default: return kr_error(EINVAL);
 	}
-	if (ret != 0) {
-		goto fail;
-	}
-
-	ds_set = knot_rrset_new(owner, type, class, pool);
-	if (!ds_set) {
-		ret = kr_error(ENOMEM);
-		goto fail;
-	}
-
-	ret = knot_rrset_add_rdata(ds_set, rdata, rd_written, ttl, pool);
-	if (ret != 0) {
-		goto fail;
-	}
-
-	*rr = ds_set;
-	ds_set = NULL;
-
-fail:
-	knot_rrset_free(&ds_set, pool);
-	mm_free(pool, rdata);
-	knot_dname_free(&owner, pool);
-	mm_free(pool, ds_cpy);
-	return ret;
-#undef RDATA_MAXSIZE
-#undef SEPARATORS
-}
-
-#define MAX_ANCHORS 16
-struct trust_anchors_nolock {
-	mm_ctx_t pool;
-	knot_rrset_t *anchors[MAX_ANCHORS];
-	int used;
-};
-
-struct trust_anchors {
-	struct trust_anchors_nolock locked;
-	pthread_rwlock_t rwlock;
-};
-
-struct trust_anchors global_trust_anchors = {
-	.locked.pool = {0, },
-	.locked.anchors = {0, },
-	.locked.used = 0,
-};
-
-static int ta_init(struct trust_anchors_nolock *tan)
-{
-	assert(tan);
-
-	memset(tan, 0, sizeof(*tan));
-	tan->pool.ctx = mp_new(4 * CPU_PAGE_SIZE);
-	tan->pool.alloc = (mm_alloc_t) mp_alloc;
-	tan->used = 0;
-
-	return kr_ok();
-}
-
-static void ta_deinit(struct trust_anchors_nolock *tan)
-{
-	assert(tan);
-
-	if (tan->pool.ctx) {
-		mp_delete(tan->pool.ctx);
-		tan->pool.ctx = NULL;
-	}
-}
-
-int kr_ta_init(struct trust_anchors *tas)
-{
-	if (!tas) {
-		return kr_error(EINVAL);
-	}
-
-	int ret = ta_init(&tas->locked);
-	if (ret != 0) {
-		return ret;
-	}
-
-	ret = pthread_rwlock_init(&tas->rwlock, NULL);
-	if (ret != 0) {
-		ta_deinit(&tas->locked);
-		return kr_error(ret);
-	}
-	return kr_ok();
-}
-
-void kr_ta_deinit(struct trust_anchors *tas)
-{
-	if (!tas) {
-		return;
-	}
-
-	while (pthread_rwlock_destroy(&tas->rwlock) == EBUSY);
-
-	ta_deinit(&tas->locked);
-}
-
-static int ta_reset(struct trust_anchors_nolock *tan, const char *ta_str)
-{
-	assert(tan);
-
-	ta_deinit(tan);
-	int ret = ta_init(tan);
-	if (ret != 0) {
-		return ret;
-	}
-
-	if (!ta_str || (ta_str[0] == '\0')) {
-		return kr_ok();
-	}
-
-	knot_rrset_t *ta = NULL;
-	ret = kr_ta_parse(&ta, ta_str, &tan->pool);
-	if (ret != 0) {
-		return ret;
-	}
-
-	assert(ta);
 
-	tan->anchors[tan->used++] = ta;
-
-	return kr_ok();
-}
-
-int kr_ta_reset(struct trust_anchors *tas, const char *ta_str)
-{
-	if (!tas) {
-		return kr_error(ENOENT);
-	}
-
-	int ret = pthread_rwlock_wrlock(&tas->rwlock);
-	if (ret != 0) {
-		return kr_error(ret);
-	}
-
-	ret = ta_reset(&tas->locked, ta_str);
-
-	pthread_rwlock_unlock(&tas->rwlock);
-	return ret;
-}
-
-static knot_rrset_t *ta_find(struct trust_anchors_nolock *tan, const knot_dname_t *name)
-{
-	assert(tan && name);
-
-	knot_rrset_t *found = NULL;
-
-	int i;
-	for (i = 0; i < tan->used; ++i) {
-		if (knot_dname_is_equal(tan->anchors[i]->owner, name)) {
-			found = tan->anchors[i];
-			break;
-		}
+	/* Create new RRSet or use existing */
+	bool is_new_key = false;
+	knot_rrset_t *ta_rr = kr_ta_get(trust_anchors, name);
+	if (!ta_rr) { 
+		ta_rr = knot_rrset_new(name, type, KNOT_CLASS_IN, NULL);
+		is_new_key = true;
 	}
-
-	return found;
-}
-
-static int ta_add(struct trust_anchors_nolock *tan, const char *ta_str)
-{
-	assert(tan && ta_str);
-
-	if (tan->used >= MAX_ANCHORS) {
+	/* Merge-in new key data */
+	if (!ta_rr || knot_rrset_add_rdata(ta_rr, rdata, rdlen, ttl, NULL) != 0) {
+		knot_rrset_free(&ta_rr, NULL);
 		return kr_error(ENOMEM);
 	}
-
-	knot_rrset_t *ta = NULL;
-	int ret = kr_ta_parse(&ta, ta_str, &tan->pool);
-	if (ret != 0) {
-		return ret;
-	}
-	assert(ta);
-
-	knot_rrset_t *found = ta_find(tan, ta->owner);
-	if (!found) {
-		tan->anchors[tan->used++] = ta;
-		return kr_ok();
+	/* Reinsert */
+	if (is_new_key) {
+		map_set(trust_anchors, (const char *)name, ta_rr);
 	}
 
-	if (found->type != ta->type) {
-		knot_rrset_free(&ta, &tan->pool);
-		return kr_error(EINVAL);
-	}
-
-	ret = knot_rdataset_merge(&found->rrs, &ta->rrs, &tan->pool);
-	knot_rrset_free(&ta, &tan->pool);
-	if (ret != 0) {
-		return ret;
-	}
-
-	return kr_ok();
-}
-
-int kr_ta_add(struct trust_anchors *tas, const char *ta_str)
-{
-	if (!tas || !ta_str) {
-		return kr_error(EINVAL);
-	}
-
-	int ret = pthread_rwlock_wrlock(&tas->rwlock);
-	if (ret != 0) {
-		return kr_error(ret);
-	}
-
-	ret = ta_add(&tas->locked, ta_str);
-
-	pthread_rwlock_unlock(&tas->rwlock);
-	return ret;
-}
-
-static int ta_get(knot_rrset_t **ta, struct trust_anchors_nolock *tan, const knot_dname_t *name, mm_ctx_t *pool)
-{
-	assert(ta && tan && name);
-
-	knot_rrset_t *copy = ta_find(tan, name);
-	if (!copy) {
-		kr_error(ENOENT);
-	}
-
-	copy = knot_rrset_copy(copy, pool);
-	if (!copy) {
-		kr_error(ENOMEM);
-	}
-
-	*ta = copy;
-
-	return kr_ok();
+	return kr_ok();	
 }
 
-int kr_ta_contains(struct trust_anchors *tas, const knot_dname_t *name)
-{
-	return ta_find(&tas->locked, name) != NULL;
-}
-
-int kr_ta_covers(struct trust_anchors *tas, const knot_dname_t *name)
+int kr_ta_covers(map_t *trust_anchors, const knot_dname_t *name)
 {
 	while(name) {
-		if (kr_ta_contains(tas, name)) {
+		if (kr_ta_get(trust_anchors, name)) {
 			return true;
 		}
 		if (name[0] == '\0') {
@@ -656,63 +75,29 @@ int kr_ta_covers(struct trust_anchors *tas, const knot_dname_t *name)
 		}
 		name = knot_wire_next_label(name, NULL);
 	}
-	return false;	
-}
-
-int kr_ta_get(knot_rrset_t **ta, struct trust_anchors *tas, const knot_dname_t *name, mm_ctx_t *pool)
-{
-	if (!ta || !tas || !name) {
-		return kr_error(EINVAL);
-	}
-
-	int ret = pthread_rwlock_rdlock(&tas->rwlock);
-	if (ret != 0) {
-		return kr_error(ret);
-	}
-
-	ret = ta_get(ta, &tas->locked, name, pool);
-
-	pthread_rwlock_unlock(&tas->rwlock);
-	return ret;
-}
-
-int kr_ta_rdlock(struct trust_anchors *tas)
-{
-	if (!tas) {
-		return kr_error(EINVAL);
-	}
-
-	return pthread_rwlock_rdlock(&tas->rwlock);
+	return false;
 }
 
-int kr_ta_unlock(struct trust_anchors *tas)
+/* Delete record data */
+static int del_record(const char *k, void *v, void *ext)
 {
-	if (!tas) {
-		return kr_error(EINVAL);
-	}
-
-	return pthread_rwlock_unlock(&tas->rwlock);
+	knot_rrset_t *ta_rr = v;
+	knot_rrset_free(&ta_rr, NULL);
+	return 0;
 }
 
-int kr_ta_rrs_count_nolock(struct trust_anchors *tas)
+int kr_ta_del(map_t *trust_anchors, const knot_dname_t *name)
 {
-	if (!tas) {
-		return kr_error(EINVAL);
+	knot_rrset_t *ta_rr = kr_ta_get(trust_anchors, name);
+	if (ta_rr) {
+		del_record(NULL, ta_rr, NULL);
+		map_del(trust_anchors, (const char *)name);
 	}
-
-	return tas->locked.used;
+	return kr_ok();
 }
 
-int kr_ta_rrs_at_nolock(const knot_rrset_t **ta, struct trust_anchors *tas, size_t pos)
+void kr_ta_clear(map_t *trust_anchors)
 {
-	if (!tas || !ta) {
-		return kr_error(EINVAL);
-	}
-
-	if (pos >= tas->locked.used) {
-		return kr_error(EINVAL);
-	}
-
-	*ta = tas->locked.anchors[pos];
-	return kr_ok();
+	map_walk(trust_anchors, del_record, NULL);
+	map_clear(trust_anchors);
 }
diff --git a/lib/dnssec/ta.h b/lib/dnssec/ta.h
index 3900044373bc2a11c80ba6a216f86d1e9d07a135..6c712a0db162205b985d01c66d837ea8e77f8a4d 100644
--- a/lib/dnssec/ta.h
+++ b/lib/dnssec/ta.h
@@ -16,46 +16,49 @@
 
 #pragma once
 
-#include <libknot/internal/mempattern.h>
+#include "lib/generic/map.h"
 #include <libknot/rrset.h>
 
-//#define ROOT_TA ". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5"
-#define ROOT_NAME ((const uint8_t *) "")
-#define ROOT_TA ". IN DS 19036 RSASHA256 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5"
-//#define ROOT_TA ". IN DNSKEY 257 3 8 AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjF FVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoX bfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaD X6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpz W5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relS Qageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulq QxA+Uk1ihz0="
-
 /**
- * Parses the supplied trust anchor string and creates a new RRSet.
- *
- * @param rr     created resource record
- * @param ds_str DS in presentation format
- * @param pool
- * @return       0 or an error code
+ * Find TA RRSet by name.
+ * @param  trust_anchors trust store
+ * @param  name          name of the TA
+ * @return non-empty RRSet or NULL
  */
-int kr_ta_parse(knot_rrset_t **rr, const char *ds_str, mm_ctx_t *pool);
-
-/** Trust anchor container structure. */
-struct trust_anchors;
-
-/** Global trust anchor container. */
-extern struct trust_anchors global_trust_anchors;
-
-int kr_ta_init(struct trust_anchors *tas);
-
-void kr_ta_deinit(struct trust_anchors *tas);
-
-int kr_ta_reset(struct trust_anchors *tas, const char *ta_str);
-
-int kr_ta_add(struct trust_anchors *tas, const char *ta_str);
-
-int kr_ta_contains(struct trust_anchors *tas, const knot_dname_t *name);
+knot_rrset_t *kr_ta_get(map_t *trust_anchors, const knot_dname_t *name);
 
-int kr_ta_covers(struct trust_anchors *tas, const knot_dname_t *name);
+/**
+ * Add TA to trust store. DS or DNSKEY types are supported.
+ * @param  trust_anchors trust store
+ * @param  name          name of the TA
+ * @param  type          RR type of the TA (DS or DNSKEY)
+ * @param  ttl           
+ * @param  rdata         
+ * @param  rdlen         
+ * @return 0 or an error
+ */
+int kr_ta_add(map_t *trust_anchors, const knot_dname_t *name, uint16_t type,
+               uint32_t ttl, const uint8_t *rdata, uint16_t rdlen);
 
-int kr_ta_get(knot_rrset_t **ta, struct trust_anchors *tas, const knot_dname_t *name, mm_ctx_t *pool);
+/**
+ * Return true if the name is below/at any TA in the store.
+ * This can be useful to check if it's possible to validate a name beforehand.
+ * @param  trust_anchors trust store
+ * @param  name          name of the TA
+ * @return boolean
+ */
+int kr_ta_covers(map_t *trust_anchors, const knot_dname_t *name);
 
-int kr_ta_rdlock(struct trust_anchors *tas);
-int kr_ta_unlock(struct trust_anchors *tas);
+/**
+ * Remove TA from trust store.
+ * @param  trust_anchors trust store
+ * @param  name          name of the TA
+ * @return 0 or an error
+ */
+int kr_ta_del(map_t *trust_anchors, const knot_dname_t *name);
 
-int kr_ta_rrs_count_nolock(struct trust_anchors *tas);
-int kr_ta_rrs_at_nolock(const knot_rrset_t **ta, struct trust_anchors *tas, size_t pos);
+/**
+ * Clear trust store.
+ * @param trust_anchors trust store
+ */
+void kr_ta_clear(map_t *trust_anchors);
diff --git a/lib/layer/validate.c b/lib/layer/validate.c
index e09a67289aafc3bd0e11068fa77c258993bf57a7..31c8da944b9655f74f3555b4ba26797c451648eb 100644
--- a/lib/layer/validate.c
+++ b/lib/layer/validate.c
@@ -20,44 +20,22 @@
 #include <stdio.h>
 #include <string.h>
 
-#include <ccan/json/json.h>
 #include <libknot/packet/wire.h>
-#include <libknot/rrset-dump.h>
 #include <libknot/rrtype/rdname.h>
 #include <libknot/rrtype/rrsig.h>
 
 #include "lib/dnssec/nsec.h"
 #include "lib/dnssec/nsec3.h"
 #include "lib/dnssec/packet/pkt.h"
-#include "lib/dnssec/ta.h"
 #include "lib/dnssec.h"
 #include "lib/layer.h"
 #include "lib/resolve.h"
 #include "lib/rplan.h"
 #include "lib/defines.h"
-#include "lib/nsrep.h"
 #include "lib/module.h"
 
 #define DEBUG_MSG(qry, fmt...) QRDEBUG(qry, "vldr", fmt)
 
-static knot_dump_style_t KNOT_DUMP_STYLE_TA = {
-	.wrap = false,
-	.show_class = true,
-	.show_ttl = false,
-	.verbose = false,
-	.empty_ttl = false,
-	.human_ttl = false,
-	.human_tmstamp = true,
-	.ascii_to_idn = NULL
-};
-
-/* Set resolution context and parameters. */
-static int begin(knot_layer_t *ctx, void *module_param)
-{
-	ctx->data = module_param;
-	return KNOT_STATE_PRODUCE;
-}
-
 struct rrset_ids {
 	const knot_dname_t *owner;
 	uint16_t type;
@@ -505,99 +483,10 @@ static int validate(knot_layer_t *ctx, knot_pkt_t *pkt)
 	DEBUG_MSG(qry, "<= answer valid, OK\n");
 	return ctx->state;
 }
-
-static int rrset_txt_dump_line(const knot_rrset_t *rrset, size_t pos,
-                               char *dst, const size_t maxlen, const knot_dump_style_t *style)
-{
-	assert(rrset && dst && maxlen && style);
-
-	int written = 0;
-	uint32_t ttl = knot_rdata_ttl(knot_rdataset_at(&rrset->rrs, 0));
-	int ret = knot_rrset_txt_dump_header(rrset, ttl, dst + written, maxlen - written, style);
-	if (ret <= 0) {
-		return ret;
-	}
-	written += ret;
-	ret = knot_rrset_txt_dump_data(rrset, pos, dst + written, maxlen - written, style);
-	if (ret <= 0) {
-		return ret;
-	}
-	written += ret;
-
-	return written;
-}
-
-static char *validate_trust_anchors(void *env, struct kr_module *module, const char *args)
-{
-#define MAX_BUF_LEN 1024
-	JsonNode *root = json_mkarray();
-
-	kr_ta_rdlock(&global_trust_anchors);
-
-	const knot_rrset_t *ta;
-	int count = kr_ta_rrs_count_nolock(&global_trust_anchors);
-	for (int i = 0; i < count; ++i) {
-		ta = NULL;
-		kr_ta_rrs_at_nolock(&ta, &global_trust_anchors, i);
-		assert(ta);
-		char buf[MAX_BUF_LEN];
-		for (uint16_t j = 0; j < ta->rrs.rr_count; ++j) {
-			buf[0] = '\0';
-			rrset_txt_dump_line(ta, j, buf, MAX_BUF_LEN, &KNOT_DUMP_STYLE_TA);
-			json_append_element(root, json_mkstring(buf));
-		}
-	}
-
-	kr_ta_unlock(&global_trust_anchors);
-
-	char *result = json_encode(root);
-	json_delete(root);
-	return result;
-#undef MAX_BUF_LEN
-}
-
-static char *validate_trust_anchor_add(void *env, struct kr_module *module, const char *args)
-{
-	int ret = 0;
-	if (!args || (args[0] == '\0')) {
-		ret = kr_error(EINVAL);
-	} else {
-		ret = kr_ta_add(&global_trust_anchors, args);
-	}
-
-	char *result = NULL;
-	asprintf(&result, "{ \"result\": %s }", ret == 0 ? "true" : "false");
-	return result;
-}
-
-static int load(struct trust_anchors *tas, const char *path)
-{
-#define MAX_LINE_LEN 512
-	auto_fclose FILE *fp = fopen(path, "r");
-	if (fp == NULL) {
-		DEBUG_MSG(NULL, "reading '%s' failed: %s\n", path, strerror(errno));
-		return kr_error(errno);
-	} else {
-		DEBUG_MSG(NULL, "reading '%s'\n", path);
-	}
-
-	char line[MAX_LINE_LEN];
-	while (fgets(line, sizeof(line), fp) != NULL) {
-		int ret = kr_ta_add(tas, line);
-		if (ret != 0) {
-			return ret;
-		}
-	}
-
-	return kr_ok();
-#undef MAX_LINE_LEN
-}
-
 /** Module implementation. */
 const knot_layer_api_t *validate_layer(struct kr_module *module)
 {
 	static const knot_layer_api_t _layer = {
-		.begin = &begin,
 		.consume = &validate,
 	};
 	/* Store module reference */
@@ -606,42 +495,7 @@ const knot_layer_api_t *validate_layer(struct kr_module *module)
 
 int validate_init(struct kr_module *module)
 {
-	int ret = kr_ta_init(&global_trust_anchors);
-	if (ret != 0) {
-		return ret;
-	}
-//	/* Add root trust anchor. */
-//	ret = kr_ta_add(&global_trust_anchors, ROOT_TA);
-	if (ret != 0) {
-		return ret;
-	}
-	return kr_ok();
-}
-
-int validate_config(struct kr_module *module, const char *conf)
-{
-	int ret = kr_ta_reset(&global_trust_anchors, NULL);
-	if (ret != 0) {
-		return ret;
-	}
-	return load(&global_trust_anchors, conf);
-}
-
-int validate_deinit(struct kr_module *module)
-{
-	kr_ta_deinit(&global_trust_anchors);
 	return kr_ok();
 }
 
-const struct kr_prop validate_prop_list[] = {
-    { &validate_trust_anchors, "trust_anchors", "Retrieve trust anchors.", },
-    { &validate_trust_anchor_add, "trust_anchor_add", "Adds a trust anchor.", },
-    { NULL, NULL, NULL }
-};
-
-struct kr_prop *validate_props(void)
-{
-	return (struct kr_prop *) validate_prop_list;
-}
-
 KR_MODULE_EXPORT(validate)
diff --git a/lib/module.c b/lib/module.c
index 53cfbe15c892e26e9f912369016c314f6ad3c8a0..071394940611722b5cb47b4441e181ed2a8e0097 100644
--- a/lib/module.c
+++ b/lib/module.c
@@ -25,16 +25,12 @@
 
 /* List of embedded modules */
 const knot_layer_api_t *iterate_layer(struct kr_module *module);
-int validate_init(struct kr_module *module);
-int validate_deinit(struct kr_module *module);
-int validate_config(struct kr_module *module, const char *conf);
 const knot_layer_api_t *validate_layer(struct kr_module *module);
-extern struct kr_prop validate_prop_list[];
 const knot_layer_api_t *rrcache_layer(struct kr_module *module);
 const knot_layer_api_t *pktcache_layer(struct kr_module *module);
 static const struct kr_module embedded_modules[] = {
 	{ "iterate",  NULL, NULL, NULL, iterate_layer, NULL, NULL, NULL },
-	{ "validate", validate_init, validate_deinit, validate_config, validate_layer, validate_prop_list, NULL, NULL },
+	{ "validate", NULL, NULL, NULL, validate_layer, NULL, NULL, NULL },
 	{ "rrcache",  NULL, NULL, NULL, rrcache_layer, NULL, NULL, NULL },
 	{ "pktcache", NULL, NULL, NULL, pktcache_layer, NULL, NULL, NULL },
 };
diff --git a/lib/resolve.c b/lib/resolve.c
index 6e6280e9fd3bbaaf3b80891e1b8d4900b8c6d646..074cbfe32d73853c6b96adde4be317026bb197de 100644
--- a/lib/resolve.c
+++ b/lib/resolve.c
@@ -248,7 +248,9 @@ static int resolve_query(struct kr_request *request, const knot_pkt_t *packet)
 
 	/* Deferred zone cut lookup for this query. */
 	qry->flags |= QUERY_AWAIT_CUT;
-	if (knot_pkt_has_dnssec(packet)) {
+	/* Want DNSSEC if it's posible to secure this name (e.g. is covered by any TA) */
+	map_t *trust_anchors = &request->ctx->trust_anchors;
+	if (knot_pkt_has_dnssec(packet) && kr_ta_covers(trust_anchors, qname)) {
 		qry->flags |= QUERY_DNSSEC_WANT;
 	}
 
@@ -354,12 +356,13 @@ static int zone_cut_subreq(struct kr_rplan *rplan, struct kr_query *parent,
 static int zone_cut_check(struct kr_request *request, struct kr_query *qry, knot_pkt_t *packet)
 {
 	struct kr_rplan *rplan = &request->rplan;
+	map_t *trust_anchors = &request->ctx->trust_anchors;
 
 	/* The query wasn't resolved from cache,
 	 * now it's the time to look up closest zone cut from cache. */
 	if (qry->flags & QUERY_AWAIT_CUT) {
 		/* Want DNSSEC if it's posible to secure this name (e.g. is covered by any TA) */
-		if (kr_ta_covers(&global_trust_anchors, qry->zone_cut.name)) {
+		if (kr_ta_covers(trust_anchors, qry->zone_cut.name)) {
 			qry->flags |= QUERY_DNSSEC_WANT;
 		}
 		int ret = ns_fetch_cut(qry, request, (qry->flags & QUERY_DNSSEC_WANT));
@@ -376,7 +379,7 @@ static int zone_cut_check(struct kr_request *request, struct kr_query *qry, knot
 	}
 	/* Enable DNSSEC if enters a new island of trust. */
 	bool want_secured = (qry->flags & QUERY_DNSSEC_WANT);
-	if (!want_secured && kr_ta_contains(&global_trust_anchors, qry->zone_cut.name)) {
+	if (!want_secured && kr_ta_get(trust_anchors, qry->zone_cut.name)) {
 		qry->flags |= QUERY_DNSSEC_WANT;
 		want_secured = true;
 		WITH_DEBUG {
@@ -387,8 +390,8 @@ static int zone_cut_check(struct kr_request *request, struct kr_query *qry, knot
 	}
 	/* @todo Disable DNSSEC if it encounters NTA */
 	if (want_secured && !qry->zone_cut.trust_anchor) {
-		kr_ta_get(&qry->zone_cut.trust_anchor, &global_trust_anchors,
-		          qry->zone_cut.name, qry->zone_cut.pool);
+		knot_rrset_t *ta_rr = kr_ta_get(trust_anchors, qry->zone_cut.name);
+		qry->zone_cut.trust_anchor = knot_rrset_copy(ta_rr, qry->zone_cut.pool);
 	}
 	/* Try to fetch missing DS. */
 	if (want_secured && (qry->flags & QUERY_AWAIT_DS)) {
diff --git a/lib/resolve.h b/lib/resolve.h
index c373819b7d9fa25c2e8ab7c2227bf3405487a3f9..d185fe48412a4b402f778573511036bacc9ca3a3 100644
--- a/lib/resolve.h
+++ b/lib/resolve.h
@@ -20,6 +20,7 @@
 #include <libknot/processing/layer.h>
 #include <libknot/packet/pkt.h>
 
+#include "lib/generic/map.h"
 #include "lib/generic/array.h"
 #include "lib/nsrep.h"
 #include "lib/rplan.h"
@@ -81,15 +82,16 @@ typedef array_t(struct kr_module *) module_array_t;
  *       be shared between threads.
  */
 struct kr_context
-{
-	mm_ctx_t *pool;
+{	
+	uint32_t options;
+	knot_rrset_t *opt_rr;
+	map_t trust_anchors;
 	struct kr_zonecut root_hints;
 	struct kr_cache cache;
 	kr_nsrep_lru_t *cache_rtt;
 	kr_nsrep_lru_t *cache_rep;
 	module_array_t *modules;
-	knot_rrset_t *opt_rr;
-	uint32_t options;
+	mm_ctx_t *pool;
 };
 
 /**
diff --git a/tests/kresd.j2 b/tests/kresd.j2
index 72b89cea37fe284d18a42fb498d52f8462ce46e3..2f787b5cf5da8a90ee5170d3391c6434430530f0 100644
--- a/tests/kresd.j2
+++ b/tests/kresd.j2
@@ -1,10 +1,10 @@
-net.listen('{{SELF_ADDR}}',53)
-cache.size = 1*MB
+net = { '{{SELF_ADDR}}' }
 modules = {'stats', 'policy', 'hints'}
+cache.size = 1*MB
 hints.root({['k.root-servers.net'] = '{{ROOT_ADDR}}'})
 option('NO_MINIMIZE', {{NO_MINIMIZE}})
 option('ALLOW_LOCAL', true)
-validate.trust_anchor_add('{{TRUST_ANCHOR}}')
+trust_anchors.add('{{TRUST_ANCHOR}}')
 verbose(true)
 
 -- Self-checks on globals