mirror of https://github.com/telemt/telemt.git
Compare commits
14 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
6996d6e597 | |
|
|
b3f11624c9 | |
|
|
4d87a790cc | |
|
|
07fed8f871 | |
|
|
407d686d49 | |
|
|
eac5cc81fb | |
|
|
c51d16f403 | |
|
|
b5146bba94 | |
|
|
5ed525fa48 | |
|
|
9f7c1693ce | |
|
|
1524396e10 | |
|
|
444a20672d | |
|
|
d673935b6d | |
|
|
363b5014f7 |
|
|
@ -151,6 +151,14 @@ jobs:
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
cp "target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}" dist/telemt
|
cp "target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}" dist/telemt
|
||||||
|
|
||||||
|
if [ "${{ matrix.target }}" = "aarch64-unknown-linux-gnu" ]; then
|
||||||
|
STRIP_BIN=aarch64-linux-gnu-strip
|
||||||
|
else
|
||||||
|
STRIP_BIN=strip
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${STRIP_BIN}" dist/telemt
|
||||||
|
|
||||||
cd dist
|
cd dist
|
||||||
tar -czf "${{ matrix.asset }}.tar.gz" \
|
tar -czf "${{ matrix.asset }}.tar.gz" \
|
||||||
--owner=0 --group=0 --numeric-owner \
|
--owner=0 --group=0 --numeric-owner \
|
||||||
|
|
@ -279,6 +287,14 @@ jobs:
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
cp "target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}" dist/telemt
|
cp "target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}" dist/telemt
|
||||||
|
|
||||||
|
if [ "${{ matrix.target }}" = "aarch64-unknown-linux-musl" ]; then
|
||||||
|
STRIP_BIN=aarch64-linux-musl-strip
|
||||||
|
else
|
||||||
|
STRIP_BIN=strip
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${STRIP_BIN}" dist/telemt
|
||||||
|
|
||||||
cd dist
|
cd dist
|
||||||
tar -czf "${{ matrix.asset }}.tar.gz" \
|
tar -czf "${{ matrix.asset }}.tar.gz" \
|
||||||
--owner=0 --group=0 --numeric-owner \
|
--owner=0 --group=0 --numeric-owner \
|
||||||
|
|
|
||||||
16
LICENSE
16
LICENSE
|
|
@ -1,4 +1,4 @@
|
||||||
###### TELEMT Public License 3 ######
|
######## TELEMT LICENSE 3.3 #########
|
||||||
##### Copyright (c) 2026 Telemt #####
|
##### Copyright (c) 2026 Telemt #####
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
|
@ -14,11 +14,15 @@ are preserved and complied with.
|
||||||
The canonical version of this License is the English version.
|
The canonical version of this License is the English version.
|
||||||
Official translations are provided for informational purposes only
|
Official translations are provided for informational purposes only
|
||||||
and for convenience, and do not have legal force. In case of any
|
and for convenience, and do not have legal force. In case of any
|
||||||
discrepancy, the English version of this License shall prevail.
|
discrepancy, the English version of this License shall prevail
|
||||||
Available versions:
|
|
||||||
- English in Markdown: docs/LICENSE/LICENSE.md
|
/----------------------------------------------------------\
|
||||||
- German: docs/LICENSE/LICENSE.de.md
|
| Language | Location |
|
||||||
- Russian: docs/LICENSE/LICENSE.ru.md
|
|-------------|--------------------------------------------|
|
||||||
|
| English | docs/LICENSE/TELEMT-LICENSE.en.md |
|
||||||
|
| German | docs/LICENSE/TELEMT-LICENSE.de.md |
|
||||||
|
| Russian | docs/LICENSE/TELEMT-LICENSE.ru.md |
|
||||||
|
\----------------------------------------------------------/
|
||||||
|
|
||||||
### License Versioning Policy
|
### License Versioning Policy
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,10 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
|
|
||||||
| Key | Type | Default |
|
| Key | Type | Default |
|
||||||
| --- | ---- | ------- |
|
| --- | ---- | ------- |
|
||||||
| [`include`](#cfg-top-include) | `String` (special directive) | `null` |
|
| [`include`](#cfg-top-include) | `String` (special directive) | — |
|
||||||
| [`show_link`](#cfg-top-show_link) | `"*"` or `String[]` | `[]` (`ShowLink::None`) |
|
| [`show_link`](#cfg-top-show_link) | `"*"` or `String[]` | `[]` (`ShowLink::None`) |
|
||||||
| [`dc_overrides`](#cfg-top-dc_overrides) | `Map<String, String or String[]>` | `{}` |
|
| [`dc_overrides`](#cfg-top-dc_overrides) | `Map<String, String or String[]>` | `{}` |
|
||||||
| [`default_dc`](#cfg-top-default_dc) | `u8` or `null` | `null` (effective fallback: `2` in ME routing) |
|
| [`default_dc`](#cfg-top-default_dc) | `u8` | — (effective fallback: `2` in ME routing) |
|
||||||
|
|
||||||
<a id="cfg-top-include"></a>
|
<a id="cfg-top-include"></a>
|
||||||
- `include`
|
- `include`
|
||||||
|
|
@ -68,17 +68,17 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
|
|
||||||
| Key | Type | Default |
|
| Key | Type | Default |
|
||||||
| --- | ---- | ------- |
|
| --- | ---- | ------- |
|
||||||
| [`data_path`](#cfg-general-data_path) | `String` or `null` | `null` |
|
| [`data_path`](#cfg-general-data_path) | `String` | — |
|
||||||
| [`prefer_ipv6`](#cfg-general-prefer_ipv6) | `bool` | `false` |
|
| [`prefer_ipv6`](#cfg-general-prefer_ipv6) | `bool` | `false` |
|
||||||
| [`fast_mode`](#cfg-general-fast_mode) | `bool` | `true` |
|
| [`fast_mode`](#cfg-general-fast_mode) | `bool` | `true` |
|
||||||
| [`use_middle_proxy`](#cfg-general-use_middle_proxy) | `bool` | `true` |
|
| [`use_middle_proxy`](#cfg-general-use_middle_proxy) | `bool` | `true` |
|
||||||
| [`proxy_secret_path`](#cfg-general-proxy_secret_path) | `String` or `null` | `"proxy-secret"` |
|
| [`proxy_secret_path`](#cfg-general-proxy_secret_path) | `String` | `"proxy-secret"` |
|
||||||
| [`proxy_config_v4_cache_path`](#cfg-general-proxy_config_v4_cache_path) | `String` or `null` | `"cache/proxy-config-v4.txt"` |
|
| [`proxy_config_v4_cache_path`](#cfg-general-proxy_config_v4_cache_path) | `String` | `"cache/proxy-config-v4.txt"` |
|
||||||
| [`proxy_config_v6_cache_path`](#cfg-general-proxy_config_v6_cache_path) | `String` or `null` | `"cache/proxy-config-v6.txt"` |
|
| [`proxy_config_v6_cache_path`](#cfg-general-proxy_config_v6_cache_path) | `String` | `"cache/proxy-config-v6.txt"` |
|
||||||
| [`ad_tag`](#cfg-general-ad_tag) | `String` or `null` | `null` |
|
| [`ad_tag`](#cfg-general-ad_tag) | `String` | — |
|
||||||
| [`middle_proxy_nat_ip`](#cfg-general-middle_proxy_nat_ip) | `IpAddr` or `null` | `null` |
|
| [`middle_proxy_nat_ip`](#cfg-general-middle_proxy_nat_ip) | `IpAddr` | — |
|
||||||
| [`middle_proxy_nat_probe`](#cfg-general-middle_proxy_nat_probe) | `bool` | `true` |
|
| [`middle_proxy_nat_probe`](#cfg-general-middle_proxy_nat_probe) | `bool` | `true` |
|
||||||
| [`middle_proxy_nat_stun`](#cfg-general-middle_proxy_nat_stun) | `String` or `null` | `null` |
|
| [`middle_proxy_nat_stun`](#cfg-general-middle_proxy_nat_stun) | `String` | — |
|
||||||
| [`middle_proxy_nat_stun_servers`](#cfg-general-middle_proxy_nat_stun_servers) | `String[]` | `[]` |
|
| [`middle_proxy_nat_stun_servers`](#cfg-general-middle_proxy_nat_stun_servers) | `String[]` | `[]` |
|
||||||
| [`stun_nat_probe_concurrency`](#cfg-general-stun_nat_probe_concurrency) | `usize` | `8` |
|
| [`stun_nat_probe_concurrency`](#cfg-general-stun_nat_probe_concurrency) | `usize` | `8` |
|
||||||
| [`middle_proxy_pool_size`](#cfg-general-middle_proxy_pool_size) | `usize` | `8` |
|
| [`middle_proxy_pool_size`](#cfg-general-middle_proxy_pool_size) | `usize` | `8` |
|
||||||
|
|
@ -144,7 +144,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
| [`upstream_unhealthy_fail_threshold`](#cfg-general-upstream_unhealthy_fail_threshold) | `u32` | `5` |
|
| [`upstream_unhealthy_fail_threshold`](#cfg-general-upstream_unhealthy_fail_threshold) | `u32` | `5` |
|
||||||
| [`upstream_connect_failfast_hard_errors`](#cfg-general-upstream_connect_failfast_hard_errors) | `bool` | `false` |
|
| [`upstream_connect_failfast_hard_errors`](#cfg-general-upstream_connect_failfast_hard_errors) | `bool` | `false` |
|
||||||
| [`stun_iface_mismatch_ignore`](#cfg-general-stun_iface_mismatch_ignore) | `bool` | `false` |
|
| [`stun_iface_mismatch_ignore`](#cfg-general-stun_iface_mismatch_ignore) | `bool` | `false` |
|
||||||
| [`unknown_dc_log_path`](#cfg-general-unknown_dc_log_path) | `String` or `null` | `"unknown-dc.txt"` |
|
| [`unknown_dc_log_path`](#cfg-general-unknown_dc_log_path) | `String` | `"unknown-dc.txt"` |
|
||||||
| [`unknown_dc_file_log_enabled`](#cfg-general-unknown_dc_file_log_enabled) | `bool` | `false` |
|
| [`unknown_dc_file_log_enabled`](#cfg-general-unknown_dc_file_log_enabled) | `bool` | `false` |
|
||||||
| [`log_level`](#cfg-general-log_level) | `"debug"`, `"verbose"`, `"normal"`, or `"silent"` | `"normal"` |
|
| [`log_level`](#cfg-general-log_level) | `"debug"`, `"verbose"`, `"normal"`, or `"silent"` | `"normal"` |
|
||||||
| [`disable_colors`](#cfg-general-disable_colors) | `bool` | `false` |
|
| [`disable_colors`](#cfg-general-disable_colors) | `bool` | `false` |
|
||||||
|
|
@ -163,7 +163,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
| [`me_route_inline_recovery_attempts`](#cfg-general-me_route_inline_recovery_attempts) | `u32` | `3` |
|
| [`me_route_inline_recovery_attempts`](#cfg-general-me_route_inline_recovery_attempts) | `u32` | `3` |
|
||||||
| [`me_route_inline_recovery_wait_ms`](#cfg-general-me_route_inline_recovery_wait_ms) | `u64` | `3000` |
|
| [`me_route_inline_recovery_wait_ms`](#cfg-general-me_route_inline_recovery_wait_ms) | `u64` | `3000` |
|
||||||
| [`fast_mode_min_tls_record`](#cfg-general-fast_mode_min_tls_record) | `usize` | `0` |
|
| [`fast_mode_min_tls_record`](#cfg-general-fast_mode_min_tls_record) | `usize` | `0` |
|
||||||
| [`update_every`](#cfg-general-update_every) | `u64` or `null` | `300` |
|
| [`update_every`](#cfg-general-update_every) | `u64` | `300` |
|
||||||
| [`me_reinit_every_secs`](#cfg-general-me_reinit_every_secs) | `u64` | `900` |
|
| [`me_reinit_every_secs`](#cfg-general-me_reinit_every_secs) | `u64` | `900` |
|
||||||
| [`me_hardswap_warmup_delay_min_ms`](#cfg-general-me_hardswap_warmup_delay_min_ms) | `u64` | `1000` |
|
| [`me_hardswap_warmup_delay_min_ms`](#cfg-general-me_hardswap_warmup_delay_min_ms) | `u64` | `1000` |
|
||||||
| [`me_hardswap_warmup_delay_max_ms`](#cfg-general-me_hardswap_warmup_delay_max_ms) | `u64` | `2000` |
|
| [`me_hardswap_warmup_delay_max_ms`](#cfg-general-me_hardswap_warmup_delay_max_ms) | `u64` | `2000` |
|
||||||
|
|
@ -205,7 +205,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
|
|
||||||
<a id="cfg-general-data_path"></a>
|
<a id="cfg-general-data_path"></a>
|
||||||
- `data_path`
|
- `data_path`
|
||||||
- **Constraints / validation**: `String` or `null`.
|
- **Constraints / validation**: `String` (optional).
|
||||||
- **Description**: Optional runtime data directory path.
|
- **Description**: Optional runtime data directory path.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -245,7 +245,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-general-proxy_secret_path"></a>
|
<a id="cfg-general-proxy_secret_path"></a>
|
||||||
- `proxy_secret_path`
|
- `proxy_secret_path`
|
||||||
- **Constraints / validation**: `String` or `null`. If `null`, the effective cache path is `"proxy-secret"`. Empty values are accepted but will likely fail at runtime (invalid file path).
|
- **Constraints / validation**: `String`. When omitted, the default path is `"proxy-secret"`. Empty values are accepted by TOML/serde but will likely fail at runtime (invalid file path).
|
||||||
- **Description**: Path to Telegram infrastructure `proxy-secret` cache file used by ME handshake/RPC auth. Telemt always tries a fresh download from `https://core.telegram.org/getProxySecret` first, caches it to this path on success, and falls back to reading the cached file (any age) on download failure.
|
- **Description**: Path to Telegram infrastructure `proxy-secret` cache file used by ME handshake/RPC auth. Telemt always tries a fresh download from `https://core.telegram.org/getProxySecret` first, caches it to this path on success, and falls back to reading the cached file (any age) on download failure.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -255,7 +255,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-general-proxy_config_v4_cache_path"></a>
|
<a id="cfg-general-proxy_config_v4_cache_path"></a>
|
||||||
- `proxy_config_v4_cache_path`
|
- `proxy_config_v4_cache_path`
|
||||||
- **Constraints / validation**: `String` or `null`. When set, must not be empty/whitespace-only.
|
- **Constraints / validation**: `String`. When set, must not be empty/whitespace-only.
|
||||||
- **Description**: Optional disk cache path for raw `getProxyConfig` (IPv4) snapshot. At startup Telemt tries to fetch a fresh snapshot first; on fetch failure or empty snapshot it falls back to this cache file when present and non-empty.
|
- **Description**: Optional disk cache path for raw `getProxyConfig` (IPv4) snapshot. At startup Telemt tries to fetch a fresh snapshot first; on fetch failure or empty snapshot it falls back to this cache file when present and non-empty.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -265,7 +265,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-general-proxy_config_v6_cache_path"></a>
|
<a id="cfg-general-proxy_config_v6_cache_path"></a>
|
||||||
- `proxy_config_v6_cache_path`
|
- `proxy_config_v6_cache_path`
|
||||||
- **Constraints / validation**: `String` or `null`. When set, must not be empty/whitespace-only.
|
- **Constraints / validation**: `String`. When set, must not be empty/whitespace-only.
|
||||||
- **Description**: Optional disk cache path for raw `getProxyConfigV6` (IPv6) snapshot. At startup Telemt tries to fetch a fresh snapshot first; on fetch failure or empty snapshot it falls back to this cache file when present and non-empty.
|
- **Description**: Optional disk cache path for raw `getProxyConfigV6` (IPv6) snapshot. At startup Telemt tries to fetch a fresh snapshot first; on fetch failure or empty snapshot it falls back to this cache file when present and non-empty.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -275,7 +275,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-general-ad_tag"></a>
|
<a id="cfg-general-ad_tag"></a>
|
||||||
- `ad_tag`
|
- `ad_tag`
|
||||||
- **Constraints / validation**: `String` or `null`. When set, must be exactly 32 hex characters; invalid values are disabled during config load.
|
- **Constraints / validation**: `String` (optional). When set, must be exactly 32 hex characters; invalid values are disabled during config load.
|
||||||
- **Description**: Global fallback sponsored-channel `ad_tag` (used when user has no override in `access.user_ad_tags`). An all-zero tag is accepted but has no effect (and is warned about) until replaced with a real tag from `@MTProxybot`.
|
- **Description**: Global fallback sponsored-channel `ad_tag` (used when user has no override in `access.user_ad_tags`). An all-zero tag is accepted but has no effect (and is warned about) until replaced with a real tag from `@MTProxybot`.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -285,7 +285,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-general-middle_proxy_nat_ip"></a>
|
<a id="cfg-general-middle_proxy_nat_ip"></a>
|
||||||
- `middle_proxy_nat_ip`
|
- `middle_proxy_nat_ip`
|
||||||
- **Constraints / validation**: `IpAddr` or `null`.
|
- **Constraints / validation**: `IpAddr` (optional).
|
||||||
- **Description**: Manual public NAT IP override used as ME address material when set.
|
- **Description**: Manual public NAT IP override used as ME address material when set.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -967,8 +967,8 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-general-unknown_dc_log_path"></a>
|
<a id="cfg-general-unknown_dc_log_path"></a>
|
||||||
- `unknown_dc_log_path`
|
- `unknown_dc_log_path`
|
||||||
- **Constraints / validation**: `String` or `null`. Must be a safe path (no `..` components, parent directory must exist); unsafe paths are rejected at runtime.
|
- **Constraints / validation**: `String` (optional). Must be a safe path (no `..` components, parent directory must exist); unsafe paths are rejected at runtime.
|
||||||
- **Description**: Log file path for unknown (non-standard) DC requests when `unknown_dc_file_log_enabled = true`. Set to `null` to disable file logging.
|
- **Description**: Log file path for unknown (non-standard) DC requests when `unknown_dc_file_log_enabled = true`. Omit this key to disable file logging.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
|
@ -1157,7 +1157,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-general-update_every"></a>
|
<a id="cfg-general-update_every"></a>
|
||||||
- `update_every`
|
- `update_every`
|
||||||
- **Constraints / validation**: `u64` (seconds) or `null`. If set, must be `> 0`. If `null`, legacy `proxy_secret_auto_reload_secs` and `proxy_config_auto_reload_secs` are used and their effective minimum must be `> 0`.
|
- **Constraints / validation**: `u64` (seconds). If set, must be `> 0`. If this key is not explicitly set, legacy `proxy_secret_auto_reload_secs` and `proxy_config_auto_reload_secs` may be used (their effective minimum must be `> 0`).
|
||||||
- **Description**: Unified refresh interval for ME updater tasks (`getProxyConfig`, `getProxyConfigV6`, `getProxySecret`). When set, it overrides legacy proxy reload intervals.
|
- **Description**: Unified refresh interval for ME updater tasks (`getProxyConfig`, `getProxyConfigV6`, `getProxySecret`). When set, it overrides legacy proxy reload intervals.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -1450,7 +1450,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-general-proxy_secret_auto_reload_secs"></a>
|
<a id="cfg-general-proxy_secret_auto_reload_secs"></a>
|
||||||
- `proxy_secret_auto_reload_secs`
|
- `proxy_secret_auto_reload_secs`
|
||||||
- **Constraints / validation**: Deprecated. Use `general.update_every`. When `general.update_every` is `null`, the effective legacy refresh interval is `min(proxy_secret_auto_reload_secs, proxy_config_auto_reload_secs)` and must be `> 0`.
|
- **Constraints / validation**: Deprecated. Use `general.update_every`. When `general.update_every` is not explicitly set, the effective legacy refresh interval is `min(proxy_secret_auto_reload_secs, proxy_config_auto_reload_secs)` and must be `> 0`.
|
||||||
- **Description**: Deprecated legacy proxy-secret refresh interval. Used only when `general.update_every` is not set.
|
- **Description**: Deprecated legacy proxy-secret refresh interval. Used only when `general.update_every` is not set.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -1463,7 +1463,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-general-proxy_config_auto_reload_secs"></a>
|
<a id="cfg-general-proxy_config_auto_reload_secs"></a>
|
||||||
- `proxy_config_auto_reload_secs`
|
- `proxy_config_auto_reload_secs`
|
||||||
- **Constraints / validation**: Deprecated. Use `general.update_every`. When `general.update_every` is `null`, the effective legacy refresh interval is `min(proxy_secret_auto_reload_secs, proxy_config_auto_reload_secs)` and must be `> 0`.
|
- **Constraints / validation**: Deprecated. Use `general.update_every`. When `general.update_every` is not explicitly set, the effective legacy refresh interval is `min(proxy_secret_auto_reload_secs, proxy_config_auto_reload_secs)` and must be `> 0`.
|
||||||
- **Description**: Deprecated legacy ME config refresh interval. Used only when `general.update_every` is not set.
|
- **Description**: Deprecated legacy ME config refresh interval. Used only when `general.update_every` is not set.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -1624,8 +1624,8 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
| Key | Type | Default |
|
| Key | Type | Default |
|
||||||
| --- | ---- | ------- |
|
| --- | ---- | ------- |
|
||||||
| [`show`](#cfg-general-links-show) | `"*"` or `String[]` | `"*"` |
|
| [`show`](#cfg-general-links-show) | `"*"` or `String[]` | `"*"` |
|
||||||
| [`public_host`](#cfg-general-links-public_host) | `String` or `null` | `null` |
|
| [`public_host`](#cfg-general-links-public_host) | `String` | — |
|
||||||
| [`public_port`](#cfg-general-links-public_port) | `u16` or `null` | `null` |
|
| [`public_port`](#cfg-general-links-public_port) | `u16` | — |
|
||||||
|
|
||||||
<a id="cfg-general-links-show"></a>
|
<a id="cfg-general-links-show"></a>
|
||||||
- `show`
|
- `show`
|
||||||
|
|
@ -1641,7 +1641,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-general-links-public_host"></a>
|
<a id="cfg-general-links-public_host"></a>
|
||||||
- `public_host`
|
- `public_host`
|
||||||
- **Constraints / validation**: `String` or `null`.
|
- **Constraints / validation**: `String` (optional).
|
||||||
- **Description**: Public hostname/IP override used for generated `tg://` links (overrides detected IP).
|
- **Description**: Public hostname/IP override used for generated `tg://` links (overrides detected IP).
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -1651,7 +1651,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-general-links-public_port"></a>
|
<a id="cfg-general-links-public_port"></a>
|
||||||
- `public_port`
|
- `public_port`
|
||||||
- **Constraints / validation**: `u16` or `null`.
|
- **Constraints / validation**: `u16` (optional).
|
||||||
- **Description**: Public port override used for generated `tg://` links (overrides `server.port`).
|
- **Description**: Public port override used for generated `tg://` links (overrides `server.port`).
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -1708,7 +1708,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
| Key | Type | Default |
|
| Key | Type | Default |
|
||||||
| --- | ---- | ------- |
|
| --- | ---- | ------- |
|
||||||
| [`ipv4`](#cfg-network-ipv4) | `bool` | `true` |
|
| [`ipv4`](#cfg-network-ipv4) | `bool` | `true` |
|
||||||
| [`ipv6`](#cfg-network-ipv6) | `bool` or `null` | `false` |
|
| [`ipv6`](#cfg-network-ipv6) | `bool` | `false` |
|
||||||
| [`prefer`](#cfg-network-prefer) | `u8` | `4` |
|
| [`prefer`](#cfg-network-prefer) | `u8` | `4` |
|
||||||
| [`multipath`](#cfg-network-multipath) | `bool` | `false` |
|
| [`multipath`](#cfg-network-multipath) | `bool` | `false` |
|
||||||
| [`stun_use`](#cfg-network-stun_use) | `bool` | `true` |
|
| [`stun_use`](#cfg-network-stun_use) | `bool` | `true` |
|
||||||
|
|
@ -1730,8 +1730,8 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-network-ipv6"></a>
|
<a id="cfg-network-ipv6"></a>
|
||||||
- `ipv6`
|
- `ipv6`
|
||||||
- **Constraints / validation**: `bool` or `null`. `null` means "auto-detect IPv6 availability".
|
- **Constraints / validation**: `bool`.
|
||||||
- **Description**: Enables/disables IPv6 when explicitly set; when `null`, Telemt will auto-detect IPv6 availability at runtime.
|
- **Description**: Enables/disables IPv6 networking. When omitted, defaults to `false`.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
|
@ -1741,9 +1741,6 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
|
|
||||||
# or: disable IPv6 explicitly
|
# or: disable IPv6 explicitly
|
||||||
# ipv6 = false
|
# ipv6 = false
|
||||||
|
|
||||||
# or: let Telemt auto-detect
|
|
||||||
# ipv6 = null
|
|
||||||
```
|
```
|
||||||
<a id="cfg-network-prefer"></a>
|
<a id="cfg-network-prefer"></a>
|
||||||
- `prefer`
|
- `prefer`
|
||||||
|
|
@ -1842,16 +1839,16 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
| Key | Type | Default |
|
| Key | Type | Default |
|
||||||
| --- | ---- | ------- |
|
| --- | ---- | ------- |
|
||||||
| [`port`](#cfg-server-port) | `u16` | `443` |
|
| [`port`](#cfg-server-port) | `u16` | `443` |
|
||||||
| [`listen_addr_ipv4`](#cfg-server-listen_addr_ipv4) | `String` or `null` | `"0.0.0.0"` |
|
| [`listen_addr_ipv4`](#cfg-server-listen_addr_ipv4) | `String` | `"0.0.0.0"` |
|
||||||
| [`listen_addr_ipv6`](#cfg-server-listen_addr_ipv6) | `String` or `null` | `"::"` |
|
| [`listen_addr_ipv6`](#cfg-server-listen_addr_ipv6) | `String` | `"::"` |
|
||||||
| [`listen_unix_sock`](#cfg-server-listen_unix_sock) | `String` or `null` | `null` |
|
| [`listen_unix_sock`](#cfg-server-listen_unix_sock) | `String` | — |
|
||||||
| [`listen_unix_sock_perm`](#cfg-server-listen_unix_sock_perm) | `String` or `null` | `null` |
|
| [`listen_unix_sock_perm`](#cfg-server-listen_unix_sock_perm) | `String` | — |
|
||||||
| [`listen_tcp`](#cfg-server-listen_tcp) | `bool` or `null` | `null` (auto) |
|
| [`listen_tcp`](#cfg-server-listen_tcp) | `bool` | — (auto) |
|
||||||
| [`proxy_protocol`](#cfg-server-proxy_protocol) | `bool` | `false` |
|
| [`proxy_protocol`](#cfg-server-proxy_protocol) | `bool` | `false` |
|
||||||
| [`proxy_protocol_header_timeout_ms`](#cfg-server-proxy_protocol_header_timeout_ms) | `u64` | `500` |
|
| [`proxy_protocol_header_timeout_ms`](#cfg-server-proxy_protocol_header_timeout_ms) | `u64` | `500` |
|
||||||
| [`proxy_protocol_trusted_cidrs`](#cfg-server-proxy_protocol_trusted_cidrs) | `IpNetwork[]` | `[]` |
|
| [`proxy_protocol_trusted_cidrs`](#cfg-server-proxy_protocol_trusted_cidrs) | `IpNetwork[]` | `[]` |
|
||||||
| [`metrics_port`](#cfg-server-metrics_port) | `u16` or `null` | `null` |
|
| [`metrics_port`](#cfg-server-metrics_port) | `u16` | — |
|
||||||
| [`metrics_listen`](#cfg-server-metrics_listen) | `String` or `null` | `null` |
|
| [`metrics_listen`](#cfg-server-metrics_listen) | `String` | — |
|
||||||
| [`metrics_whitelist`](#cfg-server-metrics_whitelist) | `IpNetwork[]` | `["127.0.0.1/32", "::1/128"]` |
|
| [`metrics_whitelist`](#cfg-server-metrics_whitelist) | `IpNetwork[]` | `["127.0.0.1/32", "::1/128"]` |
|
||||||
| [`max_connections`](#cfg-server-max_connections) | `u32` | `10000` |
|
| [`max_connections`](#cfg-server-max_connections) | `u32` | `10000` |
|
||||||
| [`accept_permit_timeout_ms`](#cfg-server-accept_permit_timeout_ms) | `u64` | `250` |
|
| [`accept_permit_timeout_ms`](#cfg-server-accept_permit_timeout_ms) | `u64` | `250` |
|
||||||
|
|
@ -1868,8 +1865,8 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-server-listen_addr_ipv4"></a>
|
<a id="cfg-server-listen_addr_ipv4"></a>
|
||||||
- `listen_addr_ipv4`
|
- `listen_addr_ipv4`
|
||||||
- **Constraints / validation**: `String` or `null`. When set, must be a valid IPv4 address string.
|
- **Constraints / validation**: `String` (optional). When set, must be a valid IPv4 address string.
|
||||||
- **Description**: IPv4 bind address for TCP listener (`null` disables IPv4 bind).
|
- **Description**: IPv4 bind address for TCP listener (omit this key to disable IPv4 bind).
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
|
@ -1878,8 +1875,8 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-server-listen_addr_ipv6"></a>
|
<a id="cfg-server-listen_addr_ipv6"></a>
|
||||||
- `listen_addr_ipv6`
|
- `listen_addr_ipv6`
|
||||||
- **Constraints / validation**: `String` or `null`. When set, must be a valid IPv6 address string.
|
- **Constraints / validation**: `String` (optional). When set, must be a valid IPv6 address string.
|
||||||
- **Description**: IPv6 bind address for TCP listener (`null` disables IPv6 bind).
|
- **Description**: IPv6 bind address for TCP listener (omit this key to disable IPv6 bind).
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
|
@ -1888,7 +1885,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-server-listen_unix_sock"></a>
|
<a id="cfg-server-listen_unix_sock"></a>
|
||||||
- `listen_unix_sock`
|
- `listen_unix_sock`
|
||||||
- **Constraints / validation**: `String` or `null`. Must not be empty when set. Unix only.
|
- **Constraints / validation**: `String` (optional). Must not be empty when set. Unix only.
|
||||||
- **Description**: Unix socket path for listener. When set, `server.listen_tcp` defaults to `false` (unless explicitly overridden).
|
- **Description**: Unix socket path for listener. When set, `server.listen_tcp` defaults to `false` (unless explicitly overridden).
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -1898,8 +1895,8 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-server-listen_unix_sock_perm"></a>
|
<a id="cfg-server-listen_unix_sock_perm"></a>
|
||||||
- `listen_unix_sock_perm`
|
- `listen_unix_sock_perm`
|
||||||
- **Constraints / validation**: `String` or `null`. When set, should be an octal permission string like `"0666"` or `"0777"`.
|
- **Constraints / validation**: `String` (optional). When set, should be an octal permission string like `"0666"` or `"0777"`.
|
||||||
- **Description**: Optional Unix socket file permissions applied after bind (chmod). `null` means "no change" (inherits umask).
|
- **Description**: Optional Unix socket file permissions applied after bind (chmod). When omitted, permissions are not changed (inherits umask).
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
|
@ -1909,7 +1906,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-server-listen_tcp"></a>
|
<a id="cfg-server-listen_tcp"></a>
|
||||||
- `listen_tcp`
|
- `listen_tcp`
|
||||||
- **Constraints / validation**: `bool` or `null`. `null` means auto:
|
- **Constraints / validation**: `bool` (optional). When omitted, Telemt auto-detects:
|
||||||
- `true` when `listen_unix_sock` is not set
|
- `true` when `listen_unix_sock` is not set
|
||||||
- `false` when `listen_unix_sock` is set
|
- `false` when `listen_unix_sock` is set
|
||||||
- **Description**: Explicit TCP listener enable/disable override.
|
- **Description**: Explicit TCP listener enable/disable override.
|
||||||
|
|
@ -1957,7 +1954,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-server-metrics_port"></a>
|
<a id="cfg-server-metrics_port"></a>
|
||||||
- `metrics_port`
|
- `metrics_port`
|
||||||
- **Constraints / validation**: `u16` or `null`.
|
- **Constraints / validation**: `u16` (optional).
|
||||||
- **Description**: Prometheus-compatible metrics endpoint port. When set, enables the metrics listener (bind behavior can be overridden by `metrics_listen`).
|
- **Description**: Prometheus-compatible metrics endpoint port. When set, enables the metrics listener (bind behavior can be overridden by `metrics_listen`).
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -1967,7 +1964,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
```
|
```
|
||||||
<a id="cfg-server-metrics_listen"></a>
|
<a id="cfg-server-metrics_listen"></a>
|
||||||
- `metrics_listen`
|
- `metrics_listen`
|
||||||
- **Constraints / validation**: `String` or `null`. When set, must be in `IP:PORT` format.
|
- **Constraints / validation**: `String` (optional). When set, must be in `IP:PORT` format.
|
||||||
- **Description**: Full metrics bind address (`IP:PORT`), overrides `metrics_port` and binds on the specified address only.
|
- **Description**: Full metrics bind address (`IP:PORT`), overrides `metrics_port` and binds on the specified address only.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -2010,6 +2007,105 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||||
|
|
||||||
Note: When `server.proxy_protocol` is enabled, incoming PROXY protocol headers are parsed from the first bytes of the connection and the client source address is replaced with `src_addr` from the header. For security, the peer source IP (the direct connection address) is verified against `server.proxy_protocol_trusted_cidrs`; if this list is empty, PROXY headers are rejected and the connection is considered untrusted.
|
Note: When `server.proxy_protocol` is enabled, incoming PROXY protocol headers are parsed from the first bytes of the connection and the client source address is replaced with `src_addr` from the header. For security, the peer source IP (the direct connection address) is verified against `server.proxy_protocol_trusted_cidrs`; if this list is empty, PROXY headers are rejected and the connection is considered untrusted.
|
||||||
|
|
||||||
|
## [server.conntrack_control]
|
||||||
|
|
||||||
|
Note: The conntrack-control worker runs **only on Linux**. On other operating systems it is not started; if `inline_conntrack_control` is `true`, a warning is logged. Effective operation also requires **CAP_NET_ADMIN** and a usable backend (`nft` or `iptables` / `ip6tables` on `PATH`). The `conntrack` utility is used for optional table entry deletes under pressure.
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Type | Default |
|
||||||
|
| --- | ---- | ------- |
|
||||||
|
| [`inline_conntrack_control`](#cfg-server-conntrack_control-inline_conntrack_control) | `bool` | `true` |
|
||||||
|
| [`mode`](#cfg-server-conntrack_control-mode) | `String` | `"tracked"` |
|
||||||
|
| [`backend`](#cfg-server-conntrack_control-backend) | `String` | `"auto"` |
|
||||||
|
| [`profile`](#cfg-server-conntrack_control-profile) | `String` | `"balanced"` |
|
||||||
|
| [`hybrid_listener_ips`](#cfg-server-conntrack_control-hybrid_listener_ips) | `IpAddr[]` | `[]` |
|
||||||
|
| [`pressure_high_watermark_pct`](#cfg-server-conntrack_control-pressure_high_watermark_pct) | `u8` | `85` |
|
||||||
|
| [`pressure_low_watermark_pct`](#cfg-server-conntrack_control-pressure_low_watermark_pct) | `u8` | `70` |
|
||||||
|
| [`delete_budget_per_sec`](#cfg-server-conntrack_control-delete_budget_per_sec) | `u64` | `4096` |
|
||||||
|
|
||||||
|
<a id="cfg-server-conntrack_control-inline_conntrack_control"></a>
|
||||||
|
- `inline_conntrack_control`
|
||||||
|
- **Constraints / validation**: `bool`.
|
||||||
|
- **Description**: Master switch for the runtime conntrack-control task: reconciles **raw/notrack** netfilter rules for listener ingress (see `mode`), samples load every second, and may run **`conntrack -D`** deletes for qualifying close events while **pressure mode** is active (see `delete_budget_per_sec`). When `false`, notrack rules are cleared and pressure-driven deletes are disabled.
|
||||||
|
- **Example**:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[server.conntrack_control]
|
||||||
|
inline_conntrack_control = true
|
||||||
|
```
|
||||||
|
<a id="cfg-server-conntrack_control-mode"></a>
|
||||||
|
- `mode`
|
||||||
|
- **Constraints / validation**: One of `tracked`, `notrack`, `hybrid` (case-insensitive; serialized lowercase).
|
||||||
|
- **Description**: **`tracked`**: do not install telemt notrack rules (connections stay in conntrack). **`notrack`**: mark matching ingress TCP to `server.port` as notrack — targets are derived from `[[server.listeners]]` if any, otherwise from `server.listen_addr_ipv4` / `server.listen_addr_ipv6` (unspecified addresses mean “any” for that family). **`hybrid`**: notrack only for addresses listed in `hybrid_listener_ips` (must be non-empty; validated at load).
|
||||||
|
- **Example**:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[server.conntrack_control]
|
||||||
|
mode = "notrack"
|
||||||
|
```
|
||||||
|
<a id="cfg-server-conntrack_control-backend"></a>
|
||||||
|
- `backend`
|
||||||
|
- **Constraints / validation**: One of `auto`, `nftables`, `iptables` (case-insensitive; serialized lowercase).
|
||||||
|
- **Description**: Which command set applies notrack rules. **`auto`**: use `nft` if present on `PATH`, else `iptables`/`ip6tables` if present. **`nftables`** / **`iptables`**: force that backend; missing binary means rules cannot be applied. The nft path uses table `inet telemt_conntrack` and a prerouting raw hook; iptables uses chain `TELEMT_NOTRACK` in the `raw` table.
|
||||||
|
- **Example**:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[server.conntrack_control]
|
||||||
|
backend = "auto"
|
||||||
|
```
|
||||||
|
<a id="cfg-server-conntrack_control-profile"></a>
|
||||||
|
- `profile`
|
||||||
|
- **Constraints / validation**: One of `conservative`, `balanced`, `aggressive` (case-insensitive; serialized lowercase).
|
||||||
|
- **Description**: When **conntrack pressure mode** is active (`pressure_*` watermarks), caps idle and activity timeouts to reduce conntrack churn: e.g. **client first-byte idle** (`client.rs`), **direct relay activity timeout** (`direct_relay.rs`), and **middle-relay idle policy** caps (`middle_relay.rs` via `ConntrackPressureProfile::*_cap_secs` / `direct_activity_timeout_secs`). More aggressive profiles use shorter caps.
|
||||||
|
- **Example**:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[server.conntrack_control]
|
||||||
|
profile = "balanced"
|
||||||
|
```
|
||||||
|
<a id="cfg-server-conntrack_control-hybrid_listener_ips"></a>
|
||||||
|
- `hybrid_listener_ips`
|
||||||
|
- **Constraints / validation**: `IpAddr[]`. Required to be **non-empty** when `mode = "hybrid"`. Ignored for `tracked` / `notrack`.
|
||||||
|
- **Description**: Explicit listener addresses that receive notrack rules in hybrid mode (split into IPv4 vs IPv6 rules by the implementation).
|
||||||
|
- **Example**:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[server.conntrack_control]
|
||||||
|
mode = "hybrid"
|
||||||
|
hybrid_listener_ips = ["203.0.113.10", "2001:db8::1"]
|
||||||
|
```
|
||||||
|
<a id="cfg-server-conntrack_control-pressure_high_watermark_pct"></a>
|
||||||
|
- `pressure_high_watermark_pct`
|
||||||
|
- **Constraints / validation**: Must be within `[1, 100]`.
|
||||||
|
- **Description**: Pressure mode **enters** when any of: connection fill vs `server.max_connections` (percentage, if `max_connections > 0`), **file-descriptor** usage vs process soft `RLIMIT_NOFILE`, **non-zero** `accept_permit_timeout` events in the last sample window, or **ME c2me send-full** counter delta. Entry compares relevant percentages against this high watermark (see `update_pressure_state` in `conntrack_control.rs`).
|
||||||
|
- **Example**:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[server.conntrack_control]
|
||||||
|
pressure_high_watermark_pct = 85
|
||||||
|
```
|
||||||
|
<a id="cfg-server-conntrack_control-pressure_low_watermark_pct"></a>
|
||||||
|
- `pressure_low_watermark_pct`
|
||||||
|
- **Constraints / validation**: Must be **strictly less than** `pressure_high_watermark_pct`.
|
||||||
|
- **Description**: Pressure mode **clears** only after **three** consecutive one-second samples where all signals are at or below this low watermark and the accept-timeout / ME-queue deltas are zero (hysteresis).
|
||||||
|
- **Example**:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[server.conntrack_control]
|
||||||
|
pressure_low_watermark_pct = 70
|
||||||
|
```
|
||||||
|
<a id="cfg-server-conntrack_control-delete_budget_per_sec"></a>
|
||||||
|
- `delete_budget_per_sec`
|
||||||
|
- **Constraints / validation**: Must be `> 0`.
|
||||||
|
- **Description**: Maximum number of **`conntrack -D`** attempts **per second** while pressure mode is active (token bucket refilled each second). Deletes run only for close events with reasons **timeout**, **pressure**, or **reset**; each attempt consumes a token regardless of outcome.
|
||||||
|
- **Example**:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[server.conntrack_control]
|
||||||
|
delete_budget_per_sec = 4096
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## [server.api]
|
## [server.api]
|
||||||
|
|
||||||
Note: This section also accepts the legacy alias `[server.admin_api]` (same schema as `[server.api]`).
|
Note: This section also accepts the legacy alias `[server.admin_api]` (same schema as `[server.api]`).
|
||||||
|
|
@ -2158,9 +2254,9 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||||
| Key | Type | Default |
|
| Key | Type | Default |
|
||||||
| --- | ---- | ------- |
|
| --- | ---- | ------- |
|
||||||
| [`ip`](#cfg-server-listeners-ip) | `IpAddr` | — |
|
| [`ip`](#cfg-server-listeners-ip) | `IpAddr` | — |
|
||||||
| [`announce`](#cfg-server-listeners-announce) | `String` or `null` | — |
|
| [`announce`](#cfg-server-listeners-announce) | `String` | — |
|
||||||
| [`announce_ip`](#cfg-server-listeners-announce_ip) | `IpAddr` or `null` | — |
|
| [`announce_ip`](#cfg-server-listeners-announce_ip) | `IpAddr` | — |
|
||||||
| [`proxy_protocol`](#cfg-server-listeners-proxy_protocol) | `bool` or `null` | `null` |
|
| [`proxy_protocol`](#cfg-server-listeners-proxy_protocol) | `bool` | — |
|
||||||
| [`reuse_allow`](#cfg-server-listeners-reuse_allow) | `bool` | `false` |
|
| [`reuse_allow`](#cfg-server-listeners-reuse_allow) | `bool` | `false` |
|
||||||
|
|
||||||
<a id="cfg-server-listeners-ip"></a>
|
<a id="cfg-server-listeners-ip"></a>
|
||||||
|
|
@ -2175,7 +2271,7 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||||
```
|
```
|
||||||
<a id="cfg-server-listeners-announce"></a>
|
<a id="cfg-server-listeners-announce"></a>
|
||||||
- `announce`
|
- `announce`
|
||||||
- **Constraints / validation**: `String` or `null`. Must not be empty when set.
|
- **Constraints / validation**: `String` (optional). Must not be empty when set.
|
||||||
- **Description**: Public IP/domain announced in proxy links for this listener. Takes precedence over `announce_ip`.
|
- **Description**: Public IP/domain announced in proxy links for this listener. Takes precedence over `announce_ip`.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -2186,7 +2282,7 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||||
```
|
```
|
||||||
<a id="cfg-server-listeners-announce_ip"></a>
|
<a id="cfg-server-listeners-announce_ip"></a>
|
||||||
- `announce_ip`
|
- `announce_ip`
|
||||||
- **Constraints / validation**: `IpAddr` or `null`. Deprecated. Use `announce`.
|
- **Constraints / validation**: `IpAddr` (optional). Deprecated. Use `announce`.
|
||||||
- **Description**: Deprecated legacy announce IP. During config load it is migrated to `announce` when `announce` is not set.
|
- **Description**: Deprecated legacy announce IP. During config load it is migrated to `announce` when `announce` is not set.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -2197,7 +2293,7 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||||
```
|
```
|
||||||
<a id="cfg-server-listeners-proxy_protocol"></a>
|
<a id="cfg-server-listeners-proxy_protocol"></a>
|
||||||
- `proxy_protocol`
|
- `proxy_protocol`
|
||||||
- **Constraints / validation**: `bool` or `null`. When set, overrides `server.proxy_protocol` for this listener.
|
- **Constraints / validation**: `bool` (optional). When set, overrides `server.proxy_protocol` for this listener.
|
||||||
- **Description**: Per-listener PROXY protocol override.
|
- **Description**: Per-listener PROXY protocol override.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -2351,9 +2447,9 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||||
| [`tls_fetch_scope`](#cfg-censorship-tls_fetch_scope) | `String` | `""` |
|
| [`tls_fetch_scope`](#cfg-censorship-tls_fetch_scope) | `String` | `""` |
|
||||||
| [`tls_fetch`](#cfg-censorship-tls_fetch) | `Table` | built-in defaults |
|
| [`tls_fetch`](#cfg-censorship-tls_fetch) | `Table` | built-in defaults |
|
||||||
| [`mask`](#cfg-censorship-mask) | `bool` | `true` |
|
| [`mask`](#cfg-censorship-mask) | `bool` | `true` |
|
||||||
| [`mask_host`](#cfg-censorship-mask_host) | `String` or `null` | `null` |
|
| [`mask_host`](#cfg-censorship-mask_host) | `String` | — |
|
||||||
| [`mask_port`](#cfg-censorship-mask_port) | `u16` | `443` |
|
| [`mask_port`](#cfg-censorship-mask_port) | `u16` | `443` |
|
||||||
| [`mask_unix_sock`](#cfg-censorship-mask_unix_sock) | `String` or `null` | `null` |
|
| [`mask_unix_sock`](#cfg-censorship-mask_unix_sock) | `String` | — |
|
||||||
| [`fake_cert_len`](#cfg-censorship-fake_cert_len) | `usize` | `2048` |
|
| [`fake_cert_len`](#cfg-censorship-fake_cert_len) | `usize` | `2048` |
|
||||||
| [`tls_emulation`](#cfg-censorship-tls_emulation) | `bool` | `true` |
|
| [`tls_emulation`](#cfg-censorship-tls_emulation) | `bool` | `true` |
|
||||||
| [`tls_front_dir`](#cfg-censorship-tls_front_dir) | `String` | `"tlsfront"` |
|
| [`tls_front_dir`](#cfg-censorship-tls_front_dir) | `String` | `"tlsfront"` |
|
||||||
|
|
@ -2440,8 +2536,8 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||||
```
|
```
|
||||||
<a id="cfg-censorship-mask_host"></a>
|
<a id="cfg-censorship-mask_host"></a>
|
||||||
- `mask_host`
|
- `mask_host`
|
||||||
- **Constraints / validation**: `String` or `null`.
|
- **Constraints / validation**: `String` (optional).
|
||||||
- If `mask_unix_sock` is set, `mask_host` must be `null` (mutually exclusive).
|
- If `mask_unix_sock` is set, `mask_host` must be omitted (mutually exclusive).
|
||||||
- If `mask_host` is not set and `mask_unix_sock` is not set, Telemt defaults `mask_host` to `tls_domain`.
|
- If `mask_host` is not set and `mask_unix_sock` is not set, Telemt defaults `mask_host` to `tls_domain`.
|
||||||
- **Description**: Upstream mask host for TLS fronting relay.
|
- **Description**: Upstream mask host for TLS fronting relay.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
@ -2462,7 +2558,7 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||||
```
|
```
|
||||||
<a id="cfg-censorship-mask_unix_sock"></a>
|
<a id="cfg-censorship-mask_unix_sock"></a>
|
||||||
- `mask_unix_sock`
|
- `mask_unix_sock`
|
||||||
- **Constraints / validation**: `String` or `null`.
|
- **Constraints / validation**: `String` (optional).
|
||||||
- Must not be empty when set.
|
- Must not be empty when set.
|
||||||
- Unix only; rejected on non-Unix platforms.
|
- Unix only; rejected on non-Unix platforms.
|
||||||
- On Unix, must be \(\le 107\) bytes (path length limit).
|
- On Unix, must be \(\le 107\) bytes (path length limit).
|
||||||
|
|
@ -2882,6 +2978,7 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||||
| [`users`](#cfg-access-users) | `Map<String, String>` | `{"default": "000…000"}` |
|
| [`users`](#cfg-access-users) | `Map<String, String>` | `{"default": "000…000"}` |
|
||||||
| [`user_ad_tags`](#cfg-access-user_ad_tags) | `Map<String, String>` | `{}` |
|
| [`user_ad_tags`](#cfg-access-user_ad_tags) | `Map<String, String>` | `{}` |
|
||||||
| [`user_max_tcp_conns`](#cfg-access-user_max_tcp_conns) | `Map<String, usize>` | `{}` |
|
| [`user_max_tcp_conns`](#cfg-access-user_max_tcp_conns) | `Map<String, usize>` | `{}` |
|
||||||
|
| [`user_max_tcp_conns_global_each`](#cfg-access-user_max_tcp_conns_global_each) | `usize` | `0` |
|
||||||
| [`user_expirations`](#cfg-access-user_expirations) | `Map<String, DateTime<Utc>>` | `{}` |
|
| [`user_expirations`](#cfg-access-user_expirations) | `Map<String, DateTime<Utc>>` | `{}` |
|
||||||
| [`user_data_quota`](#cfg-access-user_data_quota) | `Map<String, u64>` | `{}` |
|
| [`user_data_quota`](#cfg-access-user_data_quota) | `Map<String, u64>` | `{}` |
|
||||||
| [`user_max_unique_ips`](#cfg-access-user_max_unique_ips) | `Map<String, usize>` | `{}` |
|
| [`user_max_unique_ips`](#cfg-access-user_max_unique_ips) | `Map<String, usize>` | `{}` |
|
||||||
|
|
@ -2926,6 +3023,20 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||||
[access.user_max_tcp_conns]
|
[access.user_max_tcp_conns]
|
||||||
alice = 500
|
alice = 500
|
||||||
```
|
```
|
||||||
|
<a id="cfg-access-user_max_tcp_conns_global_each"></a>
|
||||||
|
- `user_max_tcp_conns_global_each`
|
||||||
|
- **Constraints / validation**: `usize`. `0` disables the inherited limit.
|
||||||
|
- **Description**: Global per-user maximum concurrent TCP connections, applied when a user has **no positive** entry in `[access.user_max_tcp_conns]` (a missing key, or a value of `0`, both fall through to this setting). Per-user limits greater than `0` in `user_max_tcp_conns` take precedence.
|
||||||
|
- **Example**:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[access]
|
||||||
|
user_max_tcp_conns_global_each = 200
|
||||||
|
|
||||||
|
[access.user_max_tcp_conns]
|
||||||
|
alice = 500 # uses 500, not the global cap
|
||||||
|
# bob has no entry → uses 200
|
||||||
|
```
|
||||||
<a id="cfg-access-user_expirations"></a>
|
<a id="cfg-access-user_expirations"></a>
|
||||||
- `user_expirations`
|
- `user_expirations`
|
||||||
- **Constraints / validation**: `Map<String, DateTime<Utc>>`. Each value must be a valid RFC3339 / ISO-8601 datetime.
|
- **Constraints / validation**: `Map<String, DateTime<Utc>>`. Each value must be a valid RFC3339 / ISO-8601 datetime.
|
||||||
|
|
@ -3027,13 +3138,13 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||||
| [`weight`](#cfg-upstreams-weight) | `u16` | `1` |
|
| [`weight`](#cfg-upstreams-weight) | `u16` | `1` |
|
||||||
| [`enabled`](#cfg-upstreams-enabled) | `bool` | `true` |
|
| [`enabled`](#cfg-upstreams-enabled) | `bool` | `true` |
|
||||||
| [`scopes`](#cfg-upstreams-scopes) | `String` | `""` |
|
| [`scopes`](#cfg-upstreams-scopes) | `String` | `""` |
|
||||||
| [`interface`](#cfg-upstreams-interface) | `String` or `null` | `null` |
|
| [`interface`](#cfg-upstreams-interface) | `String` | — |
|
||||||
| [`bind_addresses`](#cfg-upstreams-bind_addresses) | `String[]` or `null` | `null` |
|
| [`bind_addresses`](#cfg-upstreams-bind_addresses) | `String[]` | — |
|
||||||
| [`url`](#cfg-upstreams-url) | `String` | — |
|
| [`url`](#cfg-upstreams-url) | `String` | — |
|
||||||
| [`address`](#cfg-upstreams-address) | `String` | — |
|
| [`address`](#cfg-upstreams-address) | `String` | — |
|
||||||
| [`user_id`](#cfg-upstreams-user_id) | `String` or `null` | `null` |
|
| [`user_id`](#cfg-upstreams-user_id) | `String` | — |
|
||||||
| [`username`](#cfg-upstreams-username) | `String` or `null` | `null` |
|
| [`username`](#cfg-upstreams-username) | `String` | — |
|
||||||
| [`password`](#cfg-upstreams-password) | `String` or `null` | `null` |
|
| [`password`](#cfg-upstreams-password) | `String` | — |
|
||||||
|
|
||||||
<a id="cfg-upstreams-type"></a>
|
<a id="cfg-upstreams-type"></a>
|
||||||
- `type`
|
- `type`
|
||||||
|
|
@ -3090,7 +3201,7 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||||
```
|
```
|
||||||
<a id="cfg-upstreams-interface"></a>
|
<a id="cfg-upstreams-interface"></a>
|
||||||
- `interface`
|
- `interface`
|
||||||
- **Constraints / validation**: `String` or `null`.
|
- **Constraints / validation**: `String` (optional).
|
||||||
- For `"direct"`: may be an IP address (used as explicit local bind) or an OS interface name (resolved to an IP at runtime; Unix only).
|
- For `"direct"`: may be an IP address (used as explicit local bind) or an OS interface name (resolved to an IP at runtime; Unix only).
|
||||||
- For `"socks4"`/`"socks5"`: supported only when `address` is an `IP:port` literal; when `address` is a hostname, interface binding is ignored.
|
- For `"socks4"`/`"socks5"`: supported only when `address` is an `IP:port` literal; when `address` is a hostname, interface binding is ignored.
|
||||||
- For `"shadowsocks"`: passed to the shadowsocks connector as an optional outbound bind hint.
|
- For `"shadowsocks"`: passed to the shadowsocks connector as an optional outbound bind hint.
|
||||||
|
|
@ -3109,7 +3220,7 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||||
```
|
```
|
||||||
<a id="cfg-upstreams-bind_addresses"></a>
|
<a id="cfg-upstreams-bind_addresses"></a>
|
||||||
- `bind_addresses`
|
- `bind_addresses`
|
||||||
- **Constraints / validation**: `String[]` or `null`. Applies only to `type = "direct"`.
|
- **Constraints / validation**: `String[]` (optional). Applies only to `type = "direct"`.
|
||||||
- Each entry should be an IP address string.
|
- Each entry should be an IP address string.
|
||||||
- At runtime, Telemt selects an address that matches the target family (IPv4 vs IPv6). If `bind_addresses` is set and none match the target family, the connect attempt fails.
|
- At runtime, Telemt selects an address that matches the target family (IPv4 vs IPv6). If `bind_addresses` is set and none match the target family, the connect attempt fails.
|
||||||
- **Description**: Explicit local source addresses for outgoing direct TCP connects. When multiple addresses are provided, selection is round-robin.
|
- **Description**: Explicit local source addresses for outgoing direct TCP connects. When multiple addresses are provided, selection is round-robin.
|
||||||
|
|
@ -3150,7 +3261,7 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||||
```
|
```
|
||||||
<a id="cfg-upstreams-user_id"></a>
|
<a id="cfg-upstreams-user_id"></a>
|
||||||
- `user_id`
|
- `user_id`
|
||||||
- **Constraints / validation**: `String` or `null`. Only for `type = "socks4"`.
|
- **Constraints / validation**: `String` (optional). Only for `type = "socks4"`.
|
||||||
- **Description**: SOCKS4 CONNECT user ID. Note: when a request scope is selected, Telemt may override this with the selected scope value.
|
- **Description**: SOCKS4 CONNECT user ID. Note: when a request scope is selected, Telemt may override this with the selected scope value.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -3162,7 +3273,7 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||||
```
|
```
|
||||||
<a id="cfg-upstreams-username"></a>
|
<a id="cfg-upstreams-username"></a>
|
||||||
- `username`
|
- `username`
|
||||||
- **Constraints / validation**: `String` or `null`. Only for `type = "socks5"`.
|
- **Constraints / validation**: `String` (optional). Only for `type = "socks5"`.
|
||||||
- **Description**: SOCKS5 username (for username/password authentication). Note: when a request scope is selected, Telemt may override this with the selected scope value.
|
- **Description**: SOCKS5 username (for username/password authentication). Note: when a request scope is selected, Telemt may override this with the selected scope value.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
@ -3174,7 +3285,7 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||||
```
|
```
|
||||||
<a id="cfg-upstreams-password"></a>
|
<a id="cfg-upstreams-password"></a>
|
||||||
- `password`
|
- `password`
|
||||||
- **Constraints / validation**: `String` or `null`. Only for `type = "socks5"`.
|
- **Constraints / validation**: `String` (optional). Only for `type = "socks5"`.
|
||||||
- **Description**: SOCKS5 password (for username/password authentication). Note: when a request scope is selected, Telemt may override this with the selected scope value.
|
- **Description**: SOCKS5 password (for username/password authentication). Note: when a request scope is selected, Telemt may override this with the selected scope value.
|
||||||
- **Example**:
|
- **Example**:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,8 +128,8 @@ WorkingDirectory=/opt/telemt
|
||||||
ExecStart=/bin/telemt /etc/telemt/telemt.toml
|
ExecStart=/bin/telemt /etc/telemt/telemt.toml
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
LimitNOFILE=65536
|
LimitNOFILE=65536
|
||||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||||
NoNewPrivileges=true
|
NoNewPrivileges=true
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
|
|
||||||
|
|
@ -128,8 +128,8 @@ WorkingDirectory=/opt/telemt
|
||||||
ExecStart=/bin/telemt /etc/telemt/telemt.toml
|
ExecStart=/bin/telemt /etc/telemt/telemt.toml
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
LimitNOFILE=65536
|
LimitNOFILE=65536
|
||||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||||
NoNewPrivileges=true
|
NoNewPrivileges=true
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
|
|
||||||
241
install.sh
241
install.sh
|
|
@ -8,12 +8,20 @@ CONFIG_DIR="${CONFIG_DIR:-/etc/telemt}"
|
||||||
CONFIG_FILE="${CONFIG_FILE:-${CONFIG_DIR}/telemt.toml}"
|
CONFIG_FILE="${CONFIG_FILE:-${CONFIG_DIR}/telemt.toml}"
|
||||||
WORK_DIR="${WORK_DIR:-/opt/telemt}"
|
WORK_DIR="${WORK_DIR:-/opt/telemt}"
|
||||||
TLS_DOMAIN="${TLS_DOMAIN:-petrovich.ru}"
|
TLS_DOMAIN="${TLS_DOMAIN:-petrovich.ru}"
|
||||||
|
SERVER_PORT="${SERVER_PORT:-443}"
|
||||||
|
USER_SECRET=""
|
||||||
|
AD_TAG=""
|
||||||
SERVICE_NAME="telemt"
|
SERVICE_NAME="telemt"
|
||||||
TEMP_DIR=""
|
TEMP_DIR=""
|
||||||
SUDO=""
|
SUDO=""
|
||||||
CONFIG_PARENT_DIR=""
|
CONFIG_PARENT_DIR=""
|
||||||
SERVICE_START_FAILED=0
|
SERVICE_START_FAILED=0
|
||||||
|
|
||||||
|
PORT_PROVIDED=0
|
||||||
|
SECRET_PROVIDED=0
|
||||||
|
AD_TAG_PROVIDED=0
|
||||||
|
DOMAIN_PROVIDED=0
|
||||||
|
|
||||||
ACTION="install"
|
ACTION="install"
|
||||||
TARGET_VERSION="${VERSION:-latest}"
|
TARGET_VERSION="${VERSION:-latest}"
|
||||||
|
|
||||||
|
|
@ -25,8 +33,37 @@ while [ $# -gt 0 ]; do
|
||||||
printf '[ERROR] %s requires a domain argument.\n' "$1" >&2
|
printf '[ERROR] %s requires a domain argument.\n' "$1" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
TLS_DOMAIN="$2"
|
TLS_DOMAIN="$2"; DOMAIN_PROVIDED=1; shift 2 ;;
|
||||||
shift 2 ;;
|
-p|--port)
|
||||||
|
if [ "$#" -lt 2 ] || [ -z "$2" ]; then
|
||||||
|
printf '[ERROR] %s requires a port argument.\n' "$1" >&2; exit 1
|
||||||
|
fi
|
||||||
|
case "$2" in
|
||||||
|
*[!0-9]*) printf '[ERROR] Port must be a valid number.\n' >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
port_num="$(printf '%s\n' "$2" | sed 's/^0*//')"
|
||||||
|
[ -z "$port_num" ] && port_num="0"
|
||||||
|
if [ "${#port_num}" -gt 5 ] || [ "$port_num" -lt 1 ] || [ "$port_num" -gt 65535 ]; then
|
||||||
|
printf '[ERROR] Port must be between 1 and 65535.\n' >&2; exit 1
|
||||||
|
fi
|
||||||
|
SERVER_PORT="$port_num"; PORT_PROVIDED=1; shift 2 ;;
|
||||||
|
-s|--secret)
|
||||||
|
if [ "$#" -lt 2 ] || [ -z "$2" ]; then
|
||||||
|
printf '[ERROR] %s requires a secret argument.\n' "$1" >&2; exit 1
|
||||||
|
fi
|
||||||
|
case "$2" in
|
||||||
|
*[!0-9a-fA-F]*)
|
||||||
|
printf '[ERROR] Secret must contain only hex characters.\n' >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
if [ "${#2}" -ne 32 ]; then
|
||||||
|
printf '[ERROR] Secret must be exactly 32 chars.\n' >&2; exit 1
|
||||||
|
fi
|
||||||
|
USER_SECRET="$2"; SECRET_PROVIDED=1; shift 2 ;;
|
||||||
|
-a|--ad-tag|--ad_tag)
|
||||||
|
if [ "$#" -lt 2 ] || [ -z "$2" ]; then
|
||||||
|
printf '[ERROR] %s requires an ad_tag argument.\n' "$1" >&2; exit 1
|
||||||
|
fi
|
||||||
|
AD_TAG="$2"; AD_TAG_PROVIDED=1; shift 2 ;;
|
||||||
uninstall|--uninstall)
|
uninstall|--uninstall)
|
||||||
if [ "$ACTION" != "purge" ]; then ACTION="uninstall"; fi
|
if [ "$ACTION" != "purge" ]; then ACTION="uninstall"; fi
|
||||||
shift ;;
|
shift ;;
|
||||||
|
|
@ -59,12 +96,17 @@ cleanup() {
|
||||||
trap cleanup EXIT INT TERM
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
show_help() {
|
show_help() {
|
||||||
say "Usage: $0 [ <version> | install | uninstall | purge ] [ -d <domain> ] [ --help ]"
|
say "Usage: $0 [ <version> | install | uninstall | purge ] [ options ]"
|
||||||
say " <version> Install specific version (e.g. 3.3.15, default: latest)"
|
say " <version> Install specific version (e.g. 3.3.15, default: latest)"
|
||||||
say " install Install the latest version"
|
say " install Install the latest version"
|
||||||
say " uninstall Remove the binary and service (keeps config and user)"
|
say " uninstall Remove the binary and service"
|
||||||
say " purge Remove everything including configuration, data, and user"
|
say " purge Remove everything including configuration, data, and user"
|
||||||
|
say ""
|
||||||
|
say "Options:"
|
||||||
say " -d, --domain Set TLS domain (default: petrovich.ru)"
|
say " -d, --domain Set TLS domain (default: petrovich.ru)"
|
||||||
|
say " -p, --port Set server port (default: 443)"
|
||||||
|
say " -s, --secret Set specific user secret (32 hex characters)"
|
||||||
|
say " -a, --ad-tag Set ad_tag"
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,6 +162,14 @@ get_svc_mgr() {
|
||||||
else echo "none"; fi
|
else echo "none"; fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_config_exists() {
|
||||||
|
if [ -n "$SUDO" ]; then
|
||||||
|
$SUDO sh -c '[ -f "$1" ]' _ "$CONFIG_FILE"
|
||||||
|
else
|
||||||
|
[ -f "$CONFIG_FILE" ]
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
verify_common() {
|
verify_common() {
|
||||||
[ -n "$BIN_NAME" ] || die "BIN_NAME cannot be empty."
|
[ -n "$BIN_NAME" ] || die "BIN_NAME cannot be empty."
|
||||||
[ -n "$INSTALL_DIR" ] || die "INSTALL_DIR cannot be empty."
|
[ -n "$INSTALL_DIR" ] || die "INSTALL_DIR cannot be empty."
|
||||||
|
|
@ -127,7 +177,7 @@ verify_common() {
|
||||||
[ -n "$CONFIG_FILE" ] || die "CONFIG_FILE cannot be empty."
|
[ -n "$CONFIG_FILE" ] || die "CONFIG_FILE cannot be empty."
|
||||||
|
|
||||||
case "${INSTALL_DIR}${CONFIG_DIR}${WORK_DIR}${CONFIG_FILE}" in
|
case "${INSTALL_DIR}${CONFIG_DIR}${WORK_DIR}${CONFIG_FILE}" in
|
||||||
*[!a-zA-Z0-9_./-]*) die "Invalid characters in paths. Only alphanumeric, _, ., -, and / allowed." ;;
|
*[!a-zA-Z0-9_./-]*) die "Invalid characters in paths." ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case "$TARGET_VERSION" in *[!a-zA-Z0-9_.-]*) die "Invalid characters in version." ;; esac
|
case "$TARGET_VERSION" in *[!a-zA-Z0-9_.-]*) die "Invalid characters in version." ;; esac
|
||||||
|
|
@ -145,11 +195,11 @@ verify_common() {
|
||||||
if [ "$(id -u)" -eq 0 ]; then
|
if [ "$(id -u)" -eq 0 ]; then
|
||||||
SUDO=""
|
SUDO=""
|
||||||
else
|
else
|
||||||
command -v sudo >/dev/null 2>&1 || die "This script requires root or sudo. Neither found."
|
command -v sudo >/dev/null 2>&1 || die "This script requires root or sudo."
|
||||||
SUDO="sudo"
|
SUDO="sudo"
|
||||||
if ! sudo -n true 2>/dev/null; then
|
if ! sudo -n true 2>/dev/null; then
|
||||||
if ! [ -t 0 ]; then
|
if ! [ -t 0 ]; then
|
||||||
die "sudo requires a password, but no TTY detected. Aborting to prevent hang."
|
die "sudo requires a password, but no TTY detected."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
@ -162,21 +212,7 @@ verify_common() {
|
||||||
die "Safety check failed: CONFIG_FILE '$CONFIG_FILE' is a directory."
|
die "Safety check failed: CONFIG_FILE '$CONFIG_FILE' is a directory."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for path in "$CONFIG_DIR" "$CONFIG_PARENT_DIR" "$WORK_DIR"; do
|
for cmd in id uname awk grep find rm chown chmod mv mktemp mkdir tr dd sed ps head sleep cat tar gzip; do
|
||||||
check_path="$(get_realpath "$path")"
|
|
||||||
case "$check_path" in
|
|
||||||
/|/bin|/sbin|/usr|/usr/bin|/usr/sbin|/usr/local|/usr/local/bin|/usr/local/sbin|/usr/local/etc|/usr/local/share|/etc|/var|/var/lib|/var/log|/var/run|/home|/root|/tmp|/lib|/lib64|/opt|/run|/boot|/dev|/sys|/proc)
|
|
||||||
die "Safety check failed: '$path' (resolved to '$check_path') is a critical system directory." ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
check_install_dir="$(get_realpath "$INSTALL_DIR")"
|
|
||||||
case "$check_install_dir" in
|
|
||||||
/|/etc|/var|/home|/root|/tmp|/usr|/usr/local|/opt|/boot|/dev|/sys|/proc|/run)
|
|
||||||
die "Safety check failed: INSTALL_DIR '$INSTALL_DIR' is a critical system directory." ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
for cmd in id uname grep find rm chown chmod mv mktemp mkdir tr dd sed ps head sleep cat tar gzip rmdir; do
|
|
||||||
command -v "$cmd" >/dev/null 2>&1 || die "Required command not found: $cmd"
|
command -v "$cmd" >/dev/null 2>&1 || die "Required command not found: $cmd"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
@ -185,14 +221,41 @@ verify_install_deps() {
|
||||||
command -v curl >/dev/null 2>&1 || command -v wget >/dev/null 2>&1 || die "Neither curl nor wget is installed."
|
command -v curl >/dev/null 2>&1 || command -v wget >/dev/null 2>&1 || die "Neither curl nor wget is installed."
|
||||||
command -v cp >/dev/null 2>&1 || command -v install >/dev/null 2>&1 || die "Need cp or install"
|
command -v cp >/dev/null 2>&1 || command -v install >/dev/null 2>&1 || die "Need cp or install"
|
||||||
|
|
||||||
if ! command -v setcap >/dev/null 2>&1; then
|
if ! command -v setcap >/dev/null 2>&1 || ! command -v conntrack >/dev/null 2>&1; then
|
||||||
if command -v apk >/dev/null 2>&1; then
|
if command -v apk >/dev/null 2>&1; then
|
||||||
$SUDO apk add --no-cache libcap-utils >/dev/null 2>&1 || $SUDO apk add --no-cache libcap >/dev/null 2>&1 || true
|
$SUDO apk add --no-cache libcap-utils libcap conntrack-tools >/dev/null 2>&1 || true
|
||||||
elif command -v apt-get >/dev/null 2>&1; then
|
elif command -v apt-get >/dev/null 2>&1; then
|
||||||
$SUDO apt-get update -q >/dev/null 2>&1 || true
|
$SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y -q libcap2-bin conntrack >/dev/null 2>&1 || {
|
||||||
$SUDO apt-get install -y -q libcap2-bin >/dev/null 2>&1 || true
|
$SUDO env DEBIAN_FRONTEND=noninteractive apt-get update -q >/dev/null 2>&1 || true
|
||||||
elif command -v dnf >/dev/null 2>&1; then $SUDO dnf install -y -q libcap >/dev/null 2>&1 || true
|
$SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y -q libcap2-bin conntrack >/dev/null 2>&1 || true
|
||||||
elif command -v yum >/dev/null 2>&1; then $SUDO yum install -y -q libcap >/dev/null 2>&1 || true
|
}
|
||||||
|
elif command -v dnf >/dev/null 2>&1; then $SUDO dnf install -y -q libcap conntrack-tools >/dev/null 2>&1 || true
|
||||||
|
elif command -v yum >/dev/null 2>&1; then $SUDO yum install -y -q libcap conntrack-tools >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_port_availability() {
|
||||||
|
port_info=""
|
||||||
|
|
||||||
|
if command -v ss >/dev/null 2>&1; then
|
||||||
|
port_info=$($SUDO ss -tulnp 2>/dev/null | grep -E ":${SERVER_PORT}([[:space:]]|$)" || true)
|
||||||
|
elif command -v netstat >/dev/null 2>&1; then
|
||||||
|
port_info=$($SUDO netstat -tulnp 2>/dev/null | grep -E ":${SERVER_PORT}([[:space:]]|$)" || true)
|
||||||
|
elif command -v lsof >/dev/null 2>&1; then
|
||||||
|
port_info=$($SUDO lsof -i :${SERVER_PORT} 2>/dev/null | grep LISTEN || true)
|
||||||
|
else
|
||||||
|
say "[WARNING] Network diagnostic tools (ss, netstat, lsof) not found. Skipping port check."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$port_info" ]; then
|
||||||
|
if printf '%s\n' "$port_info" | grep -q "${BIN_NAME}"; then
|
||||||
|
say " -> Port ${SERVER_PORT} is in use by ${BIN_NAME}. Ignoring as it will be restarted."
|
||||||
|
else
|
||||||
|
say "[ERROR] Port ${SERVER_PORT} is already in use by another process:"
|
||||||
|
printf ' %s\n' "$port_info"
|
||||||
|
die "Please free the port ${SERVER_PORT} or change it and try again."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
@ -275,17 +338,19 @@ install_binary() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
$SUDO mkdir -p "$INSTALL_DIR" || die "Failed to create install directory"
|
$SUDO mkdir -p "$INSTALL_DIR" || die "Failed to create install directory"
|
||||||
|
|
||||||
|
$SUDO rm -f "$bin_dst" 2>/dev/null || true
|
||||||
|
|
||||||
if command -v install >/dev/null 2>&1; then
|
if command -v install >/dev/null 2>&1; then
|
||||||
$SUDO install -m 0755 "$bin_src" "$bin_dst" || die "Failed to install binary"
|
$SUDO install -m 0755 "$bin_src" "$bin_dst" || die "Failed to install binary"
|
||||||
else
|
else
|
||||||
$SUDO rm -f "$bin_dst" 2>/dev/null || true
|
|
||||||
$SUDO cp "$bin_src" "$bin_dst" && $SUDO chmod 0755 "$bin_dst" || die "Failed to copy binary"
|
$SUDO cp "$bin_src" "$bin_dst" && $SUDO chmod 0755 "$bin_dst" || die "Failed to copy binary"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
$SUDO sh -c '[ -x "$1" ]' _ "$bin_dst" || die "Binary not executable: $bin_dst"
|
$SUDO sh -c '[ -x "$1" ]' _ "$bin_dst" || die "Binary not executable: $bin_dst"
|
||||||
|
|
||||||
if command -v setcap >/dev/null 2>&1; then
|
if command -v setcap >/dev/null 2>&1; then
|
||||||
$SUDO setcap cap_net_bind_service=+ep "$bin_dst" 2>/dev/null || true
|
$SUDO setcap cap_net_bind_service,cap_net_admin=+ep "$bin_dst" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,11 +366,20 @@ generate_secret() {
|
||||||
}
|
}
|
||||||
|
|
||||||
generate_config_content() {
|
generate_config_content() {
|
||||||
|
conf_secret="$1"
|
||||||
|
conf_tag="$2"
|
||||||
escaped_tls_domain="$(printf '%s\n' "$TLS_DOMAIN" | tr -d '[:cntrl:]' | sed 's/\\/\\\\/g; s/"/\\"/g')"
|
escaped_tls_domain="$(printf '%s\n' "$TLS_DOMAIN" | tr -d '[:cntrl:]' | sed 's/\\/\\\\/g; s/"/\\"/g')"
|
||||||
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
[general]
|
[general]
|
||||||
use_middle_proxy = false
|
use_middle_proxy = true
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ -n "$conf_tag" ]; then
|
||||||
|
echo "ad_tag = \"${conf_tag}\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
[general.modes]
|
[general.modes]
|
||||||
classic = false
|
classic = false
|
||||||
|
|
@ -313,7 +387,7 @@ secure = false
|
||||||
tls = true
|
tls = true
|
||||||
|
|
||||||
[server]
|
[server]
|
||||||
port = 443
|
port = ${SERVER_PORT}
|
||||||
|
|
||||||
[server.api]
|
[server.api]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
@ -324,28 +398,73 @@ whitelist = ["127.0.0.1/32"]
|
||||||
tls_domain = "${escaped_tls_domain}"
|
tls_domain = "${escaped_tls_domain}"
|
||||||
|
|
||||||
[access.users]
|
[access.users]
|
||||||
hello = "$1"
|
hello = "${conf_secret}"
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
install_config() {
|
install_config() {
|
||||||
if [ -n "$SUDO" ]; then
|
if is_config_exists; then
|
||||||
if $SUDO sh -c '[ -f "$1" ]' _ "$CONFIG_FILE"; then
|
say " -> Config already exists at $CONFIG_FILE. Updating parameters..."
|
||||||
say " -> Config already exists at $CONFIG_FILE. Skipping creation."
|
|
||||||
return 0
|
tmp_conf="${TEMP_DIR}/config.tmp"
|
||||||
fi
|
$SUDO cat "$CONFIG_FILE" > "$tmp_conf"
|
||||||
elif [ -f "$CONFIG_FILE" ]; then
|
|
||||||
say " -> Config already exists at $CONFIG_FILE. Skipping creation."
|
escaped_domain="$(printf '%s\n' "$TLS_DOMAIN" | tr -d '[:cntrl:]' | sed 's/\\/\\\\/g; s/"/\\"/g')"
|
||||||
|
|
||||||
|
export AWK_PORT="$SERVER_PORT"
|
||||||
|
export AWK_SECRET="$USER_SECRET"
|
||||||
|
export AWK_DOMAIN="$escaped_domain"
|
||||||
|
export AWK_AD_TAG="$AD_TAG"
|
||||||
|
export AWK_FLAG_P="$PORT_PROVIDED"
|
||||||
|
export AWK_FLAG_S="$SECRET_PROVIDED"
|
||||||
|
export AWK_FLAG_D="$DOMAIN_PROVIDED"
|
||||||
|
export AWK_FLAG_A="$AD_TAG_PROVIDED"
|
||||||
|
|
||||||
|
awk '
|
||||||
|
BEGIN { ad_tag_handled = 0 }
|
||||||
|
|
||||||
|
ENVIRON["AWK_FLAG_P"] == "1" && /^[ \t]*port[ \t]*=/ { print "port = " ENVIRON["AWK_PORT"]; next }
|
||||||
|
ENVIRON["AWK_FLAG_S"] == "1" && /^[ \t]*hello[ \t]*=/ { print "hello = \"" ENVIRON["AWK_SECRET"] "\""; next }
|
||||||
|
ENVIRON["AWK_FLAG_D"] == "1" && /^[ \t]*tls_domain[ \t]*=/ { print "tls_domain = \"" ENVIRON["AWK_DOMAIN"] "\""; next }
|
||||||
|
|
||||||
|
ENVIRON["AWK_FLAG_A"] == "1" && /^[ \t]*ad_tag[ \t]*=/ {
|
||||||
|
if (!ad_tag_handled) {
|
||||||
|
print "ad_tag = \"" ENVIRON["AWK_AD_TAG"] "\"";
|
||||||
|
ad_tag_handled = 1;
|
||||||
|
}
|
||||||
|
next
|
||||||
|
}
|
||||||
|
ENVIRON["AWK_FLAG_A"] == "1" && /^\[general\]/ {
|
||||||
|
print;
|
||||||
|
if (!ad_tag_handled) {
|
||||||
|
print "ad_tag = \"" ENVIRON["AWK_AD_TAG"] "\"";
|
||||||
|
ad_tag_handled = 1;
|
||||||
|
}
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
{ print }
|
||||||
|
' "$tmp_conf" > "${tmp_conf}.new" && mv "${tmp_conf}.new" "$tmp_conf"
|
||||||
|
|
||||||
|
[ "$PORT_PROVIDED" -eq 1 ] && say " -> Updated port: $SERVER_PORT"
|
||||||
|
[ "$SECRET_PROVIDED" -eq 1 ] && say " -> Updated secret for user 'hello'"
|
||||||
|
[ "$DOMAIN_PROVIDED" -eq 1 ] && say " -> Updated tls_domain: $TLS_DOMAIN"
|
||||||
|
[ "$AD_TAG_PROVIDED" -eq 1 ] && say " -> Updated ad_tag"
|
||||||
|
|
||||||
|
write_root "$CONFIG_FILE" < "$tmp_conf"
|
||||||
|
rm -f "$tmp_conf"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
toml_secret="$(generate_secret)" || die "Failed to generate secret."
|
if [ -z "$USER_SECRET" ]; then
|
||||||
|
USER_SECRET="$(generate_secret)" || die "Failed to generate secret."
|
||||||
|
fi
|
||||||
|
|
||||||
generate_config_content "$toml_secret" | write_root "$CONFIG_FILE" || die "Failed to install config"
|
generate_config_content "$USER_SECRET" "$AD_TAG" | write_root "$CONFIG_FILE" || die "Failed to install config"
|
||||||
$SUDO chown root:telemt "$CONFIG_FILE" && $SUDO chmod 640 "$CONFIG_FILE"
|
$SUDO chown root:telemt "$CONFIG_FILE" && $SUDO chmod 640 "$CONFIG_FILE"
|
||||||
|
|
||||||
say " -> Config created successfully."
|
say " -> Config created successfully."
|
||||||
say " -> Generated secret for default user 'hello': $toml_secret"
|
say " -> Configured secret for user 'hello': $USER_SECRET"
|
||||||
}
|
}
|
||||||
|
|
||||||
generate_systemd_content() {
|
generate_systemd_content() {
|
||||||
|
|
@ -362,9 +481,10 @@ Group=telemt
|
||||||
WorkingDirectory=$WORK_DIR
|
WorkingDirectory=$WORK_DIR
|
||||||
ExecStart="${INSTALL_DIR}/${BIN_NAME}" "${CONFIG_FILE}"
|
ExecStart="${INSTALL_DIR}/${BIN_NAME}" "${CONFIG_FILE}"
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
LimitNOFILE=65536
|
LimitNOFILE=65536
|
||||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
|
||||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|
@ -429,7 +549,8 @@ kill_user_procs() {
|
||||||
if command -v pgrep >/dev/null 2>&1; then
|
if command -v pgrep >/dev/null 2>&1; then
|
||||||
pids="$(pgrep -u telemt 2>/dev/null || true)"
|
pids="$(pgrep -u telemt 2>/dev/null || true)"
|
||||||
else
|
else
|
||||||
pids="$(ps -u telemt -o pid= 2>/dev/null || true)"
|
pids="$(ps -ef 2>/dev/null | awk '$1=="telemt"{print $2}' || true)"
|
||||||
|
[ -z "$pids" ] && pids="$(ps 2>/dev/null | awk '$2=="telemt"{print $1}' || true)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$pids" ]; then
|
if [ -n "$pids" ]; then
|
||||||
|
|
@ -471,11 +592,12 @@ uninstall() {
|
||||||
say ">>> Stage 5: Purging configuration, data, and user"
|
say ">>> Stage 5: Purging configuration, data, and user"
|
||||||
$SUDO rm -rf "$CONFIG_DIR" "$WORK_DIR"
|
$SUDO rm -rf "$CONFIG_DIR" "$WORK_DIR"
|
||||||
$SUDO rm -f "$CONFIG_FILE"
|
$SUDO rm -f "$CONFIG_FILE"
|
||||||
if [ "$CONFIG_PARENT_DIR" != "$CONFIG_DIR" ] && [ "$CONFIG_PARENT_DIR" != "." ] && [ "$CONFIG_PARENT_DIR" != "/" ]; then
|
sleep 1
|
||||||
$SUDO rmdir "$CONFIG_PARENT_DIR" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
$SUDO userdel telemt 2>/dev/null || $SUDO deluser telemt 2>/dev/null || true
|
$SUDO userdel telemt 2>/dev/null || $SUDO deluser telemt 2>/dev/null || true
|
||||||
|
|
||||||
|
if check_os_entity group telemt; then
|
||||||
$SUDO groupdel telemt 2>/dev/null || $SUDO delgroup telemt 2>/dev/null || true
|
$SUDO groupdel telemt 2>/dev/null || $SUDO delgroup telemt 2>/dev/null || true
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
say "Note: Configuration and user kept. Run with 'purge' to remove completely."
|
say "Note: Configuration and user kept. Run with 'purge' to remove completely."
|
||||||
fi
|
fi
|
||||||
|
|
@ -493,7 +615,17 @@ case "$ACTION" in
|
||||||
say "Starting installation of $BIN_NAME (Version: $TARGET_VERSION)"
|
say "Starting installation of $BIN_NAME (Version: $TARGET_VERSION)"
|
||||||
|
|
||||||
say ">>> Stage 1: Verifying environment and dependencies"
|
say ">>> Stage 1: Verifying environment and dependencies"
|
||||||
verify_common; verify_install_deps
|
verify_common
|
||||||
|
verify_install_deps
|
||||||
|
|
||||||
|
if is_config_exists && [ "$PORT_PROVIDED" -eq 0 ]; then
|
||||||
|
ext_port="$($SUDO awk -F'=' '/^[ \t]*port[ \t]*=/ {gsub(/[^0-9]/, "", $2); print $2; exit}' "$CONFIG_FILE" 2>/dev/null || true)"
|
||||||
|
if [ -n "$ext_port" ]; then
|
||||||
|
SERVER_PORT="$ext_port"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
check_port_availability
|
||||||
|
|
||||||
if [ "$TARGET_VERSION" != "latest" ]; then
|
if [ "$TARGET_VERSION" != "latest" ]; then
|
||||||
TARGET_VERSION="${TARGET_VERSION#v}"
|
TARGET_VERSION="${TARGET_VERSION#v}"
|
||||||
|
|
@ -544,7 +676,7 @@ case "$ACTION" in
|
||||||
say ">>> Stage 5: Installing binary"
|
say ">>> Stage 5: Installing binary"
|
||||||
install_binary "$EXTRACTED_BIN" "${INSTALL_DIR}/${BIN_NAME}"
|
install_binary "$EXTRACTED_BIN" "${INSTALL_DIR}/${BIN_NAME}"
|
||||||
|
|
||||||
say ">>> Stage 6: Generating configuration"
|
say ">>> Stage 6: Generating/Updating configuration"
|
||||||
install_config
|
install_config
|
||||||
|
|
||||||
say ">>> Stage 7: Installing and starting service"
|
say ">>> Stage 7: Installing and starting service"
|
||||||
|
|
@ -571,11 +703,14 @@ case "$ACTION" in
|
||||||
printf ' rc-service %s status\n\n' "$SERVICE_NAME"
|
printf ' rc-service %s status\n\n' "$SERVICE_NAME"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
API_LISTEN="$($SUDO awk -F'"' '/^[ \t]*listen[ \t]*=/ {print $2; exit}' "$CONFIG_FILE" 2>/dev/null || true)"
|
||||||
|
API_LISTEN="${API_LISTEN:-127.0.0.1:9091}"
|
||||||
|
|
||||||
printf 'To get your user connection links (for Telegram), run:\n'
|
printf 'To get your user connection links (for Telegram), run:\n'
|
||||||
if command -v jq >/dev/null 2>&1; then
|
if command -v jq >/dev/null 2>&1; then
|
||||||
printf ' curl -s http://127.0.0.1:9091/v1/users | jq -r '\''.data[] | "User: \\(.username)\\n\\(.links.tls[0] // empty)\\n"'\''\n'
|
printf ' curl -s http://%s/v1/users | jq -r '\''.data[]? | "User: \\(.username)\\n\\(.links.tls[0] // empty)\\n"'\''\n' "$API_LISTEN"
|
||||||
else
|
else
|
||||||
printf ' curl -s http://127.0.0.1:9091/v1/users\n'
|
printf ' curl -s http://%s/v1/users\n' "$API_LISTEN"
|
||||||
printf ' (Tip: Install '\''jq'\'' for a much cleaner output)\n'
|
printf ' (Tip: Install '\''jq'\'' for a much cleaner output)\n'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue