diff --git a/apps/emqx/src/emqx_limiter/src/emqx_limiter_schema.erl b/apps/emqx/src/emqx_limiter/src/emqx_limiter_schema.erl
index 8421a7399..498f862e0 100644
--- a/apps/emqx/src/emqx_limiter/src/emqx_limiter_schema.erl
+++ b/apps/emqx/src/emqx_limiter/src/emqx_limiter_schema.erl
@@ -114,10 +114,10 @@ fields(limiter_opts) ->
];
fields(bucket_opts) ->
- [ {rate, sc(rate(), #{desc => "The rate for this bucket"})}
- , {capacity, sc(capacity(), #{desc => "The maximum number of tokens for this bucket"})}
+ [ {rate, sc(rate(), #{desc => "Rate for this bucket."})}
+ , {capacity, sc(capacity(), #{desc => "The maximum number of tokens for this bucket."})}
, {initial, sc(initial(), #{default => "0",
- desc => "The initial number of tokens for this bucket"})}
+ desc => "The initial number of tokens for this bucket."})}
, {per_client, sc(ref(client_bucket),
#{default => #{},
desc => "The rate limit for each user of the bucket,"
@@ -126,8 +126,8 @@ fields(bucket_opts) ->
];
fields(client_bucket) ->
- [ {rate, sc(rate(), #{default => "infinity"})}
- , {initial, sc(initial(), #{default => "0"})}
+ [ {rate, sc(rate(), #{default => "infinity", desc => "Rate for this bucket."})}
+ , {initial, sc(initial(), #{default => "0", desc => "The initial number of tokens for this bucket."})}
%% low_water_mark add for emqx_channel and emqx_session
%% both modules consume first and then check
%% so we need to use this value to prevent excessive consumption
diff --git a/apps/emqx/src/emqx_schema.erl b/apps/emqx/src/emqx_schema.erl
index 4219f8463..065c9b54b 100644
--- a/apps/emqx/src/emqx_schema.erl
+++ b/apps/emqx/src/emqx_schema.erl
@@ -100,6 +100,11 @@
-elvis([{elvis_style, god_modules, disable}]).
+-define(IDLE_TIMOUT_DESC,
+ "Close transport-layer connections from the clients that have not sent MQTT CONNECT\n"
+ "message within this interval."
+).
+
namespace() -> undefined.
roots() ->
@@ -786,7 +791,7 @@ fields("force_gc") ->
{"enable",
sc(
boolean(),
- #{default => true}
+ #{default => true, desc => "Enable forced garbage collection."}
)},
{"count",
sc(
@@ -905,24 +910,24 @@ fields("mqtt_quic_listener") ->
{"enabled",
sc(
boolean(),
- #{default => true}
+ #{default => true, desc => "Enable QUIC listener."}
)},
%% TODO: ensure cacertfile is configurable
{"certfile",
sc(
string(),
- #{}
+ #{desc => "Path to the certificate."}
)},
{"keyfile",
sc(
string(),
- #{}
+ #{desc => "Path to the secret key file."}
)},
{"ciphers", ciphers_schema(quic)},
{"idle_timeout",
sc(
duration(),
- #{default => "15s"}
+ #{default => "15s", desc => ?IDLE_TIMOUT_DESC}
)}
] ++ base_listener();
fields("ws_opts") ->
@@ -957,9 +962,7 @@ fields("ws_opts") ->
duration(),
#{
default => "15s",
- desc =>
- "The idle time after the TCP connection is established
\n"
- " If no packets are received within this time, the connection will be closed."
+ desc => ?IDLE_TIMOUT_DESC
}
)},
{"max_frame_size",
@@ -1652,17 +1655,30 @@ mqtt_listener() ->
{"access_rules",
sc(
hoconsc:array(string()),
- #{}
+ #{
+ desc =>
+ "The access control rules for this listener.
"
+ "See: https://github.com/emqtt/esockd#allowdeny"
+ }
)},
{"proxy_protocol",
sc(
boolean(),
- #{default => false}
+ #{
+ default => false,
+ desc =>
+ "Enable the Proxy Protocol V1/2 if the EMQX cluster is deployed\n"
+ " behind HAProxy or Nginx.
"
+ "See: https://www.haproxy.com/blog/haproxy/proxy-protocol/"
+ }
)},
{"proxy_protocol_timeout",
sc(
duration(),
- #{}
+ #{
+ desc =>
+ "Timeout for proxy protocol. EMQX will close the TCP connection if proxy protocol packet is not received within the timeout."
+ }
)},
{?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME,
authentication("Per-listener authentication override")}
@@ -1762,7 +1778,10 @@ common_ssl_opts_schema(Defaults) ->
{"enable",
sc(
boolean(),
- #{default => Df("enable", false)}
+ #{
+ default => Df("enable", false),
+ desc => "Enable TLS."
+ }
)},
{"cacertfile",
sc(
@@ -1808,12 +1827,20 @@ common_ssl_opts_schema(Defaults) ->
{"verify",
sc(
hoconsc:enum([verify_peer, verify_none]),
- #{default => Df("verify", verify_none)}
+ #{
+ default => Df("verify", verify_none),
+ desc =>
+ "Enable or disable peer verification."
+ }
)},
{"reuse_sessions",
sc(
boolean(),
- #{default => Df("reuse_sessions", true)}
+ #{
+ default => Df("reuse_sessions", true),
+ desc =>
+ "Enable TLS session reuse."
+ }
)},
{"depth",
sc(
@@ -1850,7 +1877,9 @@ common_ssl_opts_schema(Defaults) ->
typerefl:alias("string", any()),
#{
default => <<"emqx_tls_psk:lookup">>,
- converter => fun ?MODULE:parse_user_lookup_fun/1
+ converter => fun ?MODULE:parse_user_lookup_fun/1,
+ desc =>
+ "EMQX-internal callback that is used to lookup pre-shared key (PSK) identity."
}
)},
{"secure_renegotiate",
@@ -1905,7 +1934,14 @@ server_ssl_opts_schema(Defaults, IsRanchListener) ->
{"honor_cipher_order",
sc(
boolean(),
- #{default => Df("honor_cipher_order", true)}
+ #{
+ default => Df("honor_cipher_order", true),
+ desc =>
+ "An important security setting, it forces the cipher to be set based\n"
+ " on the server-specified order instead of the client-specified order,\n"
+ " hence enforcing the (usually more properly configured) security\n"
+ " ordering of the server administrator."
+ }
)},
{"client_renegotiation",
sc(
diff --git a/apps/emqx_conf/src/emqx_conf_schema.erl b/apps/emqx_conf/src/emqx_conf_schema.erl
index b5c91372e..69e19df88 100644
--- a/apps/emqx_conf/src/emqx_conf_schema.erl
+++ b/apps/emqx_conf/src/emqx_conf_schema.erl
@@ -608,7 +608,7 @@ fields("log") ->
[ {"console_handler", ref("console_handler")}
, {"file_handlers",
sc(map(name, ref("log_file_handler")),
- #{})}
+ #{desc => "Key-value list of file-based log handlers."})}
, {"error_logger",
sc(atom(),
#{mapping => "kernel.error_logger",
diff --git a/apps/emqx_dashboard/etc/emqx_dashboard.conf b/apps/emqx_dashboard/etc/emqx_dashboard.conf
index 0fb0184fe..180ba8c3a 100644
--- a/apps/emqx_dashboard/etc/emqx_dashboard.conf
+++ b/apps/emqx_dashboard/etc/emqx_dashboard.conf
@@ -5,10 +5,10 @@
dashboard {
default_username = "admin"
default_password = "public"
- ## notice: sample_interval should be divisible by 60.
+ ## Note: sample_interval should be a divisor of 60.
## like 1s, 2s, 3s, 5s, 10s, 12s, 15s, 20s, 30s, 60s
sample_interval = 10s
- ## api jwt timeout. default is 30 minute
+ ## JWT token expiration time.
token_expired_time = 60m
listeners = [
{
diff --git a/apps/emqx_dashboard/src/emqx_dashboard_schema.erl b/apps/emqx_dashboard/src/emqx_dashboard_schema.erl
index 36ec75963..3cd2cd195 100644
--- a/apps/emqx_dashboard/src/emqx_dashboard_schema.erl
+++ b/apps/emqx_dashboard/src/emqx_dashboard_schema.erl
@@ -38,8 +38,15 @@ Alternatively, the HTTP listener can specify a unique IP address for each listen
but use the same port."})}
, {default_username, fun default_username/1}
, {default_password, fun default_password/1}
- , {sample_interval, sc(emqx_schema:duration_s(), #{default => "10s"})}
- , {token_expired_time, sc(emqx_schema:duration(), #{default => "30m"})}
+ , {sample_interval, sc(emqx_schema:duration_s(),
+ #{ default => "10s"
+ , desc => "How often to update metrics displayed in the dashboard.
"
+ "Note: `sample_interval` should be a divisor of 60."
+ })}
+ , {token_expired_time, sc(emqx_schema:duration(),
+ #{ default => "30m"
+ , desc => "JWT token expiration time."
+ })}
, {cors, fun cors/1}
];
@@ -113,9 +120,9 @@ cors(type) -> boolean();
cors(default) -> false;
cors(required) -> false;
cors(desc) ->
-"""Support Cross-Origin Resource Sharing (CORS).
+"Support Cross-Origin Resource Sharing (CORS).
Allows a server to indicate any origins (domain, scheme, or port) other than
-its own from which a browser should permit loading resources.""";
+its own from which a browser should permit loading resources.";
cors(_) -> undefined.
sc(Type, Meta) -> hoconsc:mk(Type, Meta).
diff --git a/apps/emqx_exhook/src/emqx_exhook_schema.erl b/apps/emqx_exhook/src/emqx_exhook_schema.erl
index e95a46beb..6cc1ebfcd 100644
--- a/apps/emqx_exhook/src/emqx_exhook_schema.erl
+++ b/apps/emqx_exhook/src/emqx_exhook_schema.erl
@@ -89,7 +89,10 @@ ref(Field) ->
failed_action() ->
sc(hoconsc:enum([deny, ignore]),
- #{default => deny}).
+ #{ default => deny
+ , desc => "The value that is returned when the request "
+ "to the gRPC server fails for any reason."
+ }).
server_config() ->
fields(server).
diff --git a/apps/emqx_gateway/src/emqx_gateway_schema.erl b/apps/emqx_gateway/src/emqx_gateway_schema.erl
index 09b0b7d1c..b01df7bb8 100644
--- a/apps/emqx_gateway/src/emqx_gateway_schema.erl
+++ b/apps/emqx_gateway/src/emqx_gateway_schema.erl
@@ -382,7 +382,7 @@ fields(udp_tcp_listeners) ->
fields(tcp_listener) ->
[ %% some special configs for tcp listener
- {acceptors, sc(integer(), #{default => 16})}
+ {acceptors, sc(integer(), #{default => 16, desc => "Size of the acceptor pool."})}
] ++
tcp_opts() ++
proxy_protocol_opts() ++
@@ -404,17 +404,21 @@ fields(udp_listener) ->
common_listener_opts();
fields(dtls_listener) ->
- [ {acceptors, sc(integer(), #{default => 16})}
+ [ {acceptors, sc(integer(), #{default => 16, desc => "Size of the acceptor pool."})}
] ++
fields(udp_listener) ++
[{dtls, sc(ref(dtls_opts), #{desc => "DTLS listener options"})}];
fields(udp_opts) ->
- [ {active_n, sc(integer(), #{default => 100})}
- , {recbuf, sc(bytesize())}
- , {sndbuf, sc(bytesize())}
- , {buffer, sc(bytesize())}
- , {reuseaddr, sc(boolean(), #{default => true})}
+ [ {active_n, sc(integer(),
+ #{ default => 100
+ , desc => "Specify the {active, N} option for the socket.
"
+ "See: https://erlang.org/doc/man/inet.html#setopts-2"
+ })}
+ , {recbuf, sc(bytesize(), #{desc => "Size of the kernel-space receive buffer for the socket."})}
+ , {sndbuf, sc(bytesize(), #{desc => "Size of the kernel-space send buffer for the socket."})}
+ , {buffer, sc(bytesize(), #{desc => "Size of the user-space buffer for the socket."})}
+ , {reuseaddr, sc(boolean(), #{default => true, desc => "Allow local reuse of port numbers."})}
];
fields(dtls_opts) ->
@@ -429,9 +433,9 @@ authentication_schema() ->
sc(emqx_authn_schema:authenticator_type(),
#{ required => {false, recursively}
, desc =>
-"""Default authentication configs for all the gateway listeners.
+"Default authentication configs for all the gateway listeners.
For per-listener overrides see authentication
-in listener configs"""
+in listener configs"
}).
gateway_common_options() ->
@@ -528,10 +532,16 @@ proxy_protocol_opts() ->
[ {proxy_protocol,
sc(boolean(),
#{ default => false
+ , desc => "Enable the Proxy Protocol V1/2 if the EMQX cluster is deployed "
+ "behind HAProxy or Nginx.
"
+ "See: https://www.haproxy.com/blog/haproxy/proxy-protocol/"
})}
, {proxy_protocol_timeout,
sc(duration(),
#{ default => "15s"
+ , desc => "Timeout for proxy protocol.
"
+ "EMQX will close the TCP connection if proxy protocol packet is not "
+ "received within the timeout."
})}
].
diff --git a/apps/emqx_retainer/src/emqx_retainer_schema.erl b/apps/emqx_retainer/src/emqx_retainer_schema.erl
index 306ab5642..7868bc6fd 100644
--- a/apps/emqx_retainer/src/emqx_retainer_schema.erl
+++ b/apps/emqx_retainer/src/emqx_retainer_schema.erl
@@ -32,7 +32,7 @@ fields("retainer") ->
];
fields(mnesia_config) ->
- [ {type, ?TYPE(hoconsc:union([built_in_database]))}
+ [ {type, hoconsc:mk(hoconsc:union([built_in_database]), #{desc => "Backend type."})}
, {storage_type, sc(hoconsc:union([ram, disc]),
"Specifies whether the messages are stored in RAM or persisted on disc.",
ram)}