Compare commits

...

10 Commits

Author SHA1 Message Date
mammuthus 24eb507e91 Merge 9b64d2ee17 into 977ee53b72 2026-04-05 16:00:34 +03:00
Alexey 977ee53b72 Config Fallback + Working Directory Setup
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
2026-04-05 14:40:17 +03:00
Alexey 8fe6fcb7eb ME2DC Fast for unstoppable init 2026-04-05 13:10:35 +03:00
mammuthus 9b64d2ee17 style(metrics): apply rustfmt for build_info additions 2026-04-03 07:49:37 +00:00
mammuthus 873618ce53 metrics: export telemt_build_info version metric 2026-04-02 18:14:50 +00:00
Alexey f6704d7d65 Update README.md 2026-04-02 10:59:19 +03:00
Alexey 3d20002e56 Update README.md 2026-04-02 10:58:50 +03:00
Alexey 8fcd0fa950 Merge pull request #618 from SysAdminKo/main
Переработка документации CONFIG_PARAMS
2026-04-01 17:24:22 +03:00
SysAdminKo 645e968778 Enhance CONFIG_PARAMS documentation with AI-assisted notes and detailed parameter descriptions. Update formatting for clarity and include examples for key configuration options. 2026-04-01 16:04:11 +03:00
Alexey b46216d357 Update README.md 2026-04-01 11:52:13 +03:00
6 changed files with 3265 additions and 342 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
***Löst Probleme, bevor andere überhaupt wissen, dass sie existieren*** / ***It solves problems before others even realize they exist*** ***Löst Probleme, bevor andere überhaupt wissen, dass sie existieren*** / ***It solves problems before others even realize they exist***
[**Telemt Chat in Telegram**](https://t.me/telemtrs) ### [**Telemt Chat in Telegram**](https://t.me/telemtrs)
**Telemt** is a fast, secure, and feature-rich server written in Rust: it fully implements the official Telegram proxy algo and adds many production-ready improvements such as: **Telemt** is a fast, secure, and feature-rich server written in Rust: it fully implements the official Telegram proxy algo and adds many production-ready improvements such as:
- [ME Pool + Reader/Writer + Registry + Refill + Adaptive Floor + Trio-State + Generation Lifecycle](https://github.com/telemt/telemt/blob/main/docs/model/MODEL.en.md) - [ME Pool + Reader/Writer + Registry + Refill + Adaptive Floor + Trio-State + Generation Lifecycle](https://github.com/telemt/telemt/blob/main/docs/model/MODEL.en.md)
+3058 -317
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -100,7 +100,7 @@ pub(crate) fn default_fake_cert_len() -> usize {
} }
pub(crate) fn default_tls_front_dir() -> String { pub(crate) fn default_tls_front_dir() -> String {
"tlsfront".to_string() "/etc/telemt/tlsfront".to_string()
} }
pub(crate) fn default_replay_check_len() -> usize { pub(crate) fn default_replay_check_len() -> usize {
@@ -302,7 +302,7 @@ pub(crate) fn default_me2dc_fallback() -> bool {
} }
pub(crate) fn default_me2dc_fast() -> bool { pub(crate) fn default_me2dc_fast() -> bool {
false true
} }
pub(crate) fn default_keepalive_interval() -> u64 { pub(crate) fn default_keepalive_interval() -> u64 {
@@ -558,7 +558,7 @@ pub(crate) fn default_beobachten_flush_secs() -> u64 {
} }
pub(crate) fn default_beobachten_file() -> String { pub(crate) fn default_beobachten_file() -> String {
"cache/beobachten.txt".to_string() "/etc/telemt/beobachten.txt".to_string()
} }
pub(crate) fn default_tls_new_session_tickets() -> u8 { pub(crate) fn default_tls_new_session_tickets() -> u8 {
+82 -15
View File
@@ -18,19 +18,38 @@ use crate::transport::middle_proxy::{
pub(crate) fn resolve_runtime_config_path( pub(crate) fn resolve_runtime_config_path(
config_path_cli: &str, config_path_cli: &str,
startup_cwd: &std::path::Path, startup_cwd: &std::path::Path,
config_path_explicit: bool,
) -> PathBuf { ) -> PathBuf {
let raw = PathBuf::from(config_path_cli); if config_path_explicit {
let absolute = if raw.is_absolute() { let raw = PathBuf::from(config_path_cli);
raw let absolute = if raw.is_absolute() {
} else { raw
startup_cwd.join(raw) } else {
}; startup_cwd.join(raw)
absolute.canonicalize().unwrap_or(absolute) };
return absolute.canonicalize().unwrap_or(absolute);
}
let etc_telemt = std::path::Path::new("/etc/telemt");
let candidates = [
startup_cwd.join("config.toml"),
startup_cwd.join("telemt.toml"),
etc_telemt.join("telemt.toml"),
etc_telemt.join("config.toml"),
];
for candidate in candidates {
if candidate.is_file() {
return candidate.canonicalize().unwrap_or(candidate);
}
}
startup_cwd.join("config.toml")
} }
/// Parsed CLI arguments. /// Parsed CLI arguments.
pub(crate) struct CliArgs { pub(crate) struct CliArgs {
pub config_path: String, pub config_path: String,
pub config_path_explicit: bool,
pub data_path: Option<PathBuf>, pub data_path: Option<PathBuf>,
pub silent: bool, pub silent: bool,
pub log_level: Option<String>, pub log_level: Option<String>,
@@ -39,6 +58,7 @@ pub(crate) struct CliArgs {
pub(crate) fn parse_cli() -> CliArgs { pub(crate) fn parse_cli() -> CliArgs {
let mut config_path = "config.toml".to_string(); let mut config_path = "config.toml".to_string();
let mut config_path_explicit = false;
let mut data_path: Option<PathBuf> = None; let mut data_path: Option<PathBuf> = None;
let mut silent = false; let mut silent = false;
let mut log_level: Option<String> = None; let mut log_level: Option<String> = None;
@@ -74,6 +94,20 @@ pub(crate) fn parse_cli() -> CliArgs {
s.trim_start_matches("--data-path=").to_string(), s.trim_start_matches("--data-path=").to_string(),
)); ));
} }
"--working-dir" => {
i += 1;
if i < args.len() {
data_path = Some(PathBuf::from(args[i].clone()));
} else {
eprintln!("Missing value for --working-dir");
std::process::exit(0);
}
}
s if s.starts_with("--working-dir=") => {
data_path = Some(PathBuf::from(
s.trim_start_matches("--working-dir=").to_string(),
));
}
"--silent" | "-s" => { "--silent" | "-s" => {
silent = true; silent = true;
} }
@@ -111,13 +145,11 @@ pub(crate) fn parse_cli() -> CliArgs {
i += 1; i += 1;
} }
} }
s if s.starts_with("--working-dir") => {
if !s.contains('=') {
i += 1;
}
}
s if !s.starts_with('-') => { s if !s.starts_with('-') => {
config_path = s.to_string(); if !matches!(s, "run" | "start" | "stop" | "reload" | "status") {
config_path = s.to_string();
config_path_explicit = true;
}
} }
other => { other => {
eprintln!("Unknown option: {}", other); eprintln!("Unknown option: {}", other);
@@ -128,6 +160,7 @@ pub(crate) fn parse_cli() -> CliArgs {
CliArgs { CliArgs {
config_path, config_path,
config_path_explicit,
data_path, data_path,
silent, silent,
log_level, log_level,
@@ -152,6 +185,7 @@ fn print_help() {
eprintln!( eprintln!(
" --data-path <DIR> Set data directory (absolute path; overrides config value)" " --data-path <DIR> Set data directory (absolute path; overrides config value)"
); );
eprintln!(" --working-dir <DIR> Alias for --data-path");
eprintln!(" --silent, -s Suppress info logs"); eprintln!(" --silent, -s Suppress info logs");
eprintln!(" --log-level <LEVEL> debug|verbose|normal|silent"); eprintln!(" --log-level <LEVEL> debug|verbose|normal|silent");
eprintln!(" --help, -h Show this help"); eprintln!(" --help, -h Show this help");
@@ -210,7 +244,7 @@ mod tests {
let target = startup_cwd.join("config.toml"); let target = startup_cwd.join("config.toml");
std::fs::write(&target, " ").unwrap(); std::fs::write(&target, " ").unwrap();
let resolved = resolve_runtime_config_path("config.toml", &startup_cwd); let resolved = resolve_runtime_config_path("config.toml", &startup_cwd, true);
assert_eq!(resolved, target.canonicalize().unwrap()); assert_eq!(resolved, target.canonicalize().unwrap());
let _ = std::fs::remove_file(&target); let _ = std::fs::remove_file(&target);
@@ -226,11 +260,44 @@ mod tests {
let startup_cwd = std::env::temp_dir().join(format!("telemt_cfg_path_missing_{nonce}")); let startup_cwd = std::env::temp_dir().join(format!("telemt_cfg_path_missing_{nonce}"));
std::fs::create_dir_all(&startup_cwd).unwrap(); std::fs::create_dir_all(&startup_cwd).unwrap();
let resolved = resolve_runtime_config_path("missing.toml", &startup_cwd); let resolved = resolve_runtime_config_path("missing.toml", &startup_cwd, true);
assert_eq!(resolved, startup_cwd.join("missing.toml")); assert_eq!(resolved, startup_cwd.join("missing.toml"));
let _ = std::fs::remove_dir(&startup_cwd); let _ = std::fs::remove_dir(&startup_cwd);
} }
#[test]
fn resolve_runtime_config_path_uses_startup_candidates_when_not_explicit() {
let nonce = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let startup_cwd = std::env::temp_dir().join(format!("telemt_cfg_startup_candidates_{nonce}"));
std::fs::create_dir_all(&startup_cwd).unwrap();
let telemt = startup_cwd.join("telemt.toml");
std::fs::write(&telemt, " ").unwrap();
let resolved = resolve_runtime_config_path("config.toml", &startup_cwd, false);
assert_eq!(resolved, telemt.canonicalize().unwrap());
let _ = std::fs::remove_file(&telemt);
let _ = std::fs::remove_dir(&startup_cwd);
}
#[test]
fn resolve_runtime_config_path_defaults_to_startup_config_when_none_found() {
let nonce = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let startup_cwd = std::env::temp_dir().join(format!("telemt_cfg_startup_default_{nonce}"));
std::fs::create_dir_all(&startup_cwd).unwrap();
let resolved = resolve_runtime_config_path("config.toml", &startup_cwd, false);
assert_eq!(resolved, startup_cwd.join("config.toml"));
let _ = std::fs::remove_dir(&startup_cwd);
}
} }
pub(crate) fn print_proxy_links(host: &str, port: u16, config: &ProxyConfig) { pub(crate) fn print_proxy_links(host: &str, port: u16, config: &ProxyConfig) {
+97 -6
View File
@@ -112,6 +112,7 @@ async fn run_inner(
.await; .await;
let cli_args = parse_cli(); let cli_args = parse_cli();
let config_path_cli = cli_args.config_path; let config_path_cli = cli_args.config_path;
let config_path_explicit = cli_args.config_path_explicit;
let data_path = cli_args.data_path; let data_path = cli_args.data_path;
let cli_silent = cli_args.silent; let cli_silent = cli_args.silent;
let cli_log_level = cli_args.log_level; let cli_log_level = cli_args.log_level;
@@ -123,7 +124,8 @@ async fn run_inner(
std::process::exit(1); std::process::exit(1);
} }
}; };
let config_path = resolve_runtime_config_path(&config_path_cli, &startup_cwd); let mut config_path =
resolve_runtime_config_path(&config_path_cli, &startup_cwd, config_path_explicit);
let mut config = match ProxyConfig::load(&config_path) { let mut config = match ProxyConfig::load(&config_path) {
Ok(c) => c, Ok(c) => c,
@@ -133,11 +135,100 @@ async fn run_inner(
std::process::exit(1); std::process::exit(1);
} else { } else {
let default = ProxyConfig::default(); let default = ProxyConfig::default();
std::fs::write(&config_path, toml::to_string_pretty(&default).unwrap()).unwrap();
eprintln!( let serialized = match toml::to_string_pretty(&default)
"[telemt] Created default config at {}", .or_else(|_| toml::to_string(&default))
config_path.display() {
); Ok(value) => Some(value),
Err(serialize_error) => {
eprintln!(
"[telemt] Warning: failed to serialize default config: {}",
serialize_error
);
None
}
};
if config_path_explicit {
if let Some(serialized) = serialized.as_ref() {
if let Err(write_error) = std::fs::write(&config_path, serialized) {
eprintln!(
"[telemt] Error: failed to create explicit config at {}: {}",
config_path.display(),
write_error
);
std::process::exit(1);
}
eprintln!(
"[telemt] Created default config at {}",
config_path.display()
);
} else {
eprintln!(
"[telemt] Warning: running with in-memory default config without writing to disk"
);
}
} else {
let system_dir = std::path::Path::new("/etc/telemt");
let system_config_path = system_dir.join("telemt.toml");
let startup_config_path = startup_cwd.join("config.toml");
let mut persisted = false;
if let Some(serialized) = serialized.as_ref() {
match std::fs::create_dir_all(system_dir) {
Ok(()) => match std::fs::write(&system_config_path, serialized) {
Ok(()) => {
config_path = system_config_path;
eprintln!(
"[telemt] Created default config at {}",
config_path.display()
);
persisted = true;
}
Err(write_error) => {
eprintln!(
"[telemt] Warning: failed to write default config at {}: {}",
system_config_path.display(),
write_error
);
}
},
Err(create_error) => {
eprintln!(
"[telemt] Warning: failed to create {}: {}",
system_dir.display(),
create_error
);
}
}
if !persisted {
match std::fs::write(&startup_config_path, serialized) {
Ok(()) => {
config_path = startup_config_path;
eprintln!(
"[telemt] Created default config at {}",
config_path.display()
);
persisted = true;
}
Err(write_error) => {
eprintln!(
"[telemt] Warning: failed to write default config at {}: {}",
startup_config_path.display(),
write_error
);
}
}
}
}
if !persisted {
eprintln!(
"[telemt] Warning: running with in-memory default config without writing to disk"
);
}
}
default default
} }
} }
+24
View File
@@ -234,6 +234,17 @@ async fn render_metrics(stats: &Stats, config: &ProxyConfig, ip_tracker: &UserIp
let me_allows_normal = telemetry.me_level.allows_normal(); let me_allows_normal = telemetry.me_level.allows_normal();
let me_allows_debug = telemetry.me_level.allows_debug(); let me_allows_debug = telemetry.me_level.allows_debug();
let _ = writeln!(
out,
"# HELP telemt_build_info Build information for the running telemt binary"
);
let _ = writeln!(out, "# TYPE telemt_build_info gauge");
let _ = writeln!(
out,
"telemt_build_info{{version=\"{}\"}} 1",
env!("CARGO_PKG_VERSION")
);
let _ = writeln!(out, "# HELP telemt_uptime_seconds Proxy uptime"); let _ = writeln!(out, "# HELP telemt_uptime_seconds Proxy uptime");
let _ = writeln!(out, "# TYPE telemt_uptime_seconds gauge"); let _ = writeln!(out, "# TYPE telemt_uptime_seconds gauge");
let _ = writeln!(out, "telemt_uptime_seconds {:.1}", stats.uptime_secs()); let _ = writeln!(out, "telemt_uptime_seconds {:.1}", stats.uptime_secs());
@@ -2903,6 +2914,10 @@ mod tests {
let output = render_metrics(&stats, &config, &tracker).await; let output = render_metrics(&stats, &config, &tracker).await;
assert!(output.contains(&format!(
"telemt_build_info{{version=\"{}\"}} 1",
env!("CARGO_PKG_VERSION")
)));
assert!(output.contains("telemt_connections_total 2")); assert!(output.contains("telemt_connections_total 2"));
assert!(output.contains("telemt_connections_bad_total 1")); assert!(output.contains("telemt_connections_bad_total 1"));
assert!(output.contains("telemt_handshake_timeouts_total 1")); assert!(output.contains("telemt_handshake_timeouts_total 1"));
@@ -2995,6 +3010,7 @@ mod tests {
let tracker = UserIpTracker::new(); let tracker = UserIpTracker::new();
let config = ProxyConfig::default(); let config = ProxyConfig::default();
let output = render_metrics(&stats, &config, &tracker).await; let output = render_metrics(&stats, &config, &tracker).await;
assert!(output.contains("# TYPE telemt_build_info gauge"));
assert!(output.contains("# TYPE telemt_uptime_seconds gauge")); assert!(output.contains("# TYPE telemt_uptime_seconds gauge"));
assert!(output.contains("# TYPE telemt_connections_total counter")); assert!(output.contains("# TYPE telemt_connections_total counter"));
assert!(output.contains("# TYPE telemt_connections_bad_total counter")); assert!(output.contains("# TYPE telemt_connections_bad_total counter"));
@@ -3052,6 +3068,14 @@ mod tests {
.unwrap() .unwrap()
.contains("telemt_connections_total 3") .contains("telemt_connections_total 3")
); );
assert!(
std::str::from_utf8(body.as_ref())
.unwrap()
.contains(&format!(
"telemt_build_info{{version=\"{}\"}} 1",
env!("CARGO_PKG_VERSION")
))
);
config.general.beobachten = true; config.general.beobachten = true;
config.general.beobachten_minutes = 10; config.general.beobachten_minutes = 10;