diff --git a/daemon/lua/kres.lua b/daemon/lua/kres.lua
index e0d199487c39b9e79ca89aee37ca489022b3ebf1..4ab85646514f87a4f33fa976fe6f3faa3dc1dade 100644
--- a/daemon/lua/kres.lua
+++ b/daemon/lua/kres.lua
@@ -197,18 +197,24 @@ 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,
                uint16_t rclass, uint16_t rtype, const uint8_t *rdata, uint16_t rdlen);
 const char *kr_inaddr(const struct sockaddr *addr);
 int kr_inaddr_len(const struct sockaddr *addr);
+/* 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);
+/* DNSSEC */
+bool kr_dnssec_key_ksk(const uint8_t *dnskey_rdata);
+bool kr_dnssec_key_revoked(const uint8_t *dnskey_rdata);
+int kr_dnssec_key_tag(uint16_t rrtype, const uint8_t *rdata, size_t rdlen);
+int kr_dnssec_key_match(const uint8_t *key_a_rdata, size_t key_a_rdlen,
+                        const uint8_t *key_b_rdata, size_t key_b_rdlen);
 ]]
 
 -- Metatype for sockaddr
@@ -285,47 +291,118 @@ local kres = {
 	context = function () return kres_context end,
 }
 
+-- RFC5011 state table
+local key_state = {
+	Start = 'Start', AddPend = 'AddPend', Valid = 'Valid',
+	Missing = 'Missing', Revoked = 'Revoked', Removed = 'Removed'
+}
+
+-- Find key in current keyset
+local function ta_find(keyset, rr)
+	for i = 1, #keyset do
+		local ta = keyset[i]
+		-- Match key owner and content
+		if ta.owner == rr.owner and
+		   C.kr_dnssec_key_match(ta.rdata, #ta.rdata, rr.rdata, #rr.rdata) then
+		   return ta
+		end
+	end
+	return nil
+end
+
 -- Evaluate TA status according to RFC5011
-local function evaluate_ta(keyset, ta)
-	-- @todo: check if KSK
-	-- @todo: get TA id
-	-- @todo: check key flags for revoked
-	-- @todo: build a state table
-	table.insert(keyset, ta)
+local function ta_present(keyset, rr, force)
+	if not C.kr_dnssec_key_ksk(rr.rdata) then
+		return false -- Ignore
+	end	
+	-- Find the key in current key set and check its status
+	local key_revoked = C.kr_dnssec_key_revoked(rr.rdata)
+	local key_tag = C.kr_dnssec_key_tag(rr.type, rr.rdata, #rr.rdata)
+	local ta = ta_find(keyset, rr)
+	if ta then
+		-- Key reappears (KeyPres)
+		if ta.state == key_state.Missing then ta.state = key_state.Valid end
+		-- Key is revoked (RevBit)
+		if ta.state == key_state.Valid or ta.state == key_state.Missing then
+			if key_revoked then
+				ta.state = key_state.Revoked
+				-- @todo: ta.time = ...
+			end
+		end
+		-- @todo RemTime
+		-- @todo AddTime
+		-- Preserve key (KeyPres)
+		print('[trust_anchors] key: '..key_tag..' state: '..ta.state)
+		return true
+	elseif not key_revoked then -- First time seen (NewKey)
+		rr.state = force and key_state.Valid or key_state.AddPend
+		-- rr.time = ...
+		print('[trust_anchors] key: '..key_tag..' state: '..rr.state)
+		table.insert(keyset, rr)
+		return true
+	end
+	return false
+end
+
+-- TA is missing in the new key set
+local function ta_missing(keyset, ta)
+	-- Key is removed (KeyRem)
+	if ta.state == key_state.Valid then
+		ta.state = key_state.Missing
+		-- ta.time = ...
+	elseif ta.state == key_state.AddPend then
+		-- @todo: remove from the set (Start)
+	end
+	local key_tag = C.kr_dnssec_key_tag(ta.type, ta.rdata, #ta.rdata)
+	print('[trust_anchors] key: '..key_tag..' state: '..ta.state)
 end
 
 -- TA store management
 kres.trust_anchors = {
 	keyset = {},
+	insecure = {},
 	-- Update existing keyset
-	update = function (new_keys)
-		-- Evaluate new TAs
+	update = function (new_keys, initial)
+		-- Flag keys missing in new set (KeyRem)
 		local keyset = kres.trust_anchors.keyset
+		for i = 1, #keyset do
+			local ta = keyset[i]
+			if not ta_find(new_keys, ta) then
+				ta_missing(keyset, ta)
+			end
+		end
+		-- Evaluate new TAs
+		if not new_keys then return false end
 		for i = 1, #new_keys do
 			local rr = new_keys[i]
-			if rr.type == kres.type.DS or rr.type == kres.type.DNSKEY then
-				evaluate_ta(keyset, rr)
+			if rr.type == kres.type.DNSKEY then
+				ta_present(keyset, rr, initial)
 			end
 		end
 		-- Publish active TAs
 		local store = kres_context.trust_anchors
 		C.kr_ta_clear(store)
-		for id, key in pairs(keyset) do
-			C.kr_ta_add(store, key.owner, key.type, key.ttl, key.rdata, #key.rdata)
+		for i = 1, #keyset do
+			local ta = keyset[i]
+			-- Key MAY be used as a TA only in these two states (RFC5011, 4.2)
+			if ta.state == key_state.Valid or ta.state == key_state.Missing then
+				C.kr_ta_add(store, ta.owner, ta.type, ta.ttl, ta.rdata, #ta.rdata)
+			end
 		end
+		return true
 	end,
-	-- Load keys from a file
+	-- Load keys from a file (managed)
 	config = function (path)
 		local new_keys = require('zonefile').parse_file(path)
-		kres.trust_anchors.update(new_keys)
+		return kres.trust_anchors.update(new_keys, true)
 	end,
-	-- Add DS/DNSKEY record(s)
-	add = function (rr)
-		local new_keys = {}
+	-- Add DS/DNSKEY record(s) (unmanaged)
+	add = function (keystr)
+		local store = kres_context.trust_anchors
 		require('zonefile').parser(function (p)
-			table.insert(new_keys, p:current_rr())
-		end):read(rr..'\n')
-		kres.trust_anchors.update(new_keys)
+			local rr = p:current_rr()
+			C.kr_ta_add(store, rr.owner, rr.type, rr.ttl, rr.rdata, #rr.rdata)
+		end):read(keystr..'\n')
 	end,
 	-- Negative TA management
 	set_insecure = function (list)
@@ -334,6 +411,7 @@ kres.trust_anchors = {
 			local dname = kres.str2dname(list[i])
 			C.kr_ta_add(kres_context.negative_anchors, dname, kres.type.DS, 0, nil, 0)
 		end
+		kres.trust_anchors.insecure = list
 	end,
 }