luci-app-https-dns-proxy: update to 2025-12-29-1
authorStan Grishin <stangri@melmac.ca>
Thu, 8 Jan 2026 03:30:01 +0000 (03:30 +0000)
committerStan Grishin <stangri@melmac.ca>
Thu, 8 Jan 2026 03:30:58 +0000 (03:30 +0000)
* update version to match principal package
* split the main config section into service configs and the global
  instance configs
* add support for the new source_addr option; @karl82 I'd appreciate if
  you could look at the WebUI and documentation and suggest improvements
  for description
* add WebUI support for all available configs in both global configs and
  per-instance/provider modal window
* bugfix: store bootstrap_dns options from the provider json file when
  adding a new instance
* if listen_addr or listen_port or source_addr are not set, display nicer
  text in the GridSection view than `none`.

Signed-off-by: Stan Grishin <stangri@melmac.ca>
applications/luci-app-https-dns-proxy/Makefile
applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js
applications/luci-app-https-dns-proxy/po/templates/https-dns-proxy.pot

index ee430b60dee7ef137871e7fb61d873d27e112f48..dc867abb6b8bf0ea2616ac073999e5a591e393ec 100644 (file)
@@ -1,13 +1,13 @@
 # SPDX-License-Identifier: AGPL-3.0-or-later
-# Copyright 2017-2025 MOSSDeF, Stan Grishin (stangri@melmac.ca).
+# Copyright 2017-2026 MOSSDeF, Stan Grishin (stangri@melmac.ca).
 
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=luci-app-https-dns-proxy
 PKG_LICENSE:=AGPL-3.0-or-later
 PKG_MAINTAINER:=Stan Grishin <stangri@melmac.ca>
-PKG_VERSION:=2025.10.07
-PKG_RELEASE:=2
+PKG_VERSION:=2025.12.29
+PKG_RELEASE:=1
 
 LUCI_TITLE:=DNS Over HTTPS Proxy Web UI
 LUCI_URL:=https://github.com/stangri/luci-app-https-dns-proxy/
index 40623bd46f50ab2f63b23d1f4c10f97b79d1e320..08fa355b5be4ac1cb48a620f364c30e9b43f7bc2 100644 (file)
@@ -46,16 +46,19 @@ return view.extend({
                status = new hdp.status();
 
                m = new form.Map(pkg.Name, _("HTTPS DNS Proxy - Configuration"));
-
                s = m.section(form.NamedSection, "config", pkg.Name);
 
+               s.tab("service", _("Service Options"));
+               s.tab("global", _("Global Instance Options"));
+
                var dhcp_dnsmasq_values = Object.values(L.uci.sections("dhcp", "dnsmasq"));
                function isEmpty(obj) {
                        return Object.keys(obj).length === 0;
                }
 
                if (!isEmpty(dhcp_dnsmasq_values)) {
-                       o = s.option(
+                       o = s.taboption(
+                               "service",
                                form.ListValue,
                                "dnsmasq_config_update_option",
                                _("Update DNSMASQ Config on Start/Stop"),
@@ -93,7 +96,8 @@ return view.extend({
                                L.uci.set(pkg.Name, section_id, "dnsmasq_config_update", formvalue);
                        };
 
-                       o = s.option(
+                       o = s.taboption(
+                               "service",
                                form.MultiValue,
                                "dnsmasq_config_update",
                                _("Select the DNSMASQ Configs to update")
@@ -115,7 +119,8 @@ return view.extend({
                        o.retain = true;
                }
 
-               o = s.option(
+               o = s.taboption(
+                       "service",
                        form.ListValue,
                        "force_dns",
                        _("Force Router DNS"),
@@ -130,7 +135,8 @@ return view.extend({
                o.value("1", _("Force Router DNS server to all local devices"));
                o.default = "1";
 
-               o = s.option(
+               o = s.taboption(
+                       "service",
                        form.ListValue,
                        "canary_domains_icloud",
                        _("Canary Domains iCloud"),
@@ -146,7 +152,8 @@ return view.extend({
                o.depends("force_dns", "1");
                o.default = "1";
 
-               o = s.option(
+               o = s.taboption(
+                       "service",
                        form.ListValue,
                        "canary_domains_mozilla",
                        _("Canary Domains Mozilla"),
@@ -154,9 +161,9 @@ return view.extend({
                                "Blocks access to Mozilla Encrypted resolvers, forcing local devices to use router for DNS resolution (%smore information%s)."
                        ).format(
                                '<a href="' +
-                                       pkg.URL +
-                                       "#canary_domains_mozilla" +
-                                       '" target="_blank">',
+                               pkg.URL +
+                               "#canary_domains_mozilla" +
+                               '" target="_blank">',
                                "</a>"
                        )
                );
@@ -165,6 +172,149 @@ return view.extend({
                o.depends("force_dns", "1");
                o.default = "1";
 
+               o = s.taboption(
+                       "service",
+                       form.Value,
+                       "heartbeat_domain",
+                       _("Heartbeat Domain"),
+                       _(
+                               "The domain used for connectivity checks (%smore information%s)."
+                       ).format(
+                               '<a href="' + pkg.URL + "#heartbeat_domain" + '" target="_blank">',
+                               "</a>"
+                       )
+               );
+               o.optional = true;
+               o.placeholder = "heartbeat.melmac.ca";
+
+               o = s.taboption(
+                       "service",
+                       form.Value,
+                       "heartbeat_sleep_timeout",
+                       _("Heartbeat Sleep Timeout"),
+                       _("Time to wait before checking connectivity (seconds).")
+               );
+               o.datatype = "uinteger";
+               o.optional = true;
+               o.placeholder = "10";
+
+               o = s.taboption(
+                       "service",
+                       form.Value,
+                       "heartbeat_wait_timeout",
+                       _("Heartbeat Wait Timeout"),
+                       _("Time to wait for connectivity check response (seconds).")
+               );
+               o.datatype = "uinteger";
+               o.optional = true;
+               o.placeholder = "30";
+
+               o = s.taboption(
+                       "global",
+                       form.ListValue,
+                       "force_http1",
+                       _("Use HTTP/1")
+               );
+               o.optional = true;
+               o.rmempty = true;
+               o.value("", _("Use negotiated HTTP version"));
+               o.value("1", _("Force use of HTTP/1"));
+               o.default = "";
+
+               o = s.taboption(
+                       "global",
+                       form.ListValue,
+                       "force_http3",
+                       _("Use HTTP/3 (QUIC)")
+               );
+               o.optional = true;
+               o.rmempty = true;
+               o.value("", _("Use negotiated HTTP version"));
+               o.value("1", _("Force use of HTTP/3 (QUIC)"));
+               o.default = "";
+               o.depends("force_http1", "");
+
+               o = s.taboption(
+                       "global",
+                       form.ListValue,
+                       "force_ipv6_resolvers",
+                       _("Use IPv6 resolvers")
+               );
+               o.optional = true;
+               o.rmempty = true;
+               o.value("", _("Use any family DNS resolvers"));
+               o.value("1", _("Force use of IPv6 DNS resolvers"));
+               o.default = "";
+
+               o = s.taboption("global", form.ListValue, "verbosity", _("Logging Verbosity Level"));
+               o.optional = true;
+               o.value("", _("0: Fatal"));
+               o.value("1", _("1: Error"));
+               o.value("2", _("2: Warning"));
+               o.value("3", _("3: Info"));
+               o.value("4", _("4: Debug"));
+               o.default = "";
+
+               o = s.taboption("global", form.Value, "listen_addr", _("Listen Address"));
+               o.datatype = "ipaddr('nomask')";
+               o.optional = true;
+               o.placeholder = "127.0.0.1";
+
+               o = s.taboption("global", form.Value, "user", _("Run As User"));
+               o.optional = true;
+               o.placeholder = "nobody";
+
+               o = s.taboption("global", form.Value, "group", _("Run As Group"));
+               o.optional = true;
+               o.placeholder = "nogroup";
+
+               o = s.taboption("global", form.Value, "source_addr", _("Source Address"));
+               o.datatype = "ipaddr('nomask')";
+               o.optional = true;
+               o.placeholder = "";
+
+               o = s.taboption("global", form.Value, "logfile", _("Logging File Path"));
+               o.datatype = "file";
+               o.optional = true;
+               o.placeholder = "";
+
+               o = s.taboption("global", form.Value, "polling_interval", _("Polling Interval"));
+               o.datatype = "range(5,3600)";
+               o.optional = true;
+               o.placeholder = "120";
+
+               o = s.taboption("global", form.Value, "proxy_server", _("Proxy Server"));
+               o.optional = true;
+
+               o = s.taboption("global", form.Value, "ca_certs_file", _("CA Certs File"));
+               o.datatype = "file";
+               o.optional = true;
+
+               o = s.taboption("global", form.Value, "conn_loss_time", _("Connection Loss Time"));
+               o.datatype = "uinteger";
+               o.optional = true;
+               o.placeholder = "15";
+
+               o = s.taboption("global", form.Value, "log_limit", _("Log Limit"));
+               o.datatype = "uinteger";
+               o.optional = true;
+               o.placeholder = "0";
+
+               o = s.taboption("global", form.Value, "max_idle_time", _("Max Idle Time"));
+               o.datatype = "uinteger";
+               o.optional = true;
+               o.placeholder = "118";
+
+               o = s.taboption("global", form.Value, "statistic_interval", _("Statistic Interval"));
+               o.datatype = "uinteger";
+               o.optional = true;
+               o.placeholder = "0";
+
+               o = s.taboption("global", form.Value, "tcp_client_limit", _("TCP Client Limit"));
+               o.datatype = "uinteger";
+               o.optional = true;
+               o.placeholder = "20";
+
                text = "";
                if (!reply.platform.http2_support)
                        text +=
@@ -250,85 +400,114 @@ return view.extend({
                        });
                        return ret || "";
                };
+
                _provider.write = function (section_id, formvalue) {
-                       L.uci.set(pkg.Name, section_id, "resolver_url", formvalue);
+                       let providerTemplate = formvalue;
+                       let resolverUrl = providerTemplate;
+                       let bootstrapDns = "";
+                       let section = this.section;
+
+                       reply.providers.forEach((prov, i) => {
+                               if (prov.template === providerTemplate) {
+                                       let paramValue = "";
+                                       let paramWidgetOption = "";
+
+                                       if (prov.params && prov.params.option) {
+                                               if (prov.params.option.type === "select") {
+                                                       paramWidgetOption = "_paramList_" + i;
+                                               } else if (prov.params.option.type === "text") {
+                                                       paramWidgetOption = "_paramText_" + i;
+                                               }
+                                       }
+
+                                       if (paramWidgetOption) {
+                                               // Find the widget object to get the value safely
+                                               let widget = section.children.find(w => w.option === paramWidgetOption);
+                                               if (widget) {
+                                                       paramValue = widget.formvalue(section_id) || "";
+                                               }
+                                       }
+
+                                       resolverUrl = pkg.templateToResolver(providerTemplate, {
+                                               option: paramValue
+                                       });
+
+                                       let bootWidget = section.children.find(w => w.option === "_bootstrap_dns_" + i);
+                                       if (bootWidget) {
+                                               bootstrapDns = bootWidget.formvalue(section_id);
+                                       }
+
+                                       // Fallback to default if empty
+                                       if (!bootstrapDns) {
+                                               bootstrapDns = prov.bootstrap_dns || "";
+                                       }
+                               }
+                       });
+
+                       if (resolverUrl) {
+                               L.uci.set(pkg.Name, section_id, "resolver_url", resolverUrl);
+                       }
+
+                       if (bootstrapDns) {
+                               L.uci.set(pkg.Name, section_id, "bootstrap_dns", bootstrapDns);
+                       } else {
+                               L.uci.unset(pkg.Name, section_id, "bootstrap_dns");
+                       }
+               };
+               _provider.remove = function (section_id) {
+                       L.uci.unset(pkg.Name, section_id, "resolver_url");
+                       L.uci.unset(pkg.Name, section_id, "bootstrap_dns");
                };
 
-               reply.providers.forEach((prov, i) => {
-                       if (prov.http2_only && !reply.platform.http2_support) return;
-                       if (prov.http3_only && !reply.platform.http3_support) return;
-                       _provider.value(prov.template, _(prov.title));
+               function createProviderWidget(s, i, prov) {
                        if (
                                prov.params &&
                                prov.params.option &&
-                               prov.params.option.type &&
-                               prov.params.option.type === "select"
+                               prov.params.option.type
                        ) {
-                               let optName = prov.params.option.description || _("Parameter");
-                               var _paramList = s.option(form.ListValue, "_paramList_" + i, optName);
-                               _paramList.template = prov.template;
-                               _paramList.modalonly = true;
-                               if (prov.params.option.default) {
-                                       _paramList.default = prov.params.option.default;
-                               }
-                               prov.params.option.options.forEach((opt) => {
-                                       let val = opt.value || "";
-                                       let descr = opt.description || "";
-                                       _paramList.value(val, descr);
-                               });
-                               _paramList.depends("_provider", prov.template);
-                               _paramList.write = function (section_id, formvalue) {
-                                       let template = this.map.data.get(
-                                               this.map.config,
-                                               section_id,
-                                               "resolver_url"
-                                       );
-                                       if (!formvalue && _paramList.template !== template) return 0;
-                                       let resolver = pkg.templateToResolver(_paramList.template, {
-                                               option: formvalue || "",
+                               if (prov.params.option.type === "select") {
+                                       let optName = prov.params.option.description || _("Parameter");
+                                       var _paramList = s.option(form.ListValue, "_paramList_" + i, optName);
+                                       _paramList.template = prov.template;
+                                       _paramList.modalonly = true;
+                                       if (prov.params.option.default) {
+                                               _paramList.default = prov.params.option.default;
+                                       }
+                                       prov.params.option.options.forEach((opt) => {
+                                               let val = opt.value || "";
+                                               let descr = opt.description || "";
+                                               _paramList.value(val, descr);
                                        });
-                                       L.uci.set(pkg.Name, section_id, "resolver_url", resolver);
-                               };
-                               _paramList.remove = _paramList.write;
-                       } else if (
-                               prov.params &&
-                               prov.params.option &&
-                               prov.params.option.type &&
-                               prov.params.option.type === "text"
-                       ) {
-                               let optName = prov.params.option.description || _("Parameter");
-                               var _paramText = s.option(form.Value, "_paramText_" + i, optName);
-                               _paramText.template = prov.template;
-                               _paramText.modalonly = true;
-                               _paramText.depends("_provider", prov.template);
-                               _paramText.optional = !(
-                                       prov.params.option.default && prov.params.option.default !== ""
-                               );
-                               _paramText.cfgvalue = function (section_id) {
-                                       let resolver = this.map.data.get(
-                                               this.map.config,
-                                               section_id,
-                                               "resolver_url"
+                                       _paramList.depends("_provider", prov.template);
+                                       _paramList.write = function (section_id, formvalue) { };
+                                       _paramList.remove = function (section_id, formvalue) { };
+                               } else if (prov.params.option.type === "text") {
+                                       let optName = prov.params.option.description || _("Parameter");
+                                       var _paramText = s.option(form.Value, "_paramText_" + i, optName);
+                                       _paramText.template = prov.template;
+                                       _paramText.modalonly = true;
+                                       _paramText.depends("_provider", prov.template);
+                                       _paramText.optional = !(
+                                               prov.params.option.default && prov.params.option.default !== ""
                                        );
-                                       if (resolver === undefined || resolver === null) return null;
-                                       let regexp = pkg.templateToRegexp(prov.template);
-                                       let match = resolver.match(regexp);
-                                       return (match && match[1]) || null;
-                               };
-                               _paramText.write = function (section_id, formvalue) {
-                                       let template = this.map.data.get(
-                                               this.map.config,
-                                               section_id,
-                                               "resolver_url"
-                                       );
-                                       if (!formvalue && _paramText.template !== template) return 0;
-                                       let resolver = pkg.templateToResolver(_paramText.template, {
-                                               option: formvalue || "",
-                                       });
-                                       L.uci.set(pkg.Name, section_id, "resolver_url", resolver);
-                               };
-                               _paramText.remove = _paramText.write;
+                                       _paramText.cfgvalue = function (section_id) {
+                                               let resolver = this.map.data.get(
+                                                       this.map.config,
+                                                       section_id,
+                                                       "resolver_url"
+                                               );
+                                               if (resolver === undefined || resolver === null) return null;
+                                               let regexp = pkg.templateToRegexp(prov.template);
+                                               let match = resolver.match(regexp);
+                                               return (match && match[1]) || null;
+                                       };
+                                       _paramText.write = function (section_id, formvalue) { };
+                                       _paramText.remove = function (section_id, formvalue) { };
+                               }
                        }
+               }
+
+               function createBootstrapWidget(s, i, prov) {
                        const _boot_dns = s.option(
                                form.Value,
                                "_bootstrap_dns_" + i,
@@ -345,85 +524,120 @@ return view.extend({
                                );
                                return c_value || prov.bootstrap_dns || "";
                        };
-                       _boot_dns.write = function (section_id, formvalue) {
-                               const resolver = this.map.data.get(
-                                       this.map.config,
-                                       section_id,
-                                       "resolver_url"
-                               );
-                               const regexp = pkg.templateToRegexp(_boot_dns.template);
-                               if (regexp.test(resolver)) {
-                                       console.log(
-                                               pkg.Name,
-                                               section_id,
-                                               "bootstrap_dns",
-                                               formvalue,
-                                               this.cfgvalue(section_id)
-                                       );
-                                       if (formvalue)
-                                               L.uci.set(pkg.Name, section_id, "bootstrap_dns", formvalue);
-                                       else
-                                               L.uci.set(
-                                                       pkg.Name,
-                                                       section_id,
-                                                       "bootstrap_dns",
-                                                       this.cfgvalue(section_id)
-                                               );
-                               }
-                               _boot_dns.remove = _boot_dns.write;
-                       };
+                       _boot_dns.write = function (section_id, formvalue) { };
+                       _boot_dns.remove = function (section_id, formvalue) { };
+               }
+
+               reply.providers.forEach((prov, i) => {
+                       if (prov.http2_only && !reply.platform.http2_support) return;
+                       if (prov.http3_only && !reply.platform.http3_support) return;
+                       _provider.value(prov.template, _(prov.title));
+                       createProviderWidget(s, i, prov);
+                       createBootstrapWidget(s, i, prov);
                });
 
                o = s.option(form.Value, "listen_addr", _("Listen Address"));
                o.datatype = "ipaddr('nomask')";
-               o.default = "";
                o.optional = true;
                o.placeholder = "127.0.0.1";
+               o.textvalue = function (section_id) {
+                       var value = this.cfgvalue(section_id);
+                       if (value) return value;
+                       var globalValue = this.map.data.get(this.map.config, "config", "listen_addr");
+                       return globalValue || this.placeholder || _("auto");
+               };
 
                o = s.option(form.Value, "listen_port", _("Listen Port"));
                o.datatype = "port";
-               o.default = "";
                o.optional = true;
                o.placeholder = "5053";
+               o.textvalue = function (section_id) {
+                       var value = this.cfgvalue(section_id);
+                       return value || _("auto");
+               };
+
+               o = s.option(form.Value, "source_addr", _("Source (Bind To) Address"));
+               o.datatype = "ipaddr('nomask')";
+               o.optional = true;
+               o.textvalue = function (section_id) {
+                       var value = this.cfgvalue(section_id);
+                       return value || _("*");
+               };
 
                o = s.option(form.Value, "user", _("Run As User"));
-               o.default = "";
                o.modalonly = true;
                o.optional = true;
+               o.placeholder = "nobody";
 
                o = s.option(form.Value, "group", _("Run As Group"));
-               o.default = "";
                o.modalonly = true;
                o.optional = true;
+               o.placeholder = "nogroup";
 
                o = s.option(form.Value, "dscp_codepoint", _("DSCP Codepoint"));
                o.datatype = "range(0,63)";
-               o.default = "";
                o.modalonly = true;
                o.optional = true;
 
-               o = s.option(form.Value, "verbosity", _("Logging Verbosity"));
-               o.datatype = "range(0,4)";
-               o.default = "";
+               o = s.option(form.ListValue, "verbosity", _("Logging Verbosity Level"));
                o.modalonly = true;
                o.optional = true;
+               o.value("", _("0: Fatal"));
+               o.value("1", _("1: Error"));
+               o.value("2", _("2: Warning"));
+               o.value("3", _("3: Info"));
+               o.value("4", _("4: Debug"));
+               o.default = "";
 
                o = s.option(form.Value, "logfile", _("Logging File Path"));
-               o.default = "";
                o.modalonly = true;
                o.optional = true;
 
                o = s.option(form.Value, "polling_interval", _("Polling Interval"));
                o.datatype = "range(5,3600)";
-               o.default = "";
                o.modalonly = true;
                o.optional = true;
+               o.placeholder = "120";
 
                o = s.option(form.Value, "proxy_server", _("Proxy Server"));
-               o.default = "";
                o.modalonly = true;
                o.optional = true;
 
+               o = s.option(form.Value, "ca_certs_file", _("CA Certs File"));
+               o.datatype = "file";
+               o.modalonly = true;
+               o.optional = true;
+
+               o = s.option(form.Value, "conn_loss_time", _("Connection Loss Time"));
+               o.datatype = "uinteger";
+               o.modalonly = true;
+               o.optional = true;
+               o.placeholder = "15";
+
+               o = s.option(form.Value, "log_limit", _("Log Limit"));
+               o.datatype = "uinteger";
+               o.modalonly = true;
+               o.optional = true;
+               o.placeholder = "0";
+
+               o = s.option(form.Value, "max_idle_time", _("Max Idle Time"));
+               o.datatype = "uinteger";
+               o.modalonly = true;
+               o.optional = true;
+               o.placeholder = "118";
+
+               o = s.option(form.Value, "statistic_interval", _("Statistic Interval"));
+               o.datatype = "uinteger";
+               o.modalonly = true;
+               o.optional = true;
+               o.placeholder = "0";
+
+               o = s.option(form.Value, "tcp_client_limit", _("TCP Client Limit"));
+               o.datatype = "uinteger";
+               o.modalonly = true;
+               o.optional = true;
+               o.placeholder = "20";
+
                o = s.option(form.ListValue, "force_http1", _("Use HTTP/1"));
                o.modalonly = true;
                o.optional = true;
@@ -432,11 +646,16 @@ return view.extend({
                o.value("1", _("Force use of HTTP/1"));
                o.default = "";
 
-               o = s.option(
-                       form.ListValue,
-                       "force_ipv6_resolvers",
-                       _("Use IPv6 resolvers")
-               );
+               o = s.option(form.ListValue, "force_http3", _("Use HTTP/3 (QUIC)"));
+               o.modalonly = true;
+               o.optional = true;
+               o.rmempty = true;
+               o.value("", _("Use negotiated HTTP version"));
+               o.value("1", _("Force use of HTTP/3 (QUIC)"));
+               o.default = "";
+               o.depends("force_http1", "");
+
+               o = s.option(form.ListValue, "force_ipv6_resolvers", _("Use IPv6 resolvers"));
                o.modalonly = true;
                o.optional = true;
                o.rmempty = true;
index 1faad32c68702d06c285fe274ac43ca345e1c73b..9905aa033236c0811c79f6aa7d68354309b23a57 100644 (file)
@@ -9,6 +9,35 @@ msgstr ""
 msgid "%s%s%s proxy on port %s.%s"
 msgstr ""
 
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:564
+msgid "*"
+msgstr ""
+
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:251
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:585
+msgid "0: Fatal"
+msgstr ""
+
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:252
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:586
+msgid "1: Error"
+msgstr ""
+
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:253
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:587
+msgid "2: Warning"
+msgstr ""
+
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:254
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:588
+msgid "3: Info"
+msgstr ""
+
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:255
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:589
+msgid "4: Debug"
+msgstr ""
+
 #: applications/luci-app-https-dns-proxy/root/usr/share/https-dns-proxy/providers/co.oszx.dns.json:14
 #: applications/luci-app-https-dns-proxy/root/usr/share/https-dns-proxy/providers/gr.libredns.doh.json:14
 msgid "AdBlocking Filter"
@@ -74,22 +103,27 @@ msgstr ""
 msgid "BlahDNS"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:154
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:161
 msgid ""
 "Blocks access to Mozilla Encrypted resolvers, forcing local devices to use "
 "router for DNS resolution (%smore information%s)."
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:138
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:144
 msgid ""
 "Blocks access to iCloud Private Relay resolvers, forcing local devices to "
 "use router for DNS resolution (%smore information%s)."
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:335
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:514
 msgid "Bootstrap DNS"
 msgstr ""
 
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:289
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:606
+msgid "CA Certs File"
+msgstr ""
+
 #: applications/luci-app-https-dns-proxy/root/usr/share/https-dns-proxy/providers/net.cfiec.dns.json:2
 msgid "CFIEC Public IPv6 Only DNS (CN)"
 msgstr ""
@@ -98,11 +132,11 @@ msgstr ""
 msgid "CIRA Canadian Shield"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:152
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:159
 msgid "Canary Domains Mozilla"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:136
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:142
 msgid "Canary Domains iCloud"
 msgstr ""
 
@@ -122,6 +156,11 @@ msgstr ""
 msgid "Comss DNS (RU)"
 msgstr ""
 
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:293
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:611
+msgid "Connection Loss Time"
+msgstr ""
+
 #: applications/luci-app-https-dns-proxy/root/usr/share/https-dns-proxy/providers/com.controld.freedns.json:2
 msgid "ControlD"
 msgstr ""
@@ -146,7 +185,7 @@ msgstr ""
 msgid "DNSlify DNS"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:399
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:577
 msgid "DSCP Codepoint"
 msgstr ""
 
@@ -174,7 +213,7 @@ msgstr ""
 msgid "Disabling %s service"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:73
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:76
 msgid "Do not update configs"
 msgstr ""
 
@@ -228,25 +267,32 @@ msgstr ""
 msgid "Force DNS ports:"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:121
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:126
 msgid "Force Router DNS"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:130
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:145
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:164
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:135
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:151
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:171
 msgid "Force Router DNS server to all local devices"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:432
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:221
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:646
 msgid "Force use of HTTP/1"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:444
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:233
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:654
+msgid "Force use of HTTP/3 (QUIC)"
+msgstr ""
+
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:246
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:663
 msgid "Force use of IPv6 DNS resolvers"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:123
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:128
 msgid ""
 "Forces Router DNS use on local devices, also known as DNS Hijacking. Only "
 "works on `lan` interface by default (%smore information%s)."
@@ -256,6 +302,10 @@ msgstr ""
 msgid "Germany"
 msgstr ""
 
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:52
+msgid "Global Instance Options"
+msgstr ""
+
 #: applications/luci-app-https-dns-proxy/root/usr/share/https-dns-proxy/providers/google.dns.json:2
 msgid "Google"
 msgstr ""
@@ -272,7 +322,7 @@ msgstr ""
 msgid "HTTPS DNS Proxy - Configuration"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:191
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:341
 msgid "HTTPS DNS Proxy - Instances"
 msgstr ""
 
@@ -284,6 +334,18 @@ msgstr ""
 msgid "HTTPS DNS Proxy Instances"
 msgstr ""
 
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:179
+msgid "Heartbeat Domain"
+msgstr ""
+
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:194
+msgid "Heartbeat Sleep Timeout"
+msgstr ""
+
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:205
+msgid "Heartbeat Wait Timeout"
+msgstr ""
+
 #: applications/luci-app-https-dns-proxy/root/usr/share/https-dns-proxy/providers/net.he.ordns.json:2
 msgid "Hurricane Electric"
 msgstr ""
@@ -296,7 +358,7 @@ msgstr ""
 msgid "IIJ Public DNS (JP)"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:63
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:66
 msgid ""
 "If update option is selected, the %s'DNS Forwards' section of DHCP and DNS%s "
 "will be automatically updated to use selected DoH providers (%smore "
@@ -319,15 +381,15 @@ msgstr ""
 msgid "Lelux DNS (FI)"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:163
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:170
 msgid "Let local devices use Mozilla Private Relay"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:144
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:150
 msgid "Let local devices use iCloud Private Relay"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:129
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:134
 msgid "Let local devices use their own DNS servers if set"
 msgstr ""
 
@@ -335,12 +397,13 @@ msgstr ""
 msgid "LibreDNS (GR)"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:377
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:258
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:539
 #: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/status/include/71_https-dns-proxy.js:56
 msgid "Listen Address"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:383
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:550
 #: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/status/include/71_https-dns-proxy.js:57
 msgid "Listen Port"
 msgstr ""
@@ -350,18 +413,30 @@ msgstr ""
 msgid "Location"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:411
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:298
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:617
+msgid "Log Limit"
+msgstr ""
+
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:276
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:592
 msgid "Logging File Path"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:405
-msgid "Logging Verbosity"
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:249
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:582
+msgid "Logging Verbosity Level"
 msgstr ""
 
 #: applications/luci-app-https-dns-proxy/root/usr/share/https-dns-proxy/providers/com.controld.freedns.json:22
 msgid "Malware Filter"
 msgstr ""
 
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:303
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:623
+msgid "Max Idle Time"
+msgstr ""
+
 #: applications/luci-app-https-dns-proxy/root/usr/share/https-dns-proxy/providers/net.mullvad.dns.json:2
 msgid "Mullvad"
 msgstr ""
@@ -406,8 +481,8 @@ msgstr ""
 msgid "OpenNameServer"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:267
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:299
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:469
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:485
 msgid "Parameter"
 msgstr ""
 
@@ -415,8 +490,8 @@ msgstr ""
 msgid "Please %sdonate%s to support development of this project."
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:172
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:181
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:322
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:331
 msgid ""
 "Please note that %s is not supported on this system (%smore information%s)."
 msgstr ""
@@ -425,7 +500,8 @@ msgstr ""
 msgid "Poland"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:416
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:281
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:596
 msgid "Polling Interval"
 msgstr ""
 
@@ -453,11 +529,12 @@ msgstr ""
 msgid "Protective with child protection & ad-blocking"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:233
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:383
 msgid "Provider"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:422
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:286
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:602
 msgid "Proxy Server"
 msgstr ""
 
@@ -489,11 +566,13 @@ msgstr ""
 msgid "RubyFish (CN)"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:394
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:267
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:572
 msgid "Run As Group"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:389
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:263
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:567
 msgid "Run As User"
 msgstr ""
 
@@ -518,7 +597,7 @@ msgstr ""
 msgid "See the %sREADME%s for details."
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:99
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:103
 msgid "Select the DNSMASQ Configs to update"
 msgstr ""
 
@@ -530,6 +609,10 @@ msgstr ""
 msgid "Service Instances"
 msgstr ""
 
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:51
+msgid "Service Options"
+msgstr ""
+
 #: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/https-dns-proxy/status.js:191
 msgid "Service Status"
 msgstr ""
@@ -542,6 +625,14 @@ msgstr ""
 msgid "Snopyta DNS (FI)"
 msgstr ""
 
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:559
+msgid "Source (Bind To) Address"
+msgstr ""
+
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:271
+msgid "Source Address"
+msgstr ""
+
 #: applications/luci-app-https-dns-proxy/root/usr/share/https-dns-proxy/providers/net.ahadns.doh.json:22
 msgid "Spain"
 msgstr ""
@@ -564,6 +655,11 @@ msgstr ""
 msgid "Starting %s service"
 msgstr ""
 
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:308
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:629
+msgid "Statistic Interval"
+msgstr ""
+
 #: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/https-dns-proxy/status.js:370
 msgid "Stop"
 msgstr ""
@@ -580,6 +676,15 @@ msgstr ""
 msgid "Switzerland"
 msgstr ""
 
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:313
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:635
+msgid "TCP Client Limit"
+msgstr ""
+
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:181
+msgid "The domain used for connectivity checks (%smore information%s)."
+msgstr ""
+
 #: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/status/include/71_https-dns-proxy.js:105
 msgid "There are no active instances."
 msgstr ""
@@ -588,6 +693,14 @@ msgstr ""
 msgid "Tiarap Public DNS (JP)"
 msgstr ""
 
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:195
+msgid "Time to wait before checking connectivity (seconds)."
+msgstr ""
+
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:206
+msgid "Time to wait for connectivity check response (seconds)."
+msgstr ""
+
 #: applications/luci-app-https-dns-proxy/root/usr/share/https-dns-proxy/providers/net.ahadns.doh.json:18
 msgid "US/Chicago"
 msgstr ""
@@ -605,7 +718,7 @@ msgstr ""
 msgid "Unfiltered"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:229
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:379
 msgid "Unknown"
 msgstr ""
 
@@ -613,31 +726,42 @@ msgstr ""
 msgid "Unsecured"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:61
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:64
 msgid "Update DNSMASQ Config on Start/Stop"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:71
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:74
 msgid "Update all configs"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:72
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:75
 msgid "Update select configs"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:427
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:216
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:641
 msgid "Use HTTP/1"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:438
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:228
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:649
+msgid "Use HTTP/3 (QUIC)"
+msgstr ""
+
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:241
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:658
 msgid "Use IPv6 resolvers"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:443
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:245
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:662
 msgid "Use any family DNS resolvers"
 msgstr ""
 
-#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:431
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:220
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:232
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:645
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:653
 msgid "Use negotiated HTTP version"
 msgstr ""
 
@@ -675,6 +799,11 @@ msgstr ""
 msgid "Wikimedia"
 msgstr ""
 
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:547
+#: applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js:556
+msgid "auto"
+msgstr ""
+
 #: applications/luci-app-https-dns-proxy/root/usr/share/https-dns-proxy/providers/org.opennameserver.json:14
 msgid "ns1: Germany - Baden-Baden"
 msgstr ""