local m, s, o
local openclash = "openclash"
local uci = luci.model.uci.cursor()
local fs = require "luci.openclash"
local sys = require "luci.sys"
local HTTP = require "luci.http"
local DISP = require "luci.dispatcher"
local sid = arg[1]
local uuid = luci.sys.exec("cat /proc/sys/kernel/random/uuid")
local file_path = fs.get_file_path_from_request()
if not file_path then
HTTP.redirect(DISP.build_url("admin", "services", "openclash", "servers"))
return
end
-- A section written before the option names were unified keeps the credentials of
-- socks5/http/ssh/trusttunnel below auth_name/auth_pass.
for _, legacy in ipairs({{"auth_name", "username"}, {"auth_pass", "password"}}) do
local value = uci:get(openclash, sid, legacy[1])
if value then
if not uci:get(openclash, sid, legacy[2]) then
uci:set(openclash, sid, legacy[2], value)
end
uci:delete(openclash, sid, legacy[1])
end
end
font_red = [[]]
font_off = [[]]
bold_on = [[]]
bold_off = [[]]
local encrypt_methods_ss = {
-- stream
"none",
"rc4-md5",
"aes-128-cfb",
"aes-192-cfb",
"aes-256-cfb",
"aes-128-ctr",
"aes-192-ctr",
"aes-256-ctr",
"aes-128-gcm",
"aes-192-gcm",
"aes-256-gcm",
"aes-128-gcm-siv",
"aes-256-gcm-siv",
"aes-128-ccm",
"aes-192-ccm",
"aes-256-ccm",
"lea-128-gcm",
"lea-192-gcm",
"lea-256-gcm",
"chacha20",
"chacha20-ietf",
"xchacha20",
"chacha20-ietf-poly1305",
"xchacha20-ietf-poly1305",
"chacha8-ietf-poly1305",
"xchacha8-ietf-poly1305",
"2022-blake3-aes-128-gcm",
"2022-blake3-aes-256-gcm",
"2022-blake3-chacha20-poly1305",
"rabbit128-poly1305",
"aegis-128l",
"aegis-256",
"aez-384",
"deoxys-ii-256-128"
}
local securitys = {
"auto",
"none",
"zero",
"aes-128-gcm",
"chacha20-poly1305"
}
local protocols = {
"origin",
"auth_sha1_v4",
"auth_aes128_md5",
"auth_aes128_sha1",
"auth_chain_a",
"auth_chain_b"
}
local hysteria_protocols = {
"udp",
"wechat-video",
"faketcp"
}
local obfs = {
"plain",
"http_simple",
"http_post",
"random_head",
"tls1.2_ticket_auth",
"tls1.2_ticket_fastauth"
}
-- several types share one option, the select shows the values of every type it serves
local function add_option_values(option, lists)
local seen = {}
for _, list in ipairs(lists) do
for _, value in ipairs(list) do
if not seen[value] then
seen[value] = true
option:value(value)
end
end
end
end
-- A field of some node types must not be required for the others and a hidden field must
-- not keep the value of the previous type: the requirement follows the type dependencies
-- of the field, `optional` names the types whose field is optional.
local function require_by_type(option, optional)
optional = optional or {}
local parse = option.parse
function option.parse(self, section, novld)
local node_type = self.map:get(section, "type") or ""
local needed = false
for _, dependency in ipairs(self.deps) do
if dependency.type == node_type and not optional[node_type] then
needed = true
end
end
self.rmempty = not needed
return parse(self, section, novld)
end
return option
end
local function certificate(section, name, title, ...)
local option = section:option(TextValue, name, translate(title))
option.rmempty = true
option.rows = 6
for _, node_type in ipairs({...}) do
option:depends("type", node_type)
end
function option.validate(self, value)
if value then
value = value:gsub("\r\n?", "\n")
end
return value
end
return option
end
-- A key of a nested block: the label is its yaml path, the kind is the one mihomo reads,
-- `deps` shows the field for the option values that select the block and `constraint` is
-- the datatype of a Value or the values of a ListValue.
local function block_field(section, kind, name, label, deps, constraint)
local option = section:option(kind, name, translate(label))
option.rmempty = true
if constraint then
if type(constraint) == "table" then
for _, value in ipairs(constraint) do
option:value(value)
end
else
option.datatype = constraint
end
end
for key, value in pairs(deps or {}) do
option:depends(key, value)
end
return option
end
m = Map(openclash, translate("Edit Server"))
m.uci = uci
m.pageaction = false
m.redirect = DISP.build_url("admin/services/openclash/servers") .. "?file=" .. HTTP.urlencode(file_path)
if m.uci:get(openclash, sid) ~= "proxies" then
HTTP.redirect(m.redirect)
return
end
-- [[ Servers Setting ]] --
s = m:section(NamedSection, sid, "proxies")
s.anonymous = true
s.addremove = false
o = s:option(DummyValue, "server_url", "SS/SSR/VMESS/TROJAN URL")
o.rawhtml = true
o.template = "openclash/server_url"
o.value = sid
o = s:option(ListValue, "config", translate("Config File"))
o:value("all", translate("Use For All Config File"))
local e,a={}
for t,f in ipairs(fs.glob("/etc/openclash/config/*"))do
a=fs.stat(f)
if a then
e[t]={}
e[t].name=fs.basename(f)
if fs.IsYamlExt(e[t].name) then
o:value(e[t].name)
end
end
end
o = s:option(ListValue, "type", translate("Server Node Type"))
o:value("ss", translate("Shadowsocks"))
o:value("ssr", translate("ShadowsocksR"))
o:value("vmess", translate("Vmess"))
o:value("trojan", translate("trojan"))
o:value("vless", translate("Vless"))
o:value("hysteria", translate("Hysteria"))
o:value("hysteria2", translate("Hysteria2"))
o:value("wireguard", translate("WireGuard"))
o:value("tuic", translate("Tuic"))
o:value("snell", translate("Snell"))
o:value("mieru", translate("Mieru"))
o:value("anytls", translate("AnyTLS"))
o:value("sudoku", translate("Sudoku"))
o:value("socks5", translate("Socks5"))
o:value("http", translate("HTTP(S)"))
o:value("direct", translate("DIRECT"))
o:value("dns", translate("DNS"))
o:value("ssh", translate("SSH"))
o:value("masque", translate("MASQUE"))
o:value("trusttunnel", translate("TrustTunnel"))
o:value("tailscale", translate("Tailscale"))
o:value("zerotier", translate("ZeroTier"))
o:value("openvpn", translate("OpenVPN"))
o:value("shadowquic", translate("ShadowQUIC"))
o:value("gost-relay", translate("GostRelay"))
o.description = translate("Using incorrect encryption mothod may causes service fail to start")
o = s:option(Value, "name", translate("Server Alias"))
o.rmempty = false
o.default = "Server - "..sid
o = s:option(Value, "server", translate("Server Address"))
o.datatype = "host"
o.rmempty = true
o:depends("type", "ss")
o:depends("type", "ssr")
o:depends("type", "vmess")
o:depends("type", "trojan")
o:depends("type", "vless")
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o:depends("type", "wireguard")
o:depends("type", "tuic")
o:depends("type", "mieru")
o:depends("type", "anytls")
o:depends("type", "sudoku")
o:depends("type", "snell")
o:depends("type", "socks5")
o:depends("type", "http")
o:depends("type", "ssh")
o:depends("type", "masque")
o:depends("type", "trusttunnel")
o = s:option(Value, "port", translate("Server Port"))
o.datatype = "port"
o.rmempty = false
o.default = "443"
o:depends("type", "ss")
o:depends("type", "ssr")
o:depends("type", "vmess")
o:depends("type", "trojan")
o:depends("type", "vless")
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o:depends("type", "wireguard")
o:depends("type", "tuic")
o:depends("type", "mieru")
o:depends("type", "anytls")
o:depends("type", "sudoku")
o:depends("type", "snell")
o:depends("type", "socks5")
o:depends("type", "http")
o:depends("type", "ssh")
o:depends("type", "masque")
o:depends("type", "trusttunnel")
o = s:option(Flag, "flag_port_hopping", translate("Enable Port Hopping"))
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o.rmempty = true
o.default = "0"
o = s:option(Value, "ports", translate("Port Range"))
o.datatype = "portrange"
o.rmempty = true
o.default = "20000-40000"
o.placeholder = translate("20000-40000")
o:depends({type = "hysteria", flag_port_hopping = true})
o:depends({type = "hysteria2", flag_port_hopping = true})
o = s:option(Value, "password", translate("Password"))
o.password = true
o.rmempty = false
o:depends("type", "ss")
o:depends("type", "ssr")
o:depends("type", "trojan")
o:depends("type", "hysteria2")
o:depends("type", "mieru")
o:depends("type", "anytls")
o:depends("type", "tuic")
o = s:option(Value, "sudoku_key", translate("Key"))
o.rmempty = true
o.placeholder = translate("")
o:depends("type", "sudoku")
o = s:option(ListValue, "aead_method", translate("Aead-method"))
o.rmempty = true
o.default = "chacha20-poly1305"
o:value("chacha20-poly1305")
o:value("aes-128-gcm")
o:value("none")
o:depends("type", "sudoku")
o = s:option(Value, "padding_min", translate("Padding-min"))
o.rmempty = true
o.datatype = "uinteger"
o.placeholder = translate("2")
o:depends("type", "sudoku")
o = s:option(Value, "padding_max", translate("Padding-max"))
o.rmempty = true
o.datatype = "uinteger"
o.placeholder = translate("7")
o:depends("type", "sudoku")
o = s:option(ListValue, "table_type", translate("Table-type"))
o.rmempty = true
o.default = "prefer_ascii"
o:value("prefer_ascii")
o:value("prefer_entropy")
o:depends("type", "sudoku")
o = s:option(ListValue, "http_mask", translate("Http-mask"))
o.rmempty = true
o.default = "true"
o:value("true")
o:value("false")
o:depends("type", "sudoku")
o = s:option(ListValue, "sudoku_enable_pure_downlink", translate("enable-pure-downlink"))
o.rmempty = true
o:value("true")
o:value("false")
o:depends("type", "sudoku")
o = s:option(ListValue, "sudoku_http_mask_mode", translate("http-mask-mode"))
o.rmempty = true
o.default = "legacy"
o:value("legacy")
o:value("stream")
o:value("poll")
o:value("auto")
o:value("ws")
o:depends("type", "sudoku")
o = s:option(ListValue, "sudoku_http_mask_tls", translate("http-mask-tls"))
o.rmempty = true
o:value("true")
o:value("false")
o.description = translate("only for http-mask-mode stream, poll or auto")
o:depends("type", "sudoku")
o = s:option(Value, "sudoku_http_mask_host", translate("http-mask-host"))
o.rmempty = true
o.placeholder = translate("example.com:443")
o:depends("type", "sudoku")
o = s:option(ListValue, "sudoku_multiplex", translate("multiplex"))
o.rmempty = true
o.default = "off"
o:value("off")
o:value("auto")
o:value("on")
o:depends("type", "sudoku")
o = s:option(ListValue, "sudoku_http_mask_multiplex", translate("http-mask-multiplex"))
o.rmempty = true
o:value("off")
o:value("auto")
o:value("on")
o:depends("type", "sudoku")
o = s:option(Value, "sudoku_path_root", translate("path-root"))
o.rmempty = true
o.placeholder = translate("/prefix")
o:depends("type", "sudoku")
o = s:option(Value, "sudoku_custom_table", translate("custom-table"))
o.rmempty = true
o.placeholder = translate("xpxvvpvv")
o:depends("type", "sudoku")
o = s:option(DynamicList, "sudoku_custom_tables", translate("custom-tables"))
o.rmempty = true
o.placeholder = translate("xpxvvpvv")
o:depends("type", "sudoku")
o = s:option(Value, "port_range", translate("Port Range"))
o.datatype = "portrange"
o.rmempty = true
o.default = "20000-40000"
o.placeholder = translate("20000-40000")
o:depends("type", "mieru")
o = s:option(Value, "username", translate("Username"))
o.rmempty = false
o.placeholder = "user"
o:depends("type", "mieru")
o = s:option(ListValue, "transport", translate("Transport"))
o.rmempty = false
o.default = "TCP"
o:value("TCP")
o:depends("type", "mieru")
o = s:option(ListValue, "multiplexing", translate("Multiplexing"))
o.rmempty = false
o.default = "MULTIPLEXING_LOW"
o:value("MULTIPLEXING_OFF")
o:value("MULTIPLEXING_LOW")
o:value("MULTIPLEXING_MIDDLE")
o:value("MULTIPLEXING_HIGH")
o:depends("type", "mieru")
o = s:option(Value, "token", translate("Token (tuicV4 only)"))
o.rmempty = true
o:depends("type", "tuic")
o = s:option(ListValue, "udp_relay_mode", translate("UDP Relay Mode"))
o.rmempty = true
o.default = "native"
o:value("native")
o:value("quic")
o:depends("type", "tuic")
o = s:option(ListValue, "congestion_controller", translate("Congestion Controller"))
o.rmempty = true
o.default = "cubic"
o:value("cubic")
o:value("bbr")
o:value("new_reno")
o:depends("type", "tuic")
o = s:option(DynamicList, "alpn", translate("Alpn"))
o.rmempty = true
o:value("h3")
o:value("h2")
o:depends("type", "tuic")
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o:depends("type", "shadowquic")
o = s:option(ListValue, "disable_sni", translate("Disable SNI"))
o.rmempty = true
o.default = "true"
o:value("true")
o:value("false")
o:depends("type", "tuic")
o = s:option(ListValue, "reduce_rtt", translate("Reduce RTT"))
o.rmempty = true
o.default = "true"
o:value("true")
o:value("false")
o:depends("type", "tuic")
o = s:option(Value, "heartbeat_interval", translate("Heartbeat Interval"))
o.rmempty = true
o:depends("type", "tuic")
o.default = "8000"
o = s:option(Value, "request_timeout", translate("Request Timeout"))
o.rmempty = true
o.default = "8000"
o:depends("type", "tuic")
o = s:option(Value, "max_udp_relay_packet_size", translate("Max UDP Relay Packet Size"))
o.rmempty = true
o.default = "1500"
o:depends("type", "tuic")
o = s:option(Value, "max_open_streams", translate("Max Open Streams"))
o.rmempty = true
o.default = "100"
o:depends("type", "tuic")
o = s:option(Value, "ip", translate("IP"))
o.rmempty = true
o.placeholder = translate("127.0.0.1")
o.datatype = "or(ip4addr, ip6addr)"
o:depends("type", "tuic")
o:depends("type", "wireguard")
o:depends("type", "masque")
o = s:option(Value, "ipv6", translate("IPv6"))
o.rmempty = true
o.placeholder = translate("your_ipv6")
o.datatype = "ip6addr"
o:depends("type", "wireguard")
o:depends("type", "masque")
o = s:option(Value, "private_key", translate("Private Key"))
o.rmempty = true
o.placeholder = translate("eCtXsJZ27+4PbhDkHnB923tkUn2Gj59wZw5wFA75MnU=")
o:depends("type", "wireguard")
o:depends("type", "masque")
o:depends("type", "gost-relay")
o = s:option(Value, "public_key", translate("Public Key"))
o.rmempty = true
o.placeholder = translate("Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo=")
o:depends("type", "wireguard")
o:depends("type", "masque")
o = s:option(Value, "preshared_key", translate("Preshared Key"))
o.rmempty = true
o.placeholder = translate("base64")
o:depends("type", "wireguard")
o = s:option(DynamicList, "dns", translate("DNS"))
o.rmempty = true
o:value("1.1.1.1")
o:value("8.8.8.8")
o:depends("type", "wireguard")
o:depends("type", "masque")
o = s:option(Value, "mtu", translate("MTU"))
o.rmempty = true
o.default = "1420"
o.placeholder = translate("1420")
o:depends("type", "wireguard")
o:depends("type", "masque")
o = s:option(Flag, "flag_transport", translate("Enable Transport Protocol Settings"))
o:depends("type", "hysteria")
o.rmempty = true
o.default = "0"
o = s:option(ListValue, "hysteria_protocol", translate("Protocol"))
add_option_values(o, {hysteria_protocols})
o.rmempty = true
o:depends({type = "hysteria", flag_transport = true})
o = s:option(Value, "hysteria_up", translate("Uplink Capacity(Default:Mbps)"))
o.rmempty = false
o.description = translate("Required")
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o = s:option(Value, "hysteria_down", translate("Downlink Capacity(Default:Mbps)"))
o.rmempty = false
o.description = translate("Required")
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o = s:option(Value, "psk", translate("Psk"))
o.rmempty = true
o:depends("type", "snell")
o = s:option(ListValue, "snell_version", translate("Version"))
o:value("2")
o:value("3")
o:depends("type", "snell")
o = s:option(ListValue, "cipher", translate("Encrypt Method"))
add_option_values(o, {encrypt_methods_ss, securitys})
o.rmempty = true
o:depends("type", "ss")
o:depends("type", "ssr")
o:depends("type", "vmess")
o = s:option(ListValue, "protocol", translate("Protocol"))
for _, v in ipairs(protocols) do o:value(v) end
o.rmempty = true
o:depends("type", "ssr")
o = s:option(Value, "protocol_param", translate("Protocol param(optional)"))
o:depends("type", "ssr")
o = s:option(ListValue, "obfs_ssr", translate("Obfs"))
for _, v in ipairs(obfs) do o:value(v) end
o.rmempty = true
o:depends("type", "ssr")
o = s:option(Value, "obfs_param", translate("Obfs param(optional)"))
o:depends("type", "ssr")
o = s:option(Value, "alterId", translate("AlterId"))
o.default = "32"
o.rmempty = true
o:depends("type", "vmess")
o = s:option(Value, "uuid", translate("UUID"))
o.rmempty = true
o.default = uuid
o:depends("type", "vmess")
o:depends("type", "vless")
o:depends("type", "tuic")
o = s:option(ListValue, "udp", translate("UDP Enable"))
o.rmempty = true
o.default = "true"
o:value("true")
o:value("false")
o:depends("type", "ss")
o:depends("type", "ssr")
o:depends("type", "vmess")
o:depends("type", "vless")
o:depends("type", "socks5")
o:depends("type", "trojan")
o:depends({type = "snell", snell_version = "3"})
o:depends("type", "wireguard")
o:depends("type", "direct")
o:depends("type", "anytls")
o:depends("type", "masque")
o:depends("type", "trusttunnel")
o = s:option(ListValue, "udp_over_tcp", translate("udp-over-tcp"))
o.rmempty = true
o.default = "false"
o:value("true")
o:value("false")
o:depends("type", "ss")
o = s:option(ListValue, "xudp", translate("XUDP Enable"))
o.rmempty = true
o.default = "true"
o:value("true")
o:value("false")
o:depends({type = "vmess", udp = "true"})
o:depends({type = "vless", udp = "true"})
o = s:option(ListValue, "obfs", translate("obfs-mode"))
o.rmempty = true
o.default = "none"
o:value("none")
o:value("tls")
o:value("http")
o:value("websocket", translate("websocket (ws)"))
o:value("shadow-tls", translate("shadow-tls"))
o:value("restls", translate("restls"))
o:depends("type", "ss")
o = s:option(ListValue, "obfs_snell", translate("obfs-mode"))
o.rmempty = true
o.default = "none"
o:value("none")
o:value("tls")
o:value("http")
o:depends("type", "snell")
o = s:option(ListValue, "obfs_vless", translate("obfs-mode"))
o.rmempty = true
o.default = "tcp"
o:value("tcp", translate("tcp"))
o:value("ws", translate("websocket (ws)"))
o:value("grpc", translate("grpc"))
o:value("xhttp", translate("xhttp"))
o:value("http", translate("http"))
o:value("h2", translate("h2"))
o:depends("type", "vless")
o = s:option(ListValue, "obfs_vmess", translate("obfs-mode"))
o.rmempty = true
o.default = "none"
o:value("none")
o:value("websocket", translate("websocket (ws)"))
o:value("http", translate("http"))
o:value("h2", translate("h2"))
o:value("grpc", translate("grpc"))
o:value("mkcp", translate("mkcp"))
o:value("mekya", translate("mekya"))
o:depends("type", "vmess")
o = s:option(ListValue, "obfs_trojan", translate("obfs-mode"))
o.rmempty = true
o.default = "none"
o:value("none")
o:value("tcp", translate("tcp"))
o:value("ws", translate("websocket (ws)"))
o:value("grpc", translate("grpc"))
o:depends("type", "trojan")
o = s:option(Value, "host", translate("obfs-hosts"))
o.datatype = "host"
o.placeholder = translate("example.com")
o.rmempty = true
o:depends("obfs", "tls")
o:depends("obfs", "http")
o:depends("obfs", "websocket")
o:depends("obfs", "shadow-tls")
o:depends("obfs", "restls")
o:depends("obfs_snell", "tls")
o:depends("obfs_snell", "http")
o = s:option(Value, "obfs_password", translate("obfs-password"))
o.rmempty = true
o:depends("obfs", "shadow-tls")
o:depends("obfs", "restls")
o:depends("type", "hysteria2")
o = s:option(ListValue, "obfs_version_hint", translate("version-hint"))
o.rmempty = true
o:value("tls13")
o:value("tls12")
o:depends("obfs", "restls")
o = s:option(Value, "obfs_restls_script", translate("restls-script"))
o.rmempty = true
o:depends("obfs", "restls")
o.placeholder = translate("1000?100<1,500~100,350~100,600~100,400~200")
o = s:option(Value, "path", translate("path"))
o.rmempty = true
o.placeholder = translate("/")
o:depends("obfs", "websocket")
o = s:option(DynamicList, "h2_host", translate("host"))
o.rmempty = true
o.placeholder = translate("http.example.com")
o.datatype = "host"
o:depends("obfs_vmess", "h2")
o = s:option(Value, "h2_path", translate("path"))
o.rmempty = true
o.default = "/"
o:depends("obfs_vmess", "h2")
o = s:option(DynamicList, "http_path", translate("path"))
o.rmempty = true
o:value("/")
o:value("/video")
o:depends("obfs_vmess", "http")
o = s:option(Value, "custom", translate("headers"))
o.rmempty = true
o.placeholder = translate("v2ray.com")
o:depends("obfs", "websocket")
o = s:option(Value, "ws_path", translate("ws-opts-path"))
o.rmempty = true
o.placeholder = translate("/path")
o:depends("obfs_vmess", "websocket")
o:depends("obfs_vless", "ws")
o:depends("obfs_trojan", "ws")
o = s:option(DynamicList, "ws_headers", translate("ws-opts-headers"))
o.rmempty = true
o.placeholder = translate("Host: v2ray.com")
o:depends("obfs_vmess", "websocket")
o:depends("obfs_vless", "ws")
o:depends("obfs_trojan", "ws")
o = s:option(Value, "xhttp_opts_path", translate("xhttp-opts-path"))
o.rmempty = true
o.placeholder = translate("/path")
o:depends("obfs_vless", "xhttp")
o = s:option(Value, "xhttp_opts_host", translate("xhttp-opts-host"))
o.rmempty = true
o.placeholder = translate("xxx.com")
o:depends("obfs_vless", "xhttp")
-- [[ HTTP and H2 transport of vless ]]--
block_field(s, Value, "vless_http_method", "http-opts-method", {obfs_vless = "http"})
block_field(s, DynamicList, "vless_http_path", "http-opts-path", {obfs_vless = "http"})
block_field(s, DynamicList, "vless_http_headers", "http-opts-headers", {obfs_vless = "http"})
block_field(s, DynamicList, "vless_h2_host", "h2-opts-host", {obfs_vless = "h2"}, "host")
block_field(s, Value, "vless_h2_path", "h2-opts-path", {obfs_vless = "h2"})
o = s:option(Value, "vless_encryption", translate("encryption"))
o.rmempty = true
o.placeholder = translate("mlkem768x25519plus.native/xorpub/random.1rtt/0rtt.(padding len).(padding gap).(X25519 Password).(ML-KEM-768 Client)...")
o:depends("obfs_vless", "tcp")
o = s:option(Value, "vless_flow", translate("flow"))
o.rmempty = true
o.default = "xtls-rprx-direct"
o:value("xtls-rprx-direct")
o:value("xtls-rprx-origin")
o:value("xtls-rprx-vision")
o:depends("obfs_vless", "tcp")
o = s:option(Value, "grpc_service_name", translate("grpc-service-name"))
o.rmempty = true
o.datatype = "host"
o.placeholder = translate("example")
o:depends("obfs_trojan", "grpc")
o:depends("obfs_vmess", "grpc")
o:depends("obfs_vless", "grpc")
o = s:option(Value, "reality_public_key", translate("public-key(reality)"))
o.rmempty = true
o.placeholder = translate("CrrQSjAG_YkHLwvM2M-7XkKJilgL5upBKCp0od0tLhE")
o:depends("obfs_vless", "grpc")
o:depends("obfs_vless", "tcp")
o = s:option(Value, "reality_short_id", translate("short-id(reality)"))
o.rmempty = true
o.placeholder = translate("10f897e26c4b9478")
o:depends("obfs_vless", "grpc")
o:depends("obfs_vless", "tcp")
-- [[ ShadowSocks inside trojan ]]--
block_field(s, ListValue, "trojan_ss_enabled", "ss-opts-enabled", {type = "trojan"}, {"true", "false"})
block_field(s, ListValue, "trojan_ss_method", "ss-opts-method", {type = "trojan"}, {"none", "aes-128-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"})
block_field(s, Value, "trojan_ss_password", "ss-opts-password", {type = "trojan"})
o = s:option(Value, "max_early_data", translate("max-early-data"))
o.rmempty = true
o.placeholder = translate("2048")
o:depends("obfs_vmess", "websocket")
o = s:option(Value, "early_data_header_name", translate("early-data-header-name"))
o.rmempty = true
o.placeholder = translate("Sec-WebSocket-Protocol")
o:depends("obfs_vmess", "websocket")
o = s:option(ListValue, "skip_cert_verify", translate("skip-cert-verify"))
o.rmempty = true
o.default = "true"
o:value("true")
o:value("false")
o:depends("obfs", "websocket")
o:depends("obfs_vmess", "none")
o:depends("obfs_vmess", "grpc")
o:depends("obfs_vmess", "websocket")
o:depends("type", "socks5")
o:depends("type", "http")
o:depends("type", "trojan")
o:depends("type", "vless")
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o:depends("type", "tuic")
o:depends("type", "anytls")
o:depends("type", "trusttunnel")
o:depends("type", "masque")
o = s:option(ListValue, "tls", translate("TLS"))
o.rmempty = true
o.default = "false"
o:value("true")
o:value("false")
o:depends("obfs", "websocket")
o:depends("type", "vmess")
o:depends("type", "vless")
o:depends("type", "socks5")
o:depends("type", "http")
o = s:option(Value, "servername", translate("servername"))
o.rmempty = true
o.datatype = "host"
o.placeholder = translate("example.com")
o:depends({obfs_vmess = "websocket", tls = "true"})
o:depends({obfs_vmess = "grpc", tls = "true"})
o:depends({obfs_vmess = "none", tls = "true"})
o:depends("type", "vless")
block_field(s, Value, "mkcp_mtu", "mkcp-opts-mtu", {obfs_vmess = "mkcp"}, "uinteger")
block_field(s, Value, "mkcp_tti", "mkcp-opts-tti", {obfs_vmess = "mkcp"}, "uinteger")
block_field(s, Value, "mkcp_uplink_capacity", "mkcp-opts-uplink-capacity", {obfs_vmess = "mkcp"}, "uinteger")
block_field(s, Value, "mkcp_downlink_capacity", "mkcp-opts-downlink-capacity", {obfs_vmess = "mkcp"}, "uinteger")
block_field(s, ListValue, "mkcp_congestion", "mkcp-opts-congestion", {obfs_vmess = "mkcp"}, {"true", "false"})
block_field(s, Value, "mkcp_write_buffer", "mkcp-opts-write-buffer", {obfs_vmess = "mkcp"}, "uinteger")
block_field(s, Value, "mkcp_read_buffer", "mkcp-opts-read-buffer", {obfs_vmess = "mkcp"}, "uinteger")
block_field(s, Value, "mkcp_seed", "mkcp-opts-seed", {obfs_vmess = "mkcp"})
block_field(s, ListValue, "mkcp_header", "mkcp-opts-header", {obfs_vmess = "mkcp"}, {"none", "srtp", "utp", "wechat-video", "dtls", "wireguard"})
block_field(s, Value, "mekya_url", "mekya-opts-url", {obfs_vmess = "mekya"})
block_field(s, Value, "mekya_h2_pool_size", "mekya-opts-h2-pool-size", {obfs_vmess = "mekya"}, "uinteger")
block_field(s, Value, "mekya_max_write_delay", "mekya-opts-max-write-delay", {obfs_vmess = "mekya"}, "uinteger")
block_field(s, Value, "mekya_max_request_size", "mekya-opts-max-request-size", {obfs_vmess = "mekya"}, "uinteger")
block_field(s, Value, "mekya_polling_interval_initial", "mekya-opts-polling-interval-initial", {obfs_vmess = "mekya"}, "uinteger")
block_field(s, Value, "mekya_max_write_size", "mekya-opts-max-write-size", {obfs_vmess = "mekya"}, "uinteger")
block_field(s, Value, "mekya_max_write_duration_ms", "mekya-opts-max-write-duration-ms", {obfs_vmess = "mekya"}, "uinteger")
block_field(s, Value, "mekya_max_simultaneous_write_connection", "mekya-opts-max-simultaneous-write-connection", {obfs_vmess = "mekya"}, "uinteger")
block_field(s, Value, "mekya_packet_writing_buffer", "mekya-opts-packet-writing-buffer", {obfs_vmess = "mekya"}, "uinteger")
block_field(s, Value, "mekya_kcp_mtu", "mekya-opts-kcp-mtu", {obfs_vmess = "mekya"}, "uinteger")
block_field(s, Value, "mekya_kcp_tti", "mekya-opts-kcp-tti", {obfs_vmess = "mekya"}, "uinteger")
block_field(s, Value, "mekya_kcp_uplink_capacity", "mekya-opts-kcp-uplink-capacity", {obfs_vmess = "mekya"}, "uinteger")
block_field(s, Value, "mekya_kcp_downlink_capacity", "mekya-opts-kcp-downlink-capacity", {obfs_vmess = "mekya"}, "uinteger")
block_field(s, ListValue, "mekya_kcp_congestion", "mekya-opts-kcp-congestion", {obfs_vmess = "mekya"}, {"true", "false"})
block_field(s, Value, "mekya_kcp_write_buffer", "mekya-opts-kcp-write-buffer", {obfs_vmess = "mekya"}, "uinteger")
block_field(s, Value, "mekya_kcp_read_buffer", "mekya-opts-kcp-read-buffer", {obfs_vmess = "mekya"}, "uinteger")
block_field(s, Value, "mekya_kcp_seed", "mekya-opts-kcp-seed", {obfs_vmess = "mekya"})
block_field(s, ListValue, "mekya_kcp_header", "mekya-opts-kcp-header", {obfs_vmess = "mekya"}, {"none", "srtp", "utp", "wechat-video", "dtls", "wireguard"})
-- [[ TLS Mirror ]]--
block_field(s, Value, "tlsmirror_primary_key", "tlsmirror-opts-primary-key", {type = "vmess"})
block_field(s, ListValue, "tlsmirror_sequence_watermarking", "tlsmirror-opts-sequence-watermarking-enabled", {type = "vmess"}, {"true", "false"})
block_field(s, DynamicList, "tlsmirror_explicit_nonce_ciphersuites", "tlsmirror-opts-explicit-nonce-ciphersuites", {type = "vmess"})
o = s:option(Value, "keep_alive", translate("keep-alive"))
o.rmempty = true
o.default = "true"
o:value("true")
o:value("false")
o:depends("obfs_vmess", "http")
o = s:option(ListValue, "mux", translate("mux"))
o.rmempty = true
o.default = "false"
o:value("true")
o:value("false")
o:depends("obfs", "websocket")
o = s:option(Value, "sni", translate("sni"))
o.datatype = "host"
o.placeholder = translate("example.com")
o.rmempty = true
o:depends("type", "trojan")
o:depends("type", "http")
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o:depends("type", "anytls")
o:depends("type", "trusttunnel")
o:depends("type", "tuic")
o:depends("type", "masque")
o = s:option(DynamicList, "http_headers", translate("headers"))
o.rmempty = true
o.placeholder = translate("User-Agent: okhttp/3.11.0 Dalvik/2.1.0 ...... ")
o:depends("type", "http")
-- the socks5/http/ssh/trusttunnel user and password are the shared username/password fields
o = s:option(Value, "private_key", translate("private-key"))
o:depends("type", "ssh")
o:depends("type", "anytls")
o:depends("type", "http")
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o:depends("type", "socks5")
o:depends("type", "trojan")
o:depends("type", "trusttunnel")
o:depends("type", "tuic")
o:depends("type", "vless")
o:depends("type", "vmess")
o.rmempty = true
o = s:option(Value, "private_key_passphrase", translate("private-key-passphrase"))
o:depends("type", "ssh")
o.rmempty = true
o = s:option(DynamicList, "host_key", translate("host-key"))
o:depends("type", "ssh")
o.rmempty = true
o = s:option(DynamicList, "host_key_algorithms", translate("host-key-algorithms"))
o:depends("type", "ssh")
o.rmempty = true
o = s:option(DynamicList, "alpn", translate("alpn"))
o.rmempty = true
o:value("h2")
o:value("http/1.1")
o:depends("type", "trojan")
o:depends("type", "anytls")
o:depends("type", "trusttunnel")
-- the alpn list of hysteria / hysteria2 / tuic is the shared `alpn` option, see above
o = s:option(Value, "hysteria_obfs", translate("obfs"))
o.rmempty = true
o.placeholder = translate("obfs-str")
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
-- [[ hysteria_obfs_password ]]-- is the shared obfs_password option, see above
o = s:option(Value, "hysteria_auth", translate("auth"))
o.rmempty = true
o.placeholder = translate("[BASE64]")
o:depends("type", "hysteria")
o = s:option(Value, "hysteria_auth_str", translate("auth_str"))
o.rmempty = true
o.placeholder = translate("yubiyubi")
o:depends("type", "hysteria")
o = s:option(Flag, "flag_quicparam", translate("Hysterir QUIC parameters"))
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o.rmempty = true
o.default = "0"
o = s:option(Value, "recv_window_conn", translate("recv_window_conn"))
o.rmempty = true
o.placeholder = translate("QUIC stream receive window")
o.datatype = "uinteger"
o:depends({type = "hysteria", flag_quicparam = true})
o = s:option(Value, "recv_window", translate("recv_window"))
o.rmempty = true
o.placeholder = translate("QUIC connection receive window")
o.datatype = "uinteger"
o:depends({type = "hysteria", flag_quicparam = true})
o = s:option(Value, "initial_stream_receive_window", translate("initial_stream_receive_window"))
o.rmempty = true
o.placeholder = translate("QUIC init stream receive window")
o.datatype = "uinteger"
o:depends({type = "hysteria2", flag_quicparam = true})
o = s:option(Value, "max_stream_receive_window", translate("max_stream_receive_window"))
o.rmempty = true
o.placeholder = translate("QUIC max stream receive window")
o.datatype = "uinteger"
o:depends({type = "hysteria2", flag_quicparam = true})
o = s:option(Value, "initial_connection_receive_window", translate("initial_connection_receive_window"))
o.rmempty = true
o.placeholder = translate("QUIC init connection receive window")
o.datatype = "uinteger"
o:depends({type = "hysteria2", flag_quicparam = true})
o = s:option(Value, "max_connection_receive_window", translate("max_connection_receive_window"))
o.rmempty = true
o.placeholder = translate("QUIC max connection receive window")
o.datatype = "uinteger"
o:depends({type = "hysteria2", flag_quicparam = true})
o = s:option(Value, "hop_interval", translate("Hop Interval (Unit:second)"))
o.rmempty = true
o.default = "10"
o:depends({type = "hysteria", flag_transport = true, flag_port_hopping = true})
o:depends({type = "hysteria2", flag_port_hopping = true})
o = s:option(ListValue, "disable_mtu_discovery", translate("disable_mtu_discovery"))
o.rmempty = true
o:value("true")
o:value("false")
o.default = "false"
o:depends({type = "hysteria", flag_quicparam = true})
o = s:option(ListValue, "packet_addr", translate("Packet-Addr"))
o.rmempty = true
o.default = "true"
o:value("true")
o:value("false")
o:depends({type = "vless", xudp = "false"})
o:depends({type = "vmess", xudp = "false"})
o = s:option(Value, "packet_encoding", translate("Packet-Encoding"))
o.rmempty = true
o:depends("type", "vmess")
o:depends("type", "vless")
o = s:option(ListValue, "global_padding", translate("Global-Padding"))
o.rmempty = true
o.default = "false"
o:value("true")
o:value("false")
o:depends("type", "vmess")
o = s:option(ListValue, "authenticated_length", translate("Authenticated-Length"))
o.rmempty = true
o.default = "false"
o:value("true")
o:value("false")
o:depends("type", "vmess")
-- [[ AnyTLS ]] --
o = s:option(Value, "idle_session_check_interval", translate("idle-session-check-interval"))
o.rmempty = true
o.default = "30"
o:depends("type", "anytls")
o = s:option(Value, "idle_session_timeout", translate("idle-session-timeout"))
o.rmempty = true
o.default = "30"
o:depends("type", "anytls")
o = s:option(Value, "min_idle_session", translate("min-idle-session"))
o.rmempty = true
o.default = "0"
o:depends("type", "anytls")
-- [[ MASQUE ]] --
o = s:option(ListValue, "remote_dns_resolve", translate("Remote DNS Resolve"))
o.rmempty = true
o.default = "true"
o:value("true")
o:value("false")
o:depends("type", "masque")
o = s:option(ListValue, "masque_network", translate("network"))
o.rmempty = true
o:value("h3")
o:value("h2")
o:value("h3-l4proxy")
o:depends("type", "masque")
-- [[ TrustTunnel ]] --
o = s:option(ListValue, "trusttunnel_health_check", translate("Health Check"))
o.rmempty = true
o.default = "true"
o:value("true")
o:value("false")
o:depends("type", "trusttunnel")
o = s:option(ListValue, "trusttunnel_quic", translate("QUIC"))
o.rmempty = true
o.default = "false"
o:value("true")
o:value("false")
o:depends("type", "trusttunnel")
o = s:option(ListValue, "trusttunnel_congestion_controller", translate("Congestion Controller"))
o.rmempty = true
o.default = "bbr"
o:value("bbr")
o:depends("type", "trusttunnel")
o = s:option(Value, "trusttunnel_max_connections", translate("max-connections"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "trusttunnel")
o = s:option(Value, "trusttunnel_min_streams", translate("min-streams"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "trusttunnel")
o = s:option(Value, "trusttunnel_max_streams", translate("max-streams"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "trusttunnel")
-- [[ Fast Open ]]--
o = s:option(ListValue, "fast_open", translate("Fast Open"))
o.rmempty = true
o.default = "true"
o:value("true")
o:value("false")
o:depends("type", "hysteria")
o:depends("type", "tuic")
o = s:option(ListValue, "tfo", translate("TFO"))
o.rmempty = true
o.default = "false"
o:value("true")
o:value("false")
o:depends("type", "http")
o:depends("type", "socks5")
o:depends("type", "trojan")
o:depends("type", "vless")
o:depends("type", "vmess")
o:depends("type", "ss")
o:depends("type", "ssr")
o:depends("type", "snell")
o = s:option(Value, "fingerprint", translate("Fingerprint"))
o.rmempty = true
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o:depends("type", "socks5")
o:depends("type", "http")
o:depends("type", "trojan")
o:depends("type", "vless")
o:depends("type", "anytls")
o:depends("type", "trusttunnel")
o:depends("type", "tuic")
o:depends({type = "ss", obfs = "websocket"})
o:depends({type = "ss", obfs = "shadow-tls"})
o:depends({type = "vmess", obfs_vmess = "websocket"})
o:depends({type = "vmess", obfs_vmess = "h2"})
o:depends({type = "vmess", obfs_vmess = "grpc"})
o = s:option(ListValue, "client_fingerprint", translate("Client Fingerprint"))
o.rmempty = true
o:value("none")
o:value("random")
o:value("chrome")
o:value("firefox")
o:value("safari")
o:value("ios")
o.default = "none"
o:depends("type", "vless")
o:depends({type = "ss", obfs = "restls"})
o:depends({type = "ss", obfs = "shadow-tls"})
o:depends({type = "trojan", obfs_vmess = "grpc"})
o:depends({type = "vmess", obfs_vmess = "websocket"})
o:depends({type = "vmess", obfs_vmess = "http"})
o:depends({type = "vmess", obfs_vmess = "h2"})
o:depends({type = "vmess", obfs_vmess = "grpc"})
o:depends("type", "anytls")
o:depends("type", "trusttunnel")
-- [[ ip version ]]--
o = s:option(ListValue, "ip_version", translate("IP Version"))
o.rmempty = true
o:value("dual")
o:value("ipv4")
o:value("ipv4-prefer")
o:value("ipv6")
o:value("ipv6-prefer")
o.default = "ipv4-prefer"
o:depends("type", "ss")
o:depends("type", "ssr")
o:depends("type", "vmess")
o:depends("type", "trojan")
o:depends("type", "vless")
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o:depends("type", "wireguard")
o:depends("type", "tuic")
o:depends("type", "mieru")
o:depends("type", "snell")
o:depends("type", "socks5")
o:depends("type", "http")
o:depends("type", "ssh")
o:depends("type", "direct")
o = s:option(ListValue, "multiplex", translate("Multiplex"))
o.rmempty = false
o:value("true")
o:value("false")
o.default = "false"
o:depends({type = "ss", obfs = "none"})
o = s:option(ListValue, "multiplex_protocol", translate("Protocol"))
o.rmempty = true
o:value("smux")
o:value("yamux")
o:value("h2mux")
o.default = "smux"
o:depends("multiplex", "true")
o = s:option(Value, "multiplex_max_connections", translate("Max-connections"))
o.rmempty = true
o.placeholder = "4"
o.default = "4"
o.datatype = "uinteger"
o:depends("multiplex", "true")
o = s:option(Value, "multiplex_min_streams", translate("Min-streams"))
o.rmempty = true
o.placeholder = "4"
o.default = "4"
o.datatype = "uinteger"
o:depends("multiplex", "true")
o = s:option(Value, "multiplex_max_streams", translate("Max-streams"))
o.rmempty = true
o.placeholder = "0"
o.default = "0"
o.datatype = "uinteger"
o:depends("multiplex", "true")
o = s:option(ListValue, "multiplex_padding", translate("Padding"))
o.rmempty = false
o:value("true")
o:value("false")
o.default = "false"
o:depends("multiplex", "true")
o = s:option(ListValue, "multiplex_statistic", translate("Statistic"))
o.rmempty = false
o:value("true")
o:value("false")
o.default = "false"
o:depends("multiplex", "true")
o = s:option(ListValue, "multiplex_only_tcp", translate("Only-tcp"))
o.rmempty = false
o:value("true")
o:value("false")
o.default = "false"
o:depends("multiplex", "true")
-- [[ Fields mihomo accepts that had no uci option before ]] --
o = s:option(ListValue, "udp_over_tcp_version", translate("udp-over-tcp-version"))
o.rmempty = true
o:value("1")
o:value("2")
o:depends("type", "ss")
o = s:option(Flag, "ws_opts_v2ray_http_upgrade", translate("v2ray-http-upgrade"))
o.rmempty = true
o.default = "0"
o:depends("obfs_vmess", "websocket")
o:depends("obfs_vless", "ws")
o:depends("obfs_trojan", "ws")
o = s:option(Flag, "ws_opts_v2ray_http_upgrade_fast_open", translate("v2ray-http-upgrade-fast-open"))
o.rmempty = true
o.default = "0"
o:depends("obfs_vmess", "websocket")
o:depends("obfs_vless", "ws")
o:depends("obfs_trojan", "ws")
o = s:option(Flag, "reality_support_x25519mlkem768", translate("support-x25519mlkem768"))
o.rmempty = true
o.default = "0"
o:depends("obfs_vless", "tcp")
o:depends("obfs_vless", "grpc")
o = s:option(Value, "name_cert_verify", translate("name-cert-verify"))
o.rmempty = true
o.placeholder = translate("example.com")
o:depends("type", "vmess")
o:depends("type", "vless")
o:depends("type", "trojan")
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o:depends("type", "tuic")
o:depends("type", "anytls")
o:depends("type", "masque")
o:depends("type", "trusttunnel")
o:depends("type", "socks5")
o:depends("type", "http")
o = s:option(Value, "hysteria_up_speed", translate("up-speed (Mbps)"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "hysteria")
o = s:option(Value, "hysteria_down_speed", translate("down-speed (Mbps)"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "hysteria")
o = s:option(ListValue, "hysteria_obfs_protocol", translate("obfs-protocol"))
o.rmempty = true
o:value("salamander")
o:depends("type", "hysteria")
o = s:option(Value, "hysteria_obfs_min_packet_size", translate("obfs-min-packet-size"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "hysteria2")
o = s:option(Value, "hysteria_obfs_max_packet_size", translate("obfs-max-packet-size"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "hysteria2")
o = s:option(Value, "cwnd", translate("cwnd"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "hysteria2")
o:depends("type", "tuic")
o:depends("type", "masque")
o:depends("type", "trusttunnel")
o = s:option(ListValue, "bbr_profile", translate("bbr-profile"))
o.rmempty = true
o:value("conservative")
o:value("standard")
o:value("aggressive")
o:depends("type", "hysteria2")
o:depends("type", "tuic")
o:depends("type", "masque")
o:depends("type", "trusttunnel")
o = s:option(Value, "udp_mtu", translate("udp-mtu"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "hysteria2")
o = s:option(Value, "handshake_timeout", translate("handshake-timeout"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "hysteria2")
o:depends("type", "masque")
o = s:option(Value, "max_datagram_frame_size", translate("max-datagram-frame-size"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "tuic")
o = s:option(Flag, "udp_over_stream", translate("udp-over-stream"))
o.rmempty = true
o.default = "0"
o:depends("type", "tuic")
o = s:option(Value, "udp_over_stream_version", translate("udp-over-stream-version"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "tuic")
o = s:option(Value, "persistent_keepalive", translate("persistent-keepalive"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "wireguard")
o = s:option(Value, "workers", translate("workers"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "wireguard")
o = s:option(DynamicList, "reserved", translate("reserved"))
o.rmempty = true
o.datatype = "uinteger"
o.placeholder = translate("0, 0, 0")
o:depends("type", "wireguard")
o = s:option(ListValue, "ip_stack", translate("ip-stack"))
o.rmempty = true
o:value("system")
o:value("gvisor")
o:value("mixed")
o:depends("type", "wireguard")
o:depends("type", "masque")
o = s:option(Value, "refresh_server_ip_interval", translate("refresh-server-ip-interval"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "wireguard")
block_field(s, Value, "amnezia_version", "amnezia-wg-option-version", {type = "wireguard"}, "uinteger")
block_field(s, Value, "amnezia_jc", "amnezia-wg-option-jc", {type = "wireguard"}, "uinteger")
block_field(s, Value, "amnezia_jmin", "amnezia-wg-option-jmin", {type = "wireguard"}, "uinteger")
block_field(s, Value, "amnezia_jmax", "amnezia-wg-option-jmax", {type = "wireguard"}, "uinteger")
block_field(s, Value, "amnezia_s1", "amnezia-wg-option-s1", {type = "wireguard"}, "uinteger")
block_field(s, Value, "amnezia_s2", "amnezia-wg-option-s2", {type = "wireguard"}, "uinteger")
block_field(s, Value, "amnezia_s3", "amnezia-wg-option-s3", {type = "wireguard"}, "uinteger")
block_field(s, Value, "amnezia_s4", "amnezia-wg-option-s4", {type = "wireguard"}, "uinteger")
block_field(s, Value, "amnezia_h1", "amnezia-wg-option-h1", {type = "wireguard"})
block_field(s, Value, "amnezia_h2", "amnezia-wg-option-h2", {type = "wireguard"})
block_field(s, Value, "amnezia_h3", "amnezia-wg-option-h3", {type = "wireguard"})
block_field(s, Value, "amnezia_h4", "amnezia-wg-option-h4", {type = "wireguard"})
o = s:option(Flag, "anytls_disable_reuse", translate("disable-reuse"))
o.rmempty = true
o.default = "0"
o:depends("type", "anytls")
o = s:option(Value, "anytls_client_metadata", translate("client-metadata"))
o.rmempty = true
o:depends("type", "anytls")
o = s:option(ListValue, "mieru_handshake_mode", translate("handshake-mode"))
o.rmempty = true
o:value("HANDSHAKE_STANDARD")
o:value("HANDSHAKE_NO_WAIT")
o:depends("type", "mieru")
o = s:option(Value, "mieru_traffic_pattern", translate("traffic-pattern"))
o.rmempty = true
o:depends("type", "mieru")
o = s:option(Flag, "snell_reuse", translate("reuse"))
o.rmempty = true
o.default = "0"
o:depends("type", "snell")
o = s:option(Value, "uri", translate("uri"))
o.rmempty = true
o:depends("type", "masque")
-- [[ Tailscale ]] --
o = s:option(Value, "hostname", translate("hostname"))
o.rmempty = true
o.placeholder = translate("openclash-node")
o:depends("type", "tailscale")
o = s:option(Value, "auth_key", translate("auth-key"))
o.rmempty = true
o.password = true
o.placeholder = translate("tskey-auth-xxxxxxxxxxxxxxxx")
o:depends("type", "tailscale")
o = s:option(Value, "control_url", translate("control-url"))
o.rmempty = true
o.placeholder = translate("https://controlplane.tailscale.com")
o:depends("type", "tailscale")
o = s:option(Value, "exit_node", translate("exit-node"))
o.rmempty = true
o.placeholder = translate("100.64.0.1")
o:depends("type", "tailscale")
o = s:option(ListValue, "ephemeral", translate("ephemeral"))
o.rmempty = true
o.default = "false"
o:value("true")
o:value("false")
o:depends("type", "tailscale")
o = s:option(ListValue, "accept_routes", translate("accept-routes"))
o.rmempty = true
o.default = "false"
o:value("true")
o:value("false")
o:depends("type", "tailscale")
o = s:option(ListValue, "exit_node_allow_lan_access", translate("exit-node-allow-lan-access"))
o.rmempty = true
o.default = "false"
o:value("true")
o:value("false")
o:depends("type", "tailscale")
-- [[ ZeroTier ]] --
o = s:option(Value, "network", translate("network"))
o.rmempty = true
o.placeholder = translate("8056c2e21c000001")
o:depends("type", "zerotier")
o = s:option(Value, "planet", translate("planet"))
o.rmempty = true
o.placeholder = translate("/etc/openclash/planet")
o.description = font_red..bold_on..translate("A custom planet file. An orbit (a world and its seed) belongs to Other Parameters")..bold_off..font_off
o:depends("type", "zerotier")
o = s:option(Value, "state_dir", translate("state-dir"))
o.rmempty = true
o.placeholder = translate("/etc/openclash/state")
o:depends("type", "tailscale")
o:depends("type", "zerotier")
o = s:option(Value, "physical_mtu", translate("physical-mtu"))
o.rmempty = true
o.datatype = "uinteger"
o.placeholder = translate("1400")
o:depends("type", "zerotier")
o = s:option(Value, "primary_port", translate("primary-port"))
o.rmempty = true
o.datatype = "port"
o.placeholder = translate("9993")
o:depends("type", "zerotier")
o = s:option(Value, "secondary_port", translate("secondary-port"))
o.rmempty = true
o.datatype = "port"
o.placeholder = translate("9993")
o:depends("type", "zerotier")
o = s:option(Value, "tcp_fallback_relay", translate("tcp-fallback-relay"))
o.rmempty = true
o.placeholder = translate("host:port")
o:depends("type", "zerotier")
o = s:option(ListValue, "tcp_fallback_mode", translate("tcp-fallback-mode"))
o.rmempty = true
o:value("auto")
o:value("force")
o:value("disable")
o.description = translate("auto: relay only when the direct path fails, force: always relay, disable: off")
o:depends("type", "zerotier")
o = s:option(ListValue, "low_bandwidth", translate("low-bandwidth"))
o.rmempty = true
o.default = "false"
o:value("true")
o:value("false")
o:depends("type", "zerotier")
o = s:option(ListValue, "encrypted_hello", translate("encrypted-hello"))
o.rmempty = true
o.default = "false"
o:value("true")
o:value("false")
o:depends("type", "zerotier")
o = s:option(Value, "remote_trace_target", translate("remote-trace-target"))
o.rmempty = true
o:depends("type", "zerotier")
o = s:option(Value, "remote_trace_level", translate("remote-trace-level"))
o.rmempty = true
o.datatype = "uinteger"
o.placeholder = translate("0")
o:depends("type", "zerotier")
-- [[ OpenVPN ]] --
o = s:option(ListValue, "proto", translate("proto"))
o.rmempty = true
o.default = "udp"
o:value("udp")
o:value("tcp")
o:depends("type", "openvpn")
o = s:option(Value, "dev", translate("dev"))
o.rmempty = true
o.placeholder = translate("tun")
o:depends("type", "openvpn")
o = s:option(Value, "cipher", translate("cipher"))
o.rmempty = true
o.placeholder = translate("AES-256-GCM")
o:depends("type", "openvpn")
o = s:option(DynamicList, "data_ciphers", translate("data-ciphers"))
o.rmempty = true
o.placeholder = translate("AES-256-GCM")
o:depends("type", "openvpn")
o = s:option(Value, "data_ciphers_fallback", translate("data-ciphers-fallback"))
o.rmempty = true
o.placeholder = translate("AES-256-CBC")
o:depends("type", "openvpn")
o = s:option(Value, "auth", translate("auth"))
o.rmempty = true
o.placeholder = translate("SHA256")
o:depends("type", "openvpn")
o = s:option(ListValue, "comp_lzo", translate("comp-lzo"))
o.rmempty = true
o:value("no")
o:value("yes")
o:value("adaptive")
o:depends("type", "openvpn")
o = s:option(ListValue, "key_direction", translate("key-direction"))
o.rmempty = true
o:value("0")
o:value("1")
o:depends("type", "openvpn")
o = s:option(Value, "ping", translate("ping"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "openvpn")
o = s:option(Value, "ping_restart", translate("ping-restart"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "openvpn")
o = s:option(Value, "tran_window", translate("tran-window"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "openvpn")
o = s:option(DynamicList, "peer_info", translate("peer-info"))
o.rmempty = true
o.placeholder = translate("key: value")
o:depends("type", "openvpn")
certificate(s, "ca", "ca", "openvpn")
certificate(s, "cert", "cert", "openvpn")
certificate(s, "key", "key", "openvpn")
certificate(s, "tls_auth", "tls-auth", "openvpn")
certificate(s, "tls_crypt", "tls-crypt", "openvpn")
certificate(s, "tls_crypt_v2", "tls-crypt-v2", "openvpn")
-- [[ ShadowQUIC ]] --
o = s:option(Value, "keep_alive_interval", translate("keep-alive-interval"))
o.rmempty = true
o.datatype = "uinteger"
o:depends("type", "shadowquic")
o = s:option(ListValue, "zero_rtt", translate("zero-rtt"))
o.rmempty = true
o.default = "false"
o:value("true")
o:value("false")
o:depends("type", "shadowquic")
o = s:option(DynamicList, "quic_versions", translate("quic-versions"))
o.rmempty = true
o.placeholder = translate("v1")
o:depends("type", "shadowquic")
-- [[ GostRelay ]] --
o = s:option(ListValue, "forward", translate("forward"))
o.rmempty = true
o.default = "false"
o:value("true")
o:value("false")
o:depends("type", "gost-relay")
certificate(s, "certificate", "certificate", "gost-relay", "anytls", "http", "hysteria", "hysteria2", "socks5", "trojan", "trusttunnel", "tuic", "vless", "vmess")
o = s:option(Value, "interface_name", translate("interface-name"))
o.rmempty = true
o.placeholder = translate("eth0")
o:depends("type", "ss")
o:depends("type", "ssr")
o:depends("type", "vmess")
o:depends("type", "trojan")
o:depends("type", "vless")
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o:depends("type", "wireguard")
o:depends("type", "tuic")
o:depends("type", "mieru")
o:depends("type", "snell")
o:depends("type", "socks5")
o:depends("type", "http")
o:depends("type", "ssh")
o:depends("type", "direct")
o = s:option(Value, "routing_mark", translate("routing-mark"))
o.rmempty = true
o.placeholder = translate("2333")
o:depends("type", "ss")
o:depends("type", "ssr")
o:depends("type", "vmess")
o:depends("type", "trojan")
o:depends("type", "vless")
o:depends("type", "hysteria")
o:depends("type", "hysteria2")
o:depends("type", "wireguard")
o:depends("type", "tuic")
o:depends("type", "mieru")
o:depends("type", "snell")
o:depends("type", "socks5")
o:depends("type", "http")
o:depends("type", "ssh")
o:depends("type", "direct")
o = s:option(Value, "other_parameters", translate("Other Parameters"))
o.template = "cbi/tvalue"
o.rows = 20
o.wrap = "off"
o.description = font_red..bold_on..translate("Edit Your Other Parameters Here")..bold_off..font_off
o.rmempty = true
function o.cfgvalue(self, section)
if self.map:get(section, "other_parameters") == nil then
return "# Example:\n"..
"# Only support YAML, four spaces need to be reserved at the beginning of each line to maintain formatting alignment\n"..
"# 示例:\n"..
"# 仅支持 YAML, 每行行首需要多保留四个空格以使脚本处理后能够与上方配置保持格式对齐\n"..
"# type: ss\n"..
"# server: \"127.0.0.1\"\n"..
"# port: 443\n"..
"# cipher: rc4-md5\n"..
"# password: \"123456\"\n"..
"# udp: true\n"..
"# udp-over-tcp: false\n"..
"# ip-version: \"dual\"\n"..
"# tfo: true\n"..
"# smux:\n"..
"# enabled: false\n"..
"# plugin-opts:\n"..
"# mode: tls\n"..
"# host: world.taobao.com"
else
return Value.cfgvalue(self, section)
end
end
function o.validate(self, value)
if value then
value = value:gsub("\r\n?", "\n")
value = value:gsub("%c*$", "")
end
return value
end
o = s:option(Value, "dialer_proxy", translate("Dialer-proxy"))
o.rmempty = true
o.description = font_red..bold_on..translate("The added Dialer Proxy or Group Must Exist")..bold_off..font_off
m.uci:foreach("openclash", "proxy_groups",
function(s)
if s.name ~= "" and s.name ~= nil and (s.config == m.uci:get(openclash, sid, "config") or s.config == "all") then
o:value(s.name)
end
end)
o = s:option(DynamicList, "groups", translate("Proxy Group (Support Regex)"))
o.description = font_red..bold_on..translate("The added Proxy Groups Must Exist")..bold_off..font_off
o.rmempty = true
o:value("all", translate("All Groups"))
m.uci:foreach("openclash", "proxy_groups",
function(s)
if s.name ~= "" and s.name ~= nil and (s.config == m.uci:get(openclash, sid, "config") or s.config == "all") then
o:value(s.name)
end
end)
-- [[ Fields the node types above share ]] --
-- The types added later take part in the fields declared above instead of repeating them,
-- so a dependency of such a field is declared here.
local shared_fields = {
server = {"openvpn", "shadowquic", "gost-relay"},
port = {"openvpn", "shadowquic", "gost-relay"},
udp = {"tailscale", "zerotier", "openvpn", "gost-relay"},
tfo = {"tailscale", "zerotier", "openvpn", "shadowquic", "gost-relay"},
ip_version = {"tailscale", "zerotier", "openvpn", "shadowquic", "gost-relay"},
interface_name = {"tailscale", "zerotier", "openvpn", "shadowquic", "gost-relay"},
routing_mark = {"tailscale", "zerotier", "openvpn", "shadowquic", "gost-relay"},
dns = {"zerotier", "openvpn"},
mtu = {"openvpn", "zerotier"},
ip_stack = {"openvpn", "zerotier"},
remote_dns_resolve = {"zerotier", "openvpn", "wireguard"},
handshake_timeout = {"openvpn"},
username = {"socks5", "http", "ssh", "trusttunnel", "openvpn", "shadowquic", "gost-relay"},
password = {"socks5", "http", "ssh", "trusttunnel", "openvpn", "shadowquic", "gost-relay"},
tls = {"gost-relay"},
skip_cert_verify = {"gost-relay"},
sni = {"shadowquic", "gost-relay"},
fingerprint = {"gost-relay"},
client_fingerprint = {"gost-relay"},
name_cert_verify = {"gost-relay"},
mux = {"gost-relay"},
hysteria_up = {"shadowquic"},
hysteria_down = {"shadowquic"},
cwnd = {"shadowquic"},
bbr_profile = {"shadowquic"},
congestion_controller = {"shadowquic"},
recv_window = {"shadowquic"},
recv_window_conn = {"shadowquic"},
disable_mtu_discovery = {"shadowquic"},
max_open_streams = {"shadowquic"},
max_datagram_frame_size = {"shadowquic"},
udp_over_stream = {"shadowquic"}
}
for name, types in pairs(shared_fields) do
local field = s.fields[name]
if field then
for _, node_type in ipairs(types) do
field:depends("type", node_type)
end
end
end
-- A password is a field of every type above, but only some of them need one
require_by_type(s.fields.port)
require_by_type(s.fields.hysteria_up)
require_by_type(s.fields.hysteria_down)
require_by_type(s.fields.username, {socks5 = true, http = true, ssh = true, trusttunnel = true, openvpn = true, shadowquic = true, ["gost-relay"] = true})
require_by_type(s.fields.password, {socks5 = true, http = true, ssh = true, trusttunnel = true, openvpn = true, shadowquic = true, ["gost-relay"] = true})
local t = {
{Commit, Back}
}
a = m:section(Table, t)
o = a:option(Button,"Commit", " ")
o.inputtitle = translate("Commit Settings")
o.inputstyle = "apply"
o.write = function()
m.uci:commit(openclash)
HTTP.redirect(m.redirect)
end
o = a:option(Button,"Back", " ")
o.inputtitle = translate("Back Settings")
o.inputstyle = "reset"
o.write = function()
m.uci:revert(openclash, sid)
HTTP.redirect(m.redirect)
end
m:append(Template("openclash/config_editor"))
return m