diff --git a/.gitignore b/.gitignore
index 7579e289674a903ab64b6ef5c118cbf4f8c63377..d8d6b0a12f4f1a9ad8451f8e0dd6daab73135384 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,7 +14,7 @@
 *.out
 *.6
 *.log
-*.inc
+/daemon/lua/*.inc
 *.mdb
 *.gcno
 *.gcda
@@ -54,3 +54,4 @@ kresd.amalg.c
 libkres.amalg.c
 /doc/kresd.8
 /libkres.pc
+/daemon/lua/sandbox.lua
diff --git a/Makefile b/Makefile
index 8f4128aa28fbe7c60fd6bbc8e24a1c19bfbc4c74..371082bde7eaba51dcb3b2917ed9b835abeda017 100644
--- a/Makefile
+++ b/Makefile
@@ -31,6 +31,17 @@ $(eval $(call find_lib,socket_wrapper))
 $(eval $(call find_lib,libsystemd,227))
 $(eval $(call find_lib,gnutls))
 
+# Lookup SONAME
+$(eval $(call find_soname,libknot))
+$(eval $(call find_soname,libzscanner))
+
+ifeq ($(libknot_SONAME),)
+  $(error "Unable to resolve libknot_SONAME, update find_soname in platform.mk")
+endif
+ifeq ($(libzscanner_SONAME),)
+  $(error "Unable to resolve libzscanner_SONAME, update find_some in platform.mk")
+endif
+
 # Find Go version and platform
 GO_VERSION := $(shell $(GO) version 2>/dev/null)
 ifeq ($(GO_VERSION),)
diff --git a/daemon/daemon.mk b/daemon/daemon.mk
index 24c196a0f4aecf5db2387ec9a572881568f90c42..ff00fb108b99bd5e6802b5ca101f82d9838c2cb9 100644
--- a/daemon/daemon.mk
+++ b/daemon/daemon.mk
@@ -23,7 +23,9 @@ endif
 bindings-install: $(kresd_DIST) $(DESTDIR)$(MODULEDIR)
 	$(INSTALL) -m 0644 $(kresd_DIST) $(DESTDIR)$(MODULEDIR)
 
-kresd_CFLAGS := -fPIE
+kresd_CFLAGS := -fPIE \
+		-Dlibknot_SONAME=\"$(libknot_SONAME)\" \
+		-Dlibzscanner_SONAME=\"$(libzscanner_SONAME)\"
 kresd_DEPEND := $(libkres) $(contrib)
 kresd_LIBS := $(libkres_TARGET) $(contrib_TARGET) $(libknot_LIBS) \
               $(libzscanner_LIBS) $(libdnssec_LIBS) $(libuv_LIBS) $(lua_LIBS) \
diff --git a/daemon/engine.c b/daemon/engine.c
index d08344b63cfb9512e19e01f950d48622b6a8e730..41be1ad861f09d721a67a727c48ea356ad27e802 100644
--- a/daemon/engine.c
+++ b/daemon/engine.c
@@ -129,26 +129,6 @@ static int l_setuser(lua_State *L)
 	return 1;
 }
 
-/** Return platform-specific versioned library name. */
-static int l_libpath(lua_State *L)
-{
-	int n = lua_gettop(L);
-	if (n < 2)
-		return 0;
-	auto_free char *lib_path = NULL;
-	const char *lib_name = lua_tostring(L, 1);
-	const char *lib_version = lua_tostring(L, 2);
-#if defined(__APPLE__)
-	lib_path = afmt("%s.%s.dylib", lib_name, lib_version);
-#elif _WIN32
-	lib_path = afmt("%s.dll", lib_name); /* Versioned in RC files */
-#else
-	lib_path = afmt("%s.so.%s", lib_name, lib_version);
-#endif
-	lua_pushstring(L, lib_path);
-	return 1;
-}
-
 /** Quit current executable. */
 static int l_quit(lua_State *L)
 {
@@ -508,8 +488,10 @@ static int init_state(struct engine *engine)
 	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, libknot_SONAME);
+	lua_setglobal(engine->L, "libknot_SONAME");
+	lua_pushliteral(engine->L, libzscanner_SONAME);
+	lua_setglobal(engine->L, "libzscanner_SONAME");
 	lua_pushcfunction(engine->L, l_tojson);
 	lua_setglobal(engine->L, "tojson");
 	lua_pushcfunction(engine->L, l_map);
diff --git a/daemon/lua/kres.lua b/daemon/lua/kres.lua
index 9a0675a2458e7c2542e4dc07fbf021d043dfed84..96a34649c8ee38387bd564c9e1cb508ad656997b 100644
--- a/daemon/lua/kres.lua
+++ b/daemon/lua/kres.lua
@@ -11,17 +11,7 @@ local bit = require('bit')
 local bor = bit.bor
 local band = bit.band
 local C = ffi.C
-
--- Load any of supported libknot SO versions
-local knot
-for ver = 2, 3 do
-	local ok, lib = pcall(ffi.load, libpath('libknot', tostring(ver)))
-	if ok then
-		knot = lib
-		break
-	end
-end
-assert(knot, 'support libknot not found')
+local knot = ffi.load(libknot_SONAME)
 
 ffi.cdef[[
 
diff --git a/modules/policy/zonefile.lua b/modules/policy/zonefile.lua
index c021a9cecc0bfa0415eba6733220c9e2fa1c3a48..9d64b19b1de62e6e3d436f78d7c7c5d87d3f1730 100644
--- a/modules/policy/zonefile.lua
+++ b/modules/policy/zonefile.lua
@@ -3,7 +3,7 @@
 --
 
 local ffi = require('ffi')
-local libzscanner = ffi.load(libpath('libzscanner', '1'))
+local libzscanner = ffi.load(libzscanner_SONAME)
 ffi.cdef[[
 void free(void *ptr);
 void *realloc(void *ptr, size_t size);
diff --git a/platform.mk b/platform.mk
index c530f89e71896e884c43360e7a13e8462569e738..bfcd7175f0991f1a4e3393d194d07cbc7baf2618 100644
--- a/platform.mk
+++ b/platform.mk
@@ -177,3 +177,29 @@ endef
 define find_gopkg
 	HAS_$(1) := $(shell go list $(2) > /dev/null 2>&1 && echo yes || echo no)
 endef
+
+define find_soname
+
+# N/A on Windows
+ifeq ($(PLATFORM),Windows)
+	$(1)_SONAME = $(1).dll
+endif
+
+# Use otool -D on OS X
+ifeq ($(PLATFORM),Darwin)
+	$(1)_SONAME = $$(shell otool -D $$$$(pkg-config --variable=libdir $(1))/$(1)$(LIBEXT) | sed -ne 's,.*/\($(1)\.[0-9]*.$(LIBEXT)\),\1,p')
+endif
+
+# Use objdump -p on Linux and BSDs
+ifeq ($(PLATFORM),POSIX)
+ifeq ($(UNAME),OpenBSD)
+	$(1)_SONAME = $$(shell basename $$$$(readlink -f $$$$(pkg-config --variable=libdir $(1))/$(1)$(LIBEXT)) | cut -f 1-3 -d .)
+else
+	$(1)_SONAME = $$(shell objdump -p $$$$(pkg-config --variable=libdir $(1))/$(1)$(LIBEXT) | sed -ne 's/[[:space:]]*SONAME[[:space:]]*\($(1)\.so\.[0-4]*\)/\1/p')
+endif
+endif
+
+endef # find_soname
+
+# Use this on OpenBSD
+#