diff --git a/modules/hints/README.rst b/modules/hints/README.rst
index 627f79954250b2fe20011b0914906ba842cd288e..b7ddf618a5550440d2dabf16529a40976c7b1e0f 100644
--- a/modules/hints/README.rst
+++ b/modules/hints/README.rst
@@ -3,4 +3,25 @@
 Static hints
 ------------
 
-This is a module providing static hints from ``/etc/hosts``, document me.
\ No newline at end of file
+This is a module providing static hints from ``/etc/hosts`` like file.
+
+Properties
+^^^^^^^^^^
+
+``config``
+	Load specified hosts file.
+
+	:Input:  ``path`` i.e. ``"/etc/hosts"``
+	:Output: ``{ result: bool }``
+
+``get``
+	Return list of address record matching given name.
+
+	:Input:  ``hostname`` i.e. ``"localhost"``
+	:Output: ``{ result: [address1, address2, ...] }``
+
+``set``
+	Set hostname - address hint.
+
+ 	:Input:  ``hostname address`` i.e. ``"localhost 127.0.0.1"``
+ 	:Output: ``{ result: bool }``
\ No newline at end of file
diff --git a/modules/hints/hints.c b/modules/hints/hints.c
index aa73c54aafc8475e3e219aa666f780a9e478bc8d..8b8873bbce7ab9b00bac77132d261831bbbe8242 100644
--- a/modules/hints/hints.c
+++ b/modules/hints/hints.c
@@ -14,6 +14,13 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/**
+ * @file hints.h
+ * @brief Constructed zone cut from the hosts-like file, see @zonecut.h
+ *
+ * The module provides an override for queried address records.
+ */
+
 #include <libknot/packet/pkt.h>
 #include <libknot/descriptor.h>
 #include <libknot/internal/lists.h>
@@ -21,32 +28,17 @@
 #include <libknot/rrtype/aaaa.h>
 
 #include "lib/layer/iterate.h"
-#include "lib/utils.h"
-#include "lib/defines.h"
+#include "lib/zonecut.h"
 #include "lib/module.h"
 #include "lib/layer.h"
 
+/* Defaults */
 #define DEFAULT_FILE "/etc/hosts"
 #define DEBUG_MSG(fmt...) QRDEBUG(NULL, "hint",  fmt)
-
-/* TODO: this is an experimental (slow) proof-of-concept,
- *       this will be rewritten with namedb API
- */
-
 typedef int (*rr_callback_t)(const knot_rrset_t *, unsigned, struct kr_layer_param *);
 
-struct hint_map {
-	list_t list;
-	mm_ctx_t pool;
-};
-
-struct hint_pair {
-	node_t n;
-	knot_dname_t *name;
-	char *addr;
-};
-
-static struct hint_map *g_map = NULL;
+/** @todo Hack until layers can store userdata. */
+static struct kr_zonecut *g_map = NULL;
 
 static int begin(knot_layer_t *ctx, void *module_param)
 {
@@ -54,59 +46,93 @@ static int begin(knot_layer_t *ctx, void *module_param)
 	return ctx->state;
 }
 
+static int answer_query(pack_t *addr_set, struct kr_layer_param *param)
+{
+	struct kr_query *qry = kr_rplan_current(param->rplan);
+	knot_rrset_t rr;
+	knot_rrset_init(&rr, qry->sname, qry->stype, KNOT_CLASS_IN);
+	int family_len = sizeof(struct in_addr);
+	if (rr.type == KNOT_RRTYPE_AAAA) {
+		family_len = sizeof(struct in6_addr);
+	}
+
+	/* Update addresses */
+	uint8_t *addr = pack_head(*addr_set);
+	while (addr != pack_tail(*addr_set)) {
+		size_t len = pack_obj_len(addr);
+		void *addr_val = pack_obj_val(addr);
+		if (len == family_len) {
+			knot_rrset_add_rdata(&rr, addr_val, len, 0, NULL);
+		}
+		addr = pack_obj_next(addr);
+	}
+
+	/* Process callbacks */
+	rr_callback_t callback = &rr_update_parent;
+	if (!qry->parent) {
+		callback = &rr_update_answer;
+	}
+	callback(&rr, 0, param);
+
+	/* Finalize */
+	DEBUG_MSG("<= answered from hints\n");
+	knot_rdataset_clear(&rr.rrs, NULL);
+	qry->flags |= QUERY_RESOLVED;
+	return KNOT_STATE_DONE;
+}
+
 static int query(knot_layer_t *ctx, knot_pkt_t *pkt)
 {
 	assert(pkt && ctx);
 	struct kr_layer_param *param = ctx->data;
-	struct kr_query *cur = kr_rplan_current(param->rplan);
-	if (cur == NULL) {
+	struct kr_query *qry = kr_rplan_current(param->rplan);
+	if (qry->stype != KNOT_RRTYPE_A && qry->stype != KNOT_RRTYPE_AAAA) {
 		return ctx->state;
 	}
 
-	const knot_dname_t *qname = knot_pkt_qname(pkt);
-	uint16_t qtype = knot_pkt_qtype(pkt);
-	if (qtype != KNOT_RRTYPE_A && qtype != KNOT_RRTYPE_AAAA) {
+	/* Find a matching name */
+	pack_t *pack = kr_zonecut_find(g_map, qry->sname);
+	if (!pack || pack->len == 0) {
 		return ctx->state;
 	}
 
-	/* Check if updating parent zone cut. */
-	rr_callback_t callback = &rr_update_parent;
-	if (cur->parent == NULL) {
-		callback = &rr_update_answer;
-	}
+	return answer_query(pack, param);
+}
 
-	struct hint_pair *pair = NULL;
-	WALK_LIST(pair, g_map->list) {
-		if (knot_dname_is_equal(qname, pair->name)) {
-			DEBUG_MSG("found hint '%s'\n", pair->addr);
-			int addr_type = strchr(pair->addr, ':') ? AF_INET6 : AF_INET;
-			if ((addr_type == AF_INET) != (qtype == KNOT_RRTYPE_A)) {
-				continue;
-			}
+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);
+}
 
-			knot_rrset_t rr;
-			knot_rrset_init(&rr, pair->name, qtype, KNOT_CLASS_IN);
-			struct sockaddr_storage addr;
-			sockaddr_set(&addr, addr_type, pair->addr, 0);
-			size_t addr_len = 0;
-			uint8_t *raw_addr = sockaddr_raw(&addr, &addr_len);
-			knot_rrset_add_rdata(&rr, raw_addr, addr_len, 0, &param->answer->mm);
-			callback(&rr, 0, param);
-
-			cur->flags |= QUERY_RESOLVED;
-			return KNOT_STATE_DONE;
-		}
+static int add_pair(struct kr_zonecut *hints, const char *name, const char *addr)
+{
+	/* Build key */
+	knot_dname_t key[KNOT_DNAME_MAXLEN];
+	if (!knot_dname_from_str(key, name, sizeof(key))) {
+		return kr_error(EINVAL);
 	}
 
-	return ctx->state;
+	/* Parse address string */
+	struct sockaddr_storage ss;
+	if (parse_addr_str(&ss, addr) != 0) {
+		return kr_error(EINVAL);
+	}
+
+	/* Build rdata */
+	size_t addr_len = 0;
+	uint8_t *raw_addr = sockaddr_raw(&ss, &addr_len);
+	knot_rdata_t rdata[knot_rdata_array_size(addr_len)];
+	knot_rdata_init(rdata, addr_len, raw_addr, 0);
+
+	return kr_zonecut_add(hints, key, rdata);
 }
 
-static int load_map(struct hint_map *map, FILE *fp)
+static int load_map(struct kr_zonecut *hints, FILE *fp)
 {
-	knot_dname_t name_buf[KNOT_DNAME_MAXLEN];
 	size_t line_len = 0;
+	size_t count = 0;
 	auto_free char *line = NULL;
-	init_list(&map->list);
 
 	while(getline(&line, &line_len, fp) > 0) {
 		char *saveptr = NULL;
@@ -116,35 +142,121 @@ static int load_map(struct hint_map *map, FILE *fp)
 		}
 		char *name_tok = strtok_r(NULL, " \t\n", &saveptr);
 		while (name_tok != NULL) {
-			struct hint_pair *pair = mm_alloc(&map->pool, sizeof(struct hint_pair));
-			if (pair == NULL) {
-				return kr_error(ENOMEM);
-			}
-			pair->name = knot_dname_from_str(name_buf, name_tok, sizeof(name_buf));
-			if (pair->name == NULL) {
-				continue;
-			}
-
-			pair->name = knot_dname_copy(pair->name, &map->pool);
-			if (pair->name == NULL) {
-				return kr_error(ENOMEM);
-			}
-			pair->addr = mm_alloc(&map->pool, strlen(tok) + 1);
-			if (pair->addr == NULL) {
-				return kr_error(ENOMEM);
+			if (add_pair(hints, name_tok, tok) == 0) {
+				count += 1;
 			}
-
-			strcpy(pair->addr, tok);
-			add_tail(&map->list, &pair->n);
 			name_tok = strtok_r(NULL, " \t\n", &saveptr);
 		}
 	}
 
-	DEBUG_MSG("loaded %zu hints\n", list_size(&map->list));
-
+	DEBUG_MSG("loaded %zu hints\n", count);
 	return kr_ok();
 }
 
+static int load(struct kr_module *module, const char *path)
+{
+	auto_fclose FILE *fp = fopen(path, "r");
+	if (fp == NULL) {
+		DEBUG_MSG("reading '%s' failed: %s\n", path, strerror(errno));
+		return kr_error(errno);
+	} else {
+		DEBUG_MSG("reading '%s'\n", path);
+	}
+
+	/* Create pool and copy itself */
+	mm_ctx_t _pool;
+	mm_ctx_mempool(&_pool, MM_DEFAULT_BLKSIZE);
+	mm_ctx_t *pool = mm_alloc(&_pool, sizeof(*pool));
+	if (!pool) {
+		return kr_error(ENOMEM);
+	}
+	memcpy(pool, &_pool, sizeof(*pool));
+
+	/* Load file to map */
+	struct kr_zonecut *hints = mm_alloc(pool, sizeof(*hints));
+	kr_zonecut_init(hints, (const uint8_t *)(""), pool);
+	module->data = hints;
+	g_map = hints;
+	return load_map(hints, fp);
+}
+
+static void unload(struct kr_module *module)
+{
+	struct kr_zonecut *hints = module->data;
+	if (hints) {
+		mp_delete(hints->pool->ctx);
+		module->data = NULL;
+	}
+}
+
+/**
+ * Set name => address hint.
+ *
+ * Input:  { name, address }
+ * Output: { result: bool }
+ *
+ */
+static char* hint_set(void *env, struct kr_module *module, const char *args)
+{
+	struct kr_zonecut *hints = module->data;
+	auto_free char *args_copy = strdup(args);
+
+	int ret = -1;
+	char *addr = strchr(args_copy, ' ');
+	if (addr) {
+		*addr = '\0';
+		ret = add_pair(hints, args_copy, addr + 1);
+	}
+
+	char *result = NULL;
+	asprintf(&result, "{ \"result\": %s }", ret == 0 ? "true" : "false");
+	return result;
+}
+
+/**
+ * Retrieve address hint for given name.
+ *
+ * Input:  name
+ * Output: { address1, address2, ... }
+ */
+static char* hint_get(void *env, struct kr_module *module, const char *args)
+{
+	struct kr_zonecut *hints = module->data;
+	knot_dname_t key[KNOT_DNAME_MAXLEN];
+	pack_t *pack = NULL;
+	size_t bufsize = 4096;
+	if (knot_dname_from_str(key, args, sizeof(key))) {
+		pack = kr_zonecut_find(hints, key);
+	}
+	if (!pack || pack->len == 0) {
+		return NULL;
+	}
+
+	auto_free char *hint_buf = malloc(bufsize);
+	if (hint_buf == NULL) {
+		return NULL;
+	}
+	char *p = hint_buf, *endp = hint_buf + bufsize;
+	uint8_t *addr = pack_head(*pack);
+	while (addr != pack_tail(*pack)) {
+		size_t len = pack_obj_len(addr);
+		int family = len == sizeof(struct in_addr) ? AF_INET : AF_INET6;
+		if (!inet_ntop(family, pack_obj_val(addr), p, endp - p)) {
+			break;
+		}
+		p += strlen(p);
+		addr = pack_obj_next(addr);
+		if (p + 2 < endp && addr != pack_tail(*pack)) {
+			strcpy(p, " ");
+			p += 1;
+		}
+	}
+
+	char *result = NULL;
+	asprintf(&result, "{ \"result\": [ %s ] }", hint_buf ? hint_buf : "");
+	return result;
+}
+
 /*
  * Module implementation.
  */
@@ -160,31 +272,32 @@ const knot_layer_api_t *hints_layer(void)
 
 int hints_init(struct kr_module *module)
 {
-	auto_fclose FILE *fp = fopen(DEFAULT_FILE, "r");
-	if (fp == NULL) {
-		DEBUG_MSG("reading %s failed", DEFAULT_FILE);
-		return kr_error(errno);
-	}
-
-	mm_ctx_t pool;
-	mm_ctx_mempool(&pool, MM_DEFAULT_BLKSIZE);
-	struct hint_map *map = mm_alloc(&pool, sizeof(struct hint_map));
-	map->pool = pool;
-	module->data = map;
-
-	g_map = map;
-
-	return load_map(map, fp);
+	return load(module, DEFAULT_FILE);
 }
 
-int hints_deinit(struct kr_module *module)
+int hints_config(struct kr_module *module, const char *conf)
 {
-	struct hint_map *map = module->data;
-	if (map) {
-		mp_delete(map->pool.ctx);
+	unload(module);
+	if (!conf || strlen(conf) < 1) {
+		conf = DEFAULT_FILE;
 	}
+	return load(module, conf);
+}
 
+int hints_deinit(struct kr_module *module)
+{
+	unload(module);
 	return kr_ok();
 }
 
+struct kr_prop *hints_props(void)
+{
+	static struct kr_prop prop_list[] = {
+	    { &hint_set,    "set", "Set {name, address} hint.", },
+	    { &hint_get,    "get", "Retrieve hint for given name.", },
+	    { NULL, NULL, NULL }
+	};
+	return prop_list;
+}
+
 KR_MODULE_EXPORT(hints);