From f9910fb29ef7cf791339e6addb049bc78a22c295 Mon Sep 17 00:00:00 2001 From: Alexey <247128645+axkurcom@users.noreply.github.com> Date: Sat, 22 Aug 2026 13:50:34 +0300 Subject: [PATCH] Manifest Revision + Include Ownership + Atomic Reload Reservation in Docs --- docs/Architecture/API/API.md | 15 ++++++++------- docs/Config_params/CONFIG_PARAMS.en.md | 24 ++++++++++++------------ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/docs/Architecture/API/API.md b/docs/Architecture/API/API.md index c05ce55..f1da047 100644 --- a/docs/Architecture/API/API.md +++ b/docs/Architecture/API/API.md @@ -1393,7 +1393,7 @@ Returns the current editable config sections as TOML-shaped JSON, plus the curre } ``` -Top-level sections absent from the config file are absent from the response. Only `GET` and `PATCH` are accepted; any other method returns `405 Method Not Allowed` with `Allow: GET, PATCH`. +The response is built from the validated, include-expanded configuration and may therefore contain normalized defaults or synthesized listeners that are absent from the root file. Only `GET` and `PATCH` are accepted; any other method returns `405 Method Not Allowed` with `Allow: GET, PATCH`. --- @@ -1409,7 +1409,7 @@ Applies a sparse patch to the editable config sections. The merged config is ful | --- | --- | --- | | `Authorization` | when configured | Same token as all other endpoints. | | `Content-Type: application/json` | recommended | Not enforced, but body must be valid JSON. | -| `If-Match: ` | no | Optimistic concurrency. `` is the `revision` value from `GET /v1/config` or `config_hash` from `GET /v1/system/info`. If supplied and it does not match the current on-disk revision, returns `409 revision_conflict`. If omitted, the patch applies unconditionally. | +| `If-Match: ` | no | Optimistic concurrency. `` is the `revision` value from `GET /v1/config` or `config_hash` from `GET /v1/system/info`. It covers the complete recursive include graph. If supplied and it does not match the current source manifest, returns `409 revision_conflict`. If omitted, the patch applies unconditionally. | **Editable sections:** `general`, `timeouts`, `censorship`, `upstreams`, `dc_overrides`, plus partially editable `server` (only nested `listeners`). @@ -1422,7 +1422,7 @@ Applies a sparse patch to the editable config sections. The merged config is ful | `server` with keys other than `listeners` | `400` | `field_not_editable` | | Object with no editable key | `400` | `bad_request` | -**Merge semantics:** tables are deep-merged field-by-field; arrays and scalar values replace the existing value wholesale. File comments and untouched sections are preserved. +**Merge semantics:** tables are deep-merged field-by-field; arrays and scalar values replace the existing value wholesale. A mutation is written to the single source file that owns every touched semantic section. File comments, the root file when it is not the owner, and all other include files are preserved. A target split across sources, a patch spanning multiple owners, or an include directive nested inside a TOML table returns `409 config_patch_not_atomic` without writing any file. **Validation:** the merged config is deserialized into the full `ProxyConfig` type and validated before writing. Failures return `400` with a descriptive message; the file is not modified. @@ -1437,7 +1437,7 @@ Applies a sparse patch to the editable config sections. The merged config is ful | `timeout_secs=1..3600` | for `reload=drain` | Bounded old-generation drain interval. Invalid with `reload=instant`. | | `failure_policy=keep_new\|rollback` | no | Defaults to `keep_new`. `rollback` applies only through the activation barrier, before old-generation teardown. | -Without a `reload` query parameter, the endpoint preserves the legacy behavior: it writes the patch and the file watcher applies only supported hot fields. +Without a `reload` query parameter, the endpoint writes the patch and the file watcher applies only supported hot fields. With a reload query, coordinator capacity and status are reserved before the source file is replaced. Runtime-owned changes enqueue the validated immutable snapshot after the atomic write. A process-only patch is persisted with `200`, reports its deferred fields, and does not create a reload operation. **Success `200` or `202` response body** (`data` field of the standard envelope): ```json @@ -1459,12 +1459,12 @@ Without a `reload` query parameter, the endpoint preserves the legacy behavior: } ``` -- `revision` — SHA-256 hex of the config file after the write. +- `revision` — SHA-256 hex of the canonical source manifest after the write, including every recursive include path and its raw bytes. - `restart_required` — legacy file-watcher classification retained for compatibility. - `runtime_reload_required` — reports whether a full Maestro generation reload is needed for runtime effect. - `process_restart_required` and `deferred_process_fields` — report process-owned sockets or paths that remain unchanged by an in-process reload. - `changed` — list of top-level section names that differed. -- `reload` — accepted operation metadata; omitted when no reload query was supplied. +- `reload` — accepted operation metadata; omitted without a reload query and for process-only patches that cannot change the active generation. **Status codes:** @@ -1481,6 +1481,7 @@ Without a `reload` query parameter, the endpoint preserves the legacy behavior: | `405` | `method_not_allowed` | Method other than `GET` or `PATCH` used on `/v1/config`. | | `409` | `revision_conflict` | `If-Match` header supplied but does not match current revision. | | `409` | `reload_in_progress` | Another runtime reload is active; the patch is not written. | +| `409` | `config_patch_not_atomic` | Touched semantic sections have multiple source owners or cannot be mutated as one source-file transaction. | | `500` | `internal_error` | I/O or serialization failure. | **curl example:** @@ -1515,7 +1516,7 @@ The endpoint returns `202` with `ReloadAccepted`. A concurrent non-terminal relo Returns `ReloadStatus` with `state` equal to `accepted`, `preparing`, `activating`, `draining`, `succeeded`, `rolled_back`, or `failed`. Terminal statuses include `finished_at_epoch_secs`; failures include `error`. Successful activation may include `warnings` for old-generation cleanup failures and `deferred_process_fields` for process-owned settings. -Runtime generation activation rebuilds statistics, upstream routing, replay and buffer state, TLS-front cache, IP tracking, admission/route state, and Middle-End orchestration. Per-user quota accounting is process-scoped and remains continuous across generations. API, metrics, client TCP/Unix listeners, PID ownership, and logging remain process-scoped; changed bind/path fields are reported as deferred and do not cause Maestro to invoke systemd, containerd, or another process supervisor. +Runtime generation activation rebuilds statistics, upstream routing, replay and buffer state, TLS-front cache, IP tracking, admission/route state, and Middle-End orchestration. Per-user quota accounting is process-scoped and remains continuous across generations. API, metrics, client TCP/Unix listeners, listener MSS profiles, PID ownership, and logging remain process-scoped. Desired changes to those fields are reported as deferred and are overlaid with the active values before runtime preparation, so the published generation remains an effective-state view. Maestro does not invoke systemd, containerd, or another process supervisor. Reload preparation requires every configured TLS-front domain to have a non-default cached profile and requires a ready Middle-End pool when direct fallback is disabled. A candidate that does not satisfy either readiness condition fails without replacing the active generation. diff --git a/docs/Config_params/CONFIG_PARAMS.en.md b/docs/Config_params/CONFIG_PARAMS.en.md index b829ae1..cb9dd34 100644 --- a/docs/Config_params/CONFIG_PARAMS.en.md +++ b/docs/Config_params/CONFIG_PARAMS.en.md @@ -1981,8 +1981,8 @@ This document lists all configuration keys accepted by `config.toml`. client_mss = "tspu" ``` ## client_mss_bulk - - **Constraints / validation**: `String`. Same grammar as [`client_mss`](#client_mss) (empty/omitted, presets `"extreme-low"`/`"tspu"`/`"2in8"`, or a decimal in `88..=4096`). - - **Description**: Enables separated MSS handling for `ServerHello` and for all other packets. The listener uses `client_mss_bulk` from the start of the connection, including when the client sends `ClientHello`. Telemt sends the initial authenticated FakeTLS response (`ServerHello`) in chunks no larger than `client_mss`. Normal MTProto traffic then continues with `client_mss_bulk`, without changing MSS on an established connection. This keeps the smaller `ServerHello` chunks while avoiding small segments for all later traffic. When this option is empty or omitted, `client_mss` applies to the whole connection. **Linux-only**. + - **Constraints / validation**: Linux-only `String`. Same grammar as [`client_mss`](#client_mss) (empty/omitted, presets `"extreme-low"`/`"tspu"`/`"2in8"`, or a decimal in `88..=4096`). A non-empty value requires at least one listener with an effective `client_mss`, and it must be greater than every participating listener's handshake value. A listener may use `client_mss = ""` as an explicit opt-out. + - **Description**: Enables an experimental two-size profile. The listener uses `client_mss_bulk` from the start of the connection, including when the client sends `ClientHello`. Telemt sends the initial authenticated FakeTLS response (`ServerHello`) with best-effort userspace writes no larger than `client_mss`; normal MTProto writes then continue without the low chunk size. TCP is a byte stream: `MSG_EOR`, TCP offloads, loss, and retransmission provide no guarantee that write boundaries remain packet, SKB, or retransmitted-segment boundaries. When this option is empty or omitted, `client_mss` remains the kernel MSS for the whole connection and is the only strong segment upper-bound contract. Changes require listener restart/rebind. - **Example**: ```toml @@ -2311,15 +2311,15 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche | [`ip`](#ip) | `IpAddr` | — | `✘` | | [`port`](#port-serverlisteners) | `u16` | `server.port` | `✘` | | [`client_mss`](#client_mss-serverlisteners) | `String` | `[server].client_mss` | `✘` | -| [`synlimit`](#synlimit-serverlisteners) | `false`, `"iptables"`, `"nftables"`, or `"pf"` | `false` | `✔` | -| [`synlimit_seconds`](#synlimit_seconds-serverlisteners) | `u32` | `60` | `✔` | -| [`synlimit_hitcount`](#synlimit_hitcount-serverlisteners) | `u32` | `48` | `✔` | -| [`synlimit_burst`](#synlimit_burst-serverlisteners) | `u32` | `24` | `✔` | -| [`synlimit_ios_seconds`](#synlimit_ios_seconds-serverlisteners) | `u32` | `1` | `✔` | -| [`synlimit_ios_hitcount`](#synlimit_ios_hitcount-serverlisteners) | `u32` | `12` | `✔` | -| [`synlimit_ios_burst`](#synlimit_ios_burst-serverlisteners) | `u32` | `24` | `✔` | -| [`synlimit_hashlimit_expire_ms`](#synlimit_hashlimit_expire_ms-serverlisteners) | `u32` | `60000` | `✔` | -| [`synlimit_hashlimit_size`](#synlimit_hashlimit_size-serverlisteners) | `u32` | `32768` | `✔` | +| [`synlimit`](#synlimit-serverlisteners) | `false`, `"iptables"`, `"nftables"`, or `"pf"` | `false` | `✘` | +| [`synlimit_seconds`](#synlimit_seconds-serverlisteners) | `u32` | `60` | `✘` | +| [`synlimit_hitcount`](#synlimit_hitcount-serverlisteners) | `u32` | `48` | `✘` | +| [`synlimit_burst`](#synlimit_burst-serverlisteners) | `u32` | `24` | `✘` | +| [`synlimit_ios_seconds`](#synlimit_ios_seconds-serverlisteners) | `u32` | `1` | `✘` | +| [`synlimit_ios_hitcount`](#synlimit_ios_hitcount-serverlisteners) | `u32` | `12` | `✘` | +| [`synlimit_ios_burst`](#synlimit_ios_burst-serverlisteners) | `u32` | `24` | `✘` | +| [`synlimit_hashlimit_expire_ms`](#synlimit_hashlimit_expire_ms-serverlisteners) | `u32` | `60000` | `✘` | +| [`synlimit_hashlimit_size`](#synlimit_hashlimit_size-serverlisteners) | `u32` | `32768` | `✘` | | [`announce`](#announce) | `String` | — | `✘` | | [`announce_ip`](#announce_ip) | `IpAddr` | — | `✘` | | [`proxy_protocol`](#proxy_protocol) | `bool` | — | `✘` | @@ -2357,7 +2357,7 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche ``` ## synlimit (server.listeners) - **Constraints / validation**: `false`, `"iptables"`, `"nftables"`, or `"pf"`. Omitted or `false` disables SYN limiting for this listener. - - **Description**: Installs per-listener firewall rules for the listener port. `"iptables"` uses Linux `iptables`/`ip6tables` filter rules with the `hashlimit`, `length`, and TTL/hop-limit matches. `"nftables"` uses Linux Telemt-owned tables with per-source `meter` rules and equivalent IPv4/IPv6 classifiers. These Linux rules are inserted early in `INPUT`, accept under-limit SYN packets, and reject over-limit SYN packets with TCP RST so clients retry promptly instead of waiting for a silent DROP timeout. `"pf"` uses FreeBSD PF source tracking in a Telemt anchor with `max-src-conn-rate`; PF applies this rate after TCP three-way handshake completion, accepts under-limit connections, and rejects over-limit new connections until the source rate falls back below the configured window. The generic bucket is controlled by `synlimit_seconds`, `synlimit_hitcount`, and `synlimit_burst` on Linux; PF maps `synlimit_hitcount / synlimit_seconds` to `max-src-conn-rate` and has no direct equivalents for `synlimit_burst`, `synlimit_ios_*`, or `synlimit_hashlimit_*`. Rules are reconciled at runtime and removed during graceful Telemt shutdown; `SIGKILL` cannot be cleaned up by the process. Linux requires CAP_NET_ADMIN. FreeBSD requires root and a main PF ruleset hook such as `anchor "telemt_synlimit/*"`. `synlimit*` changes hot-reload for existing listener endpoints; changing listener `ip` or `port` still requires restart/rebind. + - **Description**: Installs startup-owned per-listener firewall rules for the listener port. Linux accepts only `"iptables"` or `"nftables"`; FreeBSD accepts only `"pf"`; other platforms accept only `false`. Linux netfilter rules accept under-limit SYN packets and reject excess packets with TCP RST. PF renders explicit `inet`/`inet6` rules and uses native `max-src-conn-rate`; excess state-creating packets are silently dropped. Startup fails before accept loops if privileges, backend validation, stale-candidate cleanup, or rule application fails. Every `synlimit*` change requires process restart. Combining enabled SYN limiting with `--run-as-user` or `--run-as-group` is rejected until a separate privileged firewall helper exists. Rules are removed during graceful shutdown; `SIGKILL` cannot be cleaned up by the process. Linux requires CAP_NET_ADMIN. FreeBSD requires root and a main PF ruleset hook such as `anchor "telemt_synlimit/*"`. - **Operator note**: Telemt does not persist rules with `iptables-persistent`, write `/etc/sysctl.d`, edit systemd limits, or modify `client_mss`. Apply host-level tuning manually if your deployment policy requires it. - **Example**: