diff --git a/daemon/README.rst b/daemon/README.rst
index e7ce3c000b8102d5dc1217302a7c5a391860b823..c7979012231767c2bfcad273ea39db7ff5510979 100644
--- a/daemon/README.rst
+++ b/daemon/README.rst
@@ -595,6 +595,8 @@ For when listening on ``localhost`` just doesn't cut it.
       > net.tcp_pipeline(50)
       50
 
+.. _tls-server-config:
+
 .. function:: net.tls([cert_path], [key_path])
 
    Get/set path to a server TLS certificate and private key for DNS/TLS.
diff --git a/daemon/bindings.c b/daemon/bindings.c
index 46aaa8c504a71c566c6d2095aaf027ef522596f2..74e9c865fbb04bf5cf79b6dc1abfa8cf3885e276 100644
--- a/daemon/bindings.c
+++ b/daemon/bindings.c
@@ -391,7 +391,7 @@ static int net_tls(lua_State *L)
 
 	int r = tls_certificate_set(net, lua_tostring(L, 1), lua_tostring(L, 2));
 	if (r != 0) {
-		lua_pushstring(L, strerror(ENOMEM));
+		lua_pushstring(L, kr_strerror(r));
 		lua_error(L);
 	}
 
@@ -511,7 +511,7 @@ static int net_tls_client(lua_State *L)
 		int r = tls_client_params_set(&net->tls_client_params,
 					      addr, port, NULL, NULL, NULL);
 		if (r != 0) {
-			lua_pushstring(L, strerror(ENOMEM));
+			lua_pushstring(L, kr_strerror(r));
 			lua_error(L);
 		}
 
@@ -529,7 +529,7 @@ static int net_tls_client(lua_State *L)
 			int r = tls_client_params_set(&net->tls_client_params,
 						      addr, port, NULL, NULL, pin);
 			if (r != 0) {
-				lua_pushstring(L, strerror(ENOMEM));
+				lua_pushstring(L, kr_strerror(r));
 				lua_error(L);
 			}
 			lua_pop(L, 1);
@@ -555,7 +555,7 @@ static int net_tls_client(lua_State *L)
 		int r = tls_client_params_set(&net->tls_client_params,
 					      addr, port, ca_file, NULL, NULL);
 		if (r != 0) {
-			lua_pushstring(L, strerror(ENOMEM));
+			lua_pushstring(L, kr_strerror(r));
 			lua_error(L);
 		}
 		/* removes 'value'; keeps 'key' for next iteration */
@@ -569,7 +569,7 @@ static int net_tls_client(lua_State *L)
 		int r = tls_client_params_set(&net->tls_client_params,
 					      addr, port, NULL, hostname, NULL);
 		if (r != 0) {
-			lua_pushstring(L, strerror(ENOMEM));
+			lua_pushstring(L, kr_strerror(r));
 			lua_error(L);
 		}
 		/* removes 'value'; keeps 'key' for next iteration */
@@ -1393,7 +1393,7 @@ static int wrk_resolve(lua_State *L)
 	/* Create query packet */
 	knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_EDNS_MAX_UDP_PAYLOAD, NULL);
 	if (!pkt) {
-		lua_pushstring(L, strerror(ENOMEM));
+		lua_pushstring(L, kr_strerror(ENOMEM));
 		lua_error(L);
 	}
 	uint8_t dname[KNOT_DNAME_MAXLEN];
diff --git a/daemon/tls.c b/daemon/tls.c
index 17e0bf5bbdc0ee729ce069f0d36fcde44c70559c..7c92be92a466e518d40424ece57ae38f50b367f1 100644
--- a/daemon/tls.c
+++ b/daemon/tls.c
@@ -578,7 +578,7 @@ int tls_client_params_set(map_t *tls_client_paramlist,
 	char key[INET6_ADDRSTRLEN + 6];
 	size_t keylen = sizeof(key);
 	if (kr_straddr_join(addr, port, key, &keylen) != kr_ok()) {
-		kr_log_error("[tls client] warning: '%s' is not a valid ip address, ignoring\n", addr);
+		kr_log_error("[tls_client] warning: '%s' is not a valid ip address, ignoring\n", addr);
 		return kr_ok();
 	}
 
@@ -593,7 +593,7 @@ int tls_client_params_set(map_t *tls_client_paramlist,
 		int ret = gnutls_certificate_allocate_credentials(&entry->credentials);
 		if (ret != GNUTLS_E_SUCCESS) {
 			free(entry);
-			kr_log_error("[tls client] error: gnutls_certificate_allocate_credentials() fails (%s)\n",
+			kr_log_error("[tls_client] error: gnutls_certificate_allocate_credentials() fails (%s)\n",
 				     gnutls_strerror_name(ret));
 			return kr_error(ENOMEM);
 		}
@@ -605,7 +605,7 @@ int tls_client_params_set(map_t *tls_client_paramlist,
 		bool already_exists = false;
 		for (size_t i = 0; i < entry->ca_files.len; ++i) {
 			if (strcmp(entry->ca_files.at[i], ca_file) == 0) {
-				kr_log_error("[tls client] error: ca file '%s'for address '%s' already was set, ignoring\n", ca_file, key);
+				kr_log_error("[tls_client] error: ca file '%s'for address '%s' already was set, ignoring\n", ca_file, key);
 				already_exists = true;
 				break;
 			}
@@ -621,7 +621,7 @@ int tls_client_params_set(map_t *tls_client_paramlist,
 				int res = gnutls_certificate_set_x509_trust_file(entry->credentials, value,
 										 GNUTLS_X509_FMT_PEM);
 				if (res < 0) {
-					kr_log_error("[tls client], failed to import certificate file '%s' (%s)\n",
+					kr_log_error("[tls_client] failed to import certificate file '%s' (%s)\n",
 						     value, gnutls_strerror_name(res));
 					/* value will be freed at cleanup */
 					ret = kr_error(EINVAL);
@@ -634,7 +634,7 @@ int tls_client_params_set(map_t *tls_client_paramlist,
 		bool already_exists = false;
 		for (size_t i = 0; i < entry->hostnames.len; ++i) {
 			if (strcmp(entry->hostnames.at[i], hostname) == 0) {
-				kr_log_error("[tls client] error: hostname '%s' for address '%s' already was set, ignoring\n", hostname, key);
+				kr_log_error("[tls_client] error: hostname '%s' for address '%s' already was set, ignoring\n", hostname, key);
 				already_exists = true;
 				break;
 			}
@@ -653,7 +653,7 @@ int tls_client_params_set(map_t *tls_client_paramlist,
 	if ((ret == kr_ok()) && pin && pin[0] != 0) {
 		for (size_t i = 0; i < entry->pins.len; ++i) {
 			if (strcmp(entry->pins.at[i], pin) == 0) {
-				kr_log_error("[tls client] warning: pin '%s' for address '%s' already was set, ignoring\n", pin, key);
+				kr_log_error("[tls_client] warning: pin '%s' for address '%s' already was set, ignoring\n", pin, key);
 				return kr_ok();
 			}
 		}
@@ -1009,7 +1009,7 @@ int tls_client_connect_start(struct tls_client_ctx_t *ctx,
 	if (ret == GNUTLS_E_SUCCESS) {
 		return kr_ok();
 	} else if (gnutls_error_is_fatal(ret) != 0) {
-		kr_log_error("[tls client] handshake failed (%s)\n", gnutls_strerror(ret));
+		kr_log_error("[tls_client] handshake failed (%s)\n", gnutls_strerror(ret));
 		return kr_error(ECONNABORTED);
 	}
 	return kr_error(EAGAIN);
diff --git a/lib/utils.c b/lib/utils.c
index ac14767ce63aa29e58c02a7c687110a733b37c65..fa516e55208f7489ae7fb18054736201c421998d 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -77,7 +77,7 @@ static inline int u16tostr(uint8_t *dst, uint16_t num)
 
 static void kres_gnutls_log(int level, const char *message)
 {
-	kr_log_verbose("gnutls: (%d) %s", level, message);
+	kr_log_verbose("[gnutls] (%d) %s", level, message);
 }
 
 bool kr_verbose_set(bool status)
diff --git a/modules/policy/README.rst b/modules/policy/README.rst
index 2c082ff198cb222837b8609dbecd173298ccb755..9b1bf188faf2b30ae1a3ef465379cec71f76f510 100644
--- a/modules/policy/README.rst
+++ b/modules/policy/README.rst
@@ -28,7 +28,8 @@ There are several actions available in the ``policy.`` table:
 * ``DENY`` - reply NXDOMAIN authoritatively
 * ``DROP`` - terminate query resolution and return SERVFAIL to the requestor
 * ``TC`` - set TC=1 if the request came through UDP, forcing client to retry with TCP
-* ``FORWARD(ip)`` - solve a query via forwarding to an IP while validating and caching locally;
+* ``FORWARD(ip)`` - resolve a query via forwarding to an IP while validating and caching locally;
+* ``TLS_FORWARD({{ip, authentication}})`` - resolve a query via TLS connection forwarding to an IP while validating and caching locally;
   the parameter can be a single IP (string) or a lua list of up to four IPs.
 * ``STUB(ip)`` - similar to ``FORWARD(ip)`` but *without* attempting DNSSEC validation.
   Each request may be either answered from cache or simply sent to one of the IPs with proxying back the answer.
@@ -43,8 +44,41 @@ Most actions stop the policy matching on the query, but "chain actions" allow to
 
 .. note:: The module (and ``kres``) expects domain names in wire format, not textual representation. So each label in name is prefixed with its length, e.g. "example.com" equals to ``"\7example\3com"``. You can use convenience function ``todname('example.com')`` for automatic conversion.
 
-Examples
-^^^^^^^^
+Forwarding over TLS protocol (DNS-over-TLS)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Policy `TLS_FORWARD` allows you to forward queries using `Transport Layer Security`_ protocol, which hides the content of your queries from an attacker observing the network traffic. Further details about this protocol can be found in `RFC 7858`_ and `IETF draft dprive-dtls-and-tls-profiles`_.
+
+Queries affected by `TLS_FORWARD` policy will always be resolved over TLS connection. Knot Resolver does not implement fallback to non-TLS connection, so if TLS connection cannot be established or authenticated according to the configuration, the resolution will fail.
+
+To test this feature you need to either :ref:`configure Knot Resolver as DNS-over-TLS server <tls-server-config>`, or pick some public DNS-over-TLS server. Please see `DNS Privacy Project`_ homepage for list of public servers.
+
+When multiple servers are specified, the one with the lowest round-trip time is used.
+
+TLS Examples
+~~~~~~~~~~~~
+
+.. code-block:: lua
+
+	modules = { 'policy' }
+	-- forward all queries over TLS to the specified server
+	policy.add(policy.all(policy.TLS_FORWARD({{'192.0.2.1', pin_sha256='YQ=='}})))
+	-- for brevity, other TLS examples omit policy.add(policy.all())
+	-- single server authenticated using its certificate pin_sha256
+	  policy.TLS_FORWARD({{'192.0.2.1', pin_sha256='YQ=='}})  -- pin_sha256 is base64-encoded
+	-- single server using non-standard port
+	  policy.TLS_FORWARD({{'192.0.2.1@443', pin_sha256='YQ=='}})  -- use @ or # to specify port
+	-- single server with multiple valid pins (e.g. anycast)
+	  policy.TLS_FORWARD({{'192.0.2.1', pin_sha256={'YQ==', 'Wg=='}})
+	-- multiple servers, each with own authenticator
+	  policy.TLS_FORWARD({ -- please note that { here starts list of servers
+		{'192.0.2.1', pin_sha256='Wg=='},
+		-- server must present certificate issued by specified CA and hostname must match
+		{'2001:DB8::d0c', hostname='res.example.', ca_file='/etc/knot-resolver/tlsca.crt'}
+	})
+
+
+Other examples
+^^^^^^^^^^^^^^
 
 .. code-block:: lua
 
@@ -164,3 +198,7 @@ Most properties (actions, filters) are described above.
 .. _RPZ: https://dnsrpz.info/
 .. _`Pro DNS and BIND`: http://www.zytrax.com/books/dns/ch7/rpz.html
 .. _`Jan-Piet Mens's post`: http://jpmens.net/2011/04/26/how-to-configure-your-bind-resolvers-to-lie-using-response-policy-zones-rpz/
+.. _`Transport Layer Security`: https://en.wikipedia.org/wiki/Transport_Layer_Security
+.. _`DNS Privacy Project`: https://dnsprivacy.org/
+.. _`RFC 7858`: https://tools.ietf.org/html/rfc7858
+.. _`IETF draft dprive-dtls-and-tls-profiles`: https://tools.ietf.org/html/draft-ietf-dprive-dtls-and-tls-profiles
diff --git a/modules/policy/policy.lua b/modules/policy/policy.lua
index 7a829954a21c7a5c82b79c305159c6e112b0e180..95010d4f07ed35576279ae607537ece685c5ff7a 100644
--- a/modules/policy/policy.lua
+++ b/modules/policy/policy.lua
@@ -128,78 +128,114 @@ local function forward(target)
 	end
 end
 
--- Forward request and all subrequests to upstream over TCP; validate answers
+-- object must be non-empty string or non-empty table of non-empty strings
+local function is_nonempty_string_or_table(object)
+	if type(object) == 'string' then
+		return #object ~= 0
+	elseif type(object) ~= 'table' or not next(object) then
+		return false
+	end
+	for _, val in pairs(object) do
+		if type(val) ~= 'string' or #val == 0 then
+			return false
+		end
+	end
+	return true
+end
+
+local function insert_from_string_or_table(source, destination)
+	if type(source) == 'table' then
+		for _, v in pairs(source) do
+			table.insert(destination, v)
+		end
+	else
+		table.insert(destination, source)
+	end
+end
+
+-- Check for allowed authentication types and return type for the current target
+local function tls_forward_target_authtype(idx, target)
+	if (target.pin_sha256 and not (target.ca_file or target.hostname or target.insecure)) then
+		if not is_nonempty_string_or_table(target.pin_sha256) then
+			error('TLS_FORWARD target authentication is invalid at position '
+			      .. idx .. '; pin_sha256 must be string or list of strings')
+		end
+		return 'pin_sha256'
+	elseif (target.insecure and not (target.ca_file or target.hostname or target.pin_sha256)) then
+		return 'insecure'
+	elseif (target.ca_file and target.hostname and not (target.insecure or target.pin_sha256)) then
+		if not (is_nonempty_string_or_table(target.hostname)
+			and is_nonempty_string_or_table(target.ca_file)) then
+			error('TLS_FORWARD target authentication is invalid at position '
+			      .. idx .. '; hostname and ca_file must be string or list of strings')
+		end
+		return 'cert'
+	else
+		error('TLS_FORWARD authentication options at position ' .. idx
+		      .. ' are invalid; specify one of: pin_sha256 / hostname+ca_file / insecure')
+	end
+end
+
+local function tls_forward_target_check_syntax(idx, list_entry)
+	if type(list_entry) ~= 'table' then
+		error('TLS_FORWARD target must be a non-empty table (found '
+		      .. type(list_entry) .. ' at position ' .. idx .. ')')
+	end
+	if type(list_entry[1]) ~= 'string' then
+		error('TLS_FORWARD target must start with an IP address (found '
+		      .. type(list_entry[1]) .. ' at the beginning of target position ' .. idx .. ')')
+	end
+end
+
+-- Forward request and all subrequests to upstream over TLS; validate answers
 local function tls_forward(target)
-	local sockaddr_list = {}
-	local addr_list = {}
+	local sockaddr_c_list = {}
+	local sockaddr_config = {}  -- items: { string_addr=<addr string>, auth_type=<auth type> }
 	local ca_files = {}
 	local hostnames = {}
 	local pins = {}
-	if type(target) ~= 'table' then
-		assert(false, 'wrong TLS_FORWARD target')
-	end
-	for _, upstream_list_entry in pairs(target) do
-		local upstream_addr = upstream_list_entry[1]
-		if type(upstream_addr) ~= 'string' then
-			assert(false, 'bad IP address in TLS_FORWARD target')
-		end
-		table.insert(sockaddr_list, addr2sock(upstream_addr, 853))
-		table.insert(addr_list, upstream_addr)
-		local ca_file = upstream_list_entry['ca_file']
-		if ca_file ~= nil then
-			local hostname = upstream_list_entry['hostname']
-			if hostname == nil then
-				assert(false, 'hostname(s) is absent in TLS_FORWARD target')
-			end
-			local ca_files_local = {}
-			if type(ca_file) == 'table' then
-				for _, v in pairs(ca_file) do
-					table.insert(ca_files_local, v)
-				end
-			else
-				table.insert(ca_files_local, ca_file)
-			end
-			local hostnames_local = {}
-			if type(hostname) == 'table' then
-				for _, v in pairs(hostname) do
-					table.insert(hostnames_local, v)
-				end
-			else
-				table.insert(hostnames_local, hostname)
-			end
-			if next(ca_files_local) then
-				ca_files[upstream_addr] = ca_files_local
-			end
-			if next(hostnames_local) then
-				hostnames[upstream_addr] = hostnames_local
-			end
-		end
-		local pin = upstream_list_entry['pin']
-		local pins_local = {}
-		if pin ~= nil then
-			if type(pin) == 'table' then
-				for _, v in pairs(pin) do
-					table.insert(pins_local, v)
-				end
-			else
-				table.insert(pins_local, pin)
-			end
+	if type(target) ~= 'table' or #target < 1 then
+		error('TLS_FORWARD argument must be a non-empty table')
+	end
+	for idx, upstream_list_entry in pairs(target) do
+		tls_forward_target_check_syntax(idx, upstream_list_entry)
+		local auth_type = tls_forward_target_authtype(idx, upstream_list_entry)
+		local string_addr = upstream_list_entry[1]
+		local sockaddr_c = addr2sock(string_addr, 853)
+		local sockaddr_lua = ffi.string(sockaddr_c, ffi.C.kr_inaddr_len(sockaddr_c))
+		if sockaddr_config[sockaddr_lua] then
+			error('TLS_FORWARD configuration cannot declare two configs for IP address ' .. string_addr)
 		end
-		if next(pins_local) then
-			pins[upstream_addr] = pins_local
+		table.insert(sockaddr_c_list, sockaddr_c)
+		sockaddr_config[sockaddr_lua] = {string_addr=string_addr, auth_type=auth_type}
+		if auth_type == 'cert' then
+			ca_files[sockaddr_lua] = {}
+			hostnames[sockaddr_lua] = {}
+			insert_from_string_or_table(upstream_list_entry.ca_file, ca_files[sockaddr_lua])
+			insert_from_string_or_table(upstream_list_entry.hostname, hostnames[sockaddr_lua])
+		elseif auth_type == 'pin_sha256' then
+			pins[sockaddr_lua] = {}
+			insert_from_string_or_table(upstream_list_entry.pin_sha256, pins[sockaddr_lua])
+		elseif auth_type ~= 'insecure' then
+			-- insecure does nothing, user does not want authentication
+			assert(false, 'unsupported auth_type')
 		end
 	end
 
-	-- Update the global table of authentication data.
-	for _, v in pairs(addr_list) do
-		if (pins[v] == nil and ca_files[v] == nil) then
-			net.tls_client(v)
-		elseif (pins[v] ~= nil and ca_files[v] == nil) then
-			net.tls_client(v, pins[v])
-		elseif (pins[v] == nil and ca_files[v] ~= nil) then
-			net.tls_client(v, ca_files[v], hostnames[v])
+	-- Update the global table of authentication data only if all checks above passed
+	for sockaddr_lua, config in pairs(sockaddr_config) do
+		assert(#config.string_addr > 0)
+		if config.auth_type == 'insecure' then
+			net.tls_client(config.string_addr)
+		elseif config.auth_type == 'pin_sha256' then
+			assert(#pins[sockaddr_lua] > 0)
+			net.tls_client(config.string_addr, pins[sockaddr_lua])
+		elseif config.auth_type == 'cert' then
+			assert(#ca_files[sockaddr_lua] > 0)
+			assert(#hostnames[sockaddr_lua] > 0)
+			net.tls_client(config.string_addr, ca_files[sockaddr_lua], hostnames[sockaddr_lua])
 		else
-			net.tls_client(v, pins[v], ca_files[v], hostnames[v])
+			assert(false, 'unsupported auth_type')
 		end
 	end
 
@@ -213,7 +249,7 @@ local function tls_forward(target)
 		qry.flags.AWAIT_CUT = true
 		req.options.TCP = true
 		qry.flags.TCP = true
-		set_nslist(qry, sockaddr_list)
+		set_nslist(qry, sockaddr_c_list)
 		return state
 	end
 end
diff --git a/modules/policy/policy_test.lua b/modules/policy/policy_test.lua
new file mode 100644
index 0000000000000000000000000000000000000000..61da580fd12c85dc34186edef6c3388437f4f71f
--- /dev/null
+++ b/modules/policy/policy_test.lua
@@ -0,0 +1,45 @@
+-- setup resolver
+modules = { 'policy' }
+
+-- test for default configuration
+local function test_tls_forward()
+	boom(policy.TLS_FORWARD, {}, 'TLS_FORWARD without arguments')
+	boom(policy.TLS_FORWARD, {'1'}, 'TLS_FORWARD with non-table argument')
+	boom(policy.TLS_FORWARD, {{}}, 'TLS_FORWARD with empty table')
+	boom(policy.TLS_FORWARD, {{{}}}, 'TLS_FORWARD with empty target table')
+	boom(policy.TLS_FORWARD, {{{bleble=''}}}, 'TLS_FORWARD with invalid parameters in table')
+
+	boom(policy.TLS_FORWARD, {{'1'}}, 'TLS_FORWARD with invalid IP address')
+	-- boom(policy.TLS_FORWARD, {{{'::1', bleble=''}}}, 'TLS_FORWARD with valid IP and invalid parameters')
+	boom(policy.TLS_FORWARD, {{{'127.0.0.1'}}}, 'TLS_FORWARD with missing auth parameters')
+
+	ok(policy.TLS_FORWARD({{'127.0.0.1', insecure=true}}), 'TLS_FORWARD with no authentication')
+	boom(policy.TLS_FORWARD, {{{'100:dead::', insecure=true},
+				   {'100:DEAD:0::', insecure=true}
+			   }}, 'TLS_FORWARD with duplicate IP addresses is not allowed')
+	ok(policy.TLS_FORWARD({{'100:dead::', insecure=true},
+			       {'100:dead::@443', insecure=true}
+			   }), 'TLS_FORWARD with duplicate IP addresses but different ports is allowed')
+
+	boom(policy.TLS_FORWARD, {{{'::1', pin_sha256=''}}}, 'TLS_FORWARD with empty pin_sha256')
+	-- boom(policy.TLS_FORWARD, {{{'::1', pin_sha256='č'}}}, 'TLS_FORWARD with bad pin_sha256')
+	ok(policy.TLS_FORWARD({
+			{'::1', pin_sha256='ZTNiMGM0NDI5OGZjMWMxNDlhZmJmNGM4OTk2ZmI5MjQyN2FlNDFlNDY0OWI5MzRjYTQ5NTk5MWI3ODUyYjg1NQ=='}
+		}), 'TLS_FORWARD with base64 pin_sha256')
+	ok(policy.TLS_FORWARD({
+		{'::1', pin_sha256={
+			'ZTNiMGM0NDI5OGZjMWMxNDlhZmJmNGM4OTk2ZmI5MjQyN2FlNDFlNDY0OWI5MzRjYTQ5NTk5MWI3ODUyYjg1NQ==',
+			'MTcwYWUzMGNjZDlmYmE2MzBhZjhjZGE2ODQxZTAwYzZiNjU3OWNlYzc3NmQ0MTllNzAyZTIwYzY5YzQ4OGZmOA=='
+		}}}), 'TLS_FORWARD with table of pins')
+
+	-- ok(policy.TLS_FORWARD({{'::1', hostname='test.', ca_file='/tmp/ca.crt'}}), 'TLS_FORWARD with hostname + CA cert')
+	boom(policy.TLS_FORWARD, {{{'::1', hostname='test.'}}}, 'TLS_FORWARD with just hostname')
+	boom(policy.TLS_FORWARD, {{{'::1', ca_file='/tmp/ca.crt'}}}, 'TLS_FORWARD with just CA cert')
+	boom(policy.TLS_FORWARD, {{{'::1', hostname='', ca_file='/tmp/ca.crt'}}}, 'TLS_FORWARD with empty hostname + CA cert')
+	boom(policy.TLS_FORWARD, {{{'::1', hostname='test.', ca_file='/dev/a_file_which_surely_does_NOT_exist!'}}},
+		'TLS_FORWARD with hostname + unreadable CA cert')
+end
+
+return {
+	test_tls_forward
+}