Web Limits Wildcard in Docs

This commit is contained in:
Alexey
2026-10-28 21:29:00 +03:00
parent 4eef3d8608
commit 1870ef82a9
3 changed files with 221 additions and 34 deletions
+99 -14
View File
@@ -27,6 +27,7 @@ Dieses Dokument listet alle Konfigurationsschlüssel auf, die `config.toml` akze
- [web](#web)
- [web.debug](#webdebug)
- [web.limits](#weblimits)
- [web.limits.wildcard](#weblimitswildcard)
- [web.timeouts](#webtimeouts)
- [web.vhosts](#webvhosts)
- [web.vhosts.decoy](#webvhostsdecoy)
@@ -332,6 +333,7 @@ Dieses Dokument listet alle Konfigurationsschlüssel auf, die `config.toml` akze
data_path = "/var/lib/telemt"
```
## quota_state_path
- **Periodische Quoten**: Diese Datei speichert auch die UTC-Kalendermarken je Benutzer. Ist [`user_quota_periods`](#user_quota_periods) nicht leer, verhindert ein ungültiger vorhandener Zustand den Start; eine fehlende Datei beginnt mit frischem Zustand. Auch die erste dauerhafte Veröffentlichung muss vor Abschluss des Starts erfolgreich sein.
- **Einschränkungen / Validierung**: `Path`. Relative Pfade werden aus dem Arbeitsverzeichnis des Prozesses aufgelöst.
- **Beschreibung**: JSON-Statusdatei, die verwendet wird, um den Kontingentverbrauch pro User während der Runtime beizubehalten.
- **Beispiel**:
@@ -696,6 +698,7 @@ Dieses Dokument listet alle Konfigurationsschlüssel auf, die `config.toml` akze
direct_relay_copy_buf_c2s_bytes = 65536
```
## direct_relay_copy_buf_s2c_bytes
- **Flush-Verhalten**: In beiden Richtungen treibt Direct Adaptive Copy das Leeren gepufferter Ausgaben voran, sobald die Quelle wartet, ohne EOF oder ein weiteres Quellpaket abzuwarten. TLS-Framing, konfigurierte Puffergrenzen und Quotendurchsetzung bleiben unverändert.
- **Einschränkungen / Validierung**: Muss innerhalb von `8192..=2097152` (Bytes) liegen.
- **Beschreibung**: Puffergröße für DC->Client-Richtung im direkten Relay kopieren.
- **Beispiel**:
@@ -1119,6 +1122,9 @@ Dieses Dokument listet alle Konfigurationsschlüssel auf, die `config.toml` akze
disable_colors = false
```
## me_socks_kdf_policy
Gültige öffentliche SOCKS-Werte `BND.ADDR/BND.PORT` mit einem Port ungleich null liefern das vollständige KDF-Quelltupel vor konfigurierten/ermittelten NAT-Daten. Die Familie muss dem logischen ME-Ziel entsprechen, unabhängig von der TCP-Familie zur SOCKS-Verbindung. IPv4-mapped IPv6-Ziele gelten als IPv4 und erfordern ein natives IPv4-BND-Tupel; IPv4-compatible Ziele bleiben IPv6. `strict` weist fehlendes/ungültiges BND ab, `compat` behält den bisherigen Fallback. KDF-Felder und Ports, SOCKS-CONNECT-Zielcodierung, Prüfung öffentlicher Adressen und STUN bleiben unverändert.
- **Einschränkungen / Validierung**: `"strict"` oder `"compat"`.
- **Beschreibung**: SOCKS-gebundene KDF-Fallback-Richtlinie für Middle-End-Handshake.
- **Beispiel**:
@@ -2062,6 +2068,9 @@ Dieses Dokument listet alle Konfigurationsschlüssel auf, die `config.toml` akze
proxy_protocol_trusted_cidrs = ["127.0.0.1/32", "10.0.0.0/8"]
```
## metrics_port
Bei allein gesetztem `metrics_port` versucht Telemt, IPv4- und IPv6-Loopback zu binden. `metrics_listen` ersetzt diese Bindung. Docker-Portfreigabe allein macht einen Loopback-Listener nicht erreichbar: Konfigurieren Sie eine über das Containernetz erreichbare Bindung und erlauben Sie den direkten Scraper-/Docker-Peer in der Whitelist.
- **Einschränkungen / Validierung**: `u16` (optional).
- **Beschreibung**: Prometheus-kompatibler Metrik-Endpunktport. Wenn gesetzt, wird der Metrik-Listener aktiviert (Bindungsverhalten kann durch `metrics_listen` überschrieben werden).
- **Beispiel**:
@@ -2080,6 +2089,9 @@ Dieses Dokument listet alle Konfigurationsschlüssel auf, die `config.toml` akze
metrics_listen = "127.0.0.1:9090"
```
## metrics_whitelist
Die Autorisierung prüft die IP des direkten Peers in Originalform und bei IPv4-mapped IPv6 auch in kanonischer IPv4-Form. Explizite IPv6-CIDRs bleiben gültig. Forwarded-Header gewähren keinen Zugriff.
- **Einschränkungen / Validierung**: `IpNetwork[]`.
- **Beschreibung**: CIDR-Allowlist für Metrik-Endpunktzugriff.
- **Beispiel**:
@@ -2456,6 +2468,9 @@ Hinweis: Dieser Abschnitt akzeptiert auch den Legacy-Alias `[server.admin_api]`
synlimit_burst = 24
```
## synlimit_ios_seconds (server.listeners)
Dies ist eine Paketform-Heuristik, keine authentifizierte Apple-Geräteerkennung. IPv4-SYN-Pakete der Länge `60` verwenden den allgemeinen Bucket; daraus folgt keine universelle Lösung für iOS oder die Rückkehr aus dem Hintergrund.
- **Einschränkungen / Validierung**: `u32`, muss `> 0` sein. Der Default ist `1`.
- **Beschreibung**: Token-Bucket-Intervall für SYN-Pakete, die dem iOS-ähnlichen Classifier entsprechen. IPv4 entspricht der Paketlänge `64` und TTL `< 65`; IPv6 entspricht der Paketlänge `84` und dem Hop-Limit `< 65`.
- **Beispiel**:
@@ -2589,6 +2604,7 @@ Der WEB-Modus transportiert MTProxy-Datenverkehr von Telegram Desktop über HTTP
| Schlüssel | Typ | Default | Hot-Reload |
| --- | --- | --- | --- |
| `enabled` | `bool` | `false` | `✔` |
| `allow_wildcard_hosts` | `bool` | `false` | `✘` |
| `carrier` | `"https"`, `"https-lanes"`, `"websocket"` oder `"websocket-lanes"` | `"https"` | `✔` |
| `carriers` | `false` oder ein nicht leeres Array eindeutiger Carrier | `false` | `✔` |
| `carrier_learning` | `bool` | `true` | `✔` |
@@ -2673,11 +2689,11 @@ Diese prozessweiten Obergrenzen begrenzen alle WEB-Register, Warteschlangen, Req
| `control_bytes_global` | `usize` | `16777216` | Prozessweites, nur für Control Frames verfügbares Byte-Budget. |
| `max_bootstraps_global` | `usize` | `512` | Prozessweit aktive Bootstrap-Zugangsdaten. |
| `max_bootstraps_per_ip` | `usize` | `64` | Aktive Bootstrap-Zugangsdaten pro Client-IP. |
| `max_vhosts` | `usize` | `8` | Konfigurierte virtuelle WEB-Hosts. |
| `max_profiles` | `usize` | `32` | WEB-Profile über alle vhosts. |
| `max_static_files` | `usize` | `4096` | Einträge statischer Snapshots über alle vhosts. |
| `max_vhosts` | `usize` | `8` | Konkrete exakte WEB-Hostnamen aller Gruppen; jeder exakte `hosts`-Eintrag zählt einzeln. Wildcard-Selektoren zählen zu `max_rules`. |
| `max_profiles` | `usize` | `32` | Exakte Host-/Profilbindungen plus ein gemeinsamer Profilsatz je Wildcard-Quellgruppe. |
| `max_static_files` | `usize` | `4096` | Statische Snapshot-Einträge aller Gruppen; ein gemeinsamer Gruppensnapshot zählt einmal. |
| `max_static_file_bytes` | `usize` | `8388608` | Maximale Größe einer statischen Datei. |
| `max_static_bytes` | `usize` | `67108864` | Bytes statischer Snapshots über alle vhosts. |
| `max_static_bytes` | `usize` | `67108864` | Statische Snapshot-Bytes aller Gruppen; ein gemeinsamer Gruppensnapshot zählt einmal. |
| `debug_records_capacity` | `usize` | `65536` | Maximale Zahl gespeicherter WEB-Debugdatensätze. |
| `debug_bytes_global` | `usize` | `67108864` | Globale Byte-Obergrenze für gespeicherte und in Verarbeitung befindliche WEB-Debugdaten; mindestens 4096. |
| `memory_envelope_bytes` | `usize` | `1342177280` | Deklarierter Rahmen für HTTP-Heads, Bodys, gemeinsame Queues/WebSocket-I/O, Lane-Zustand, Carrier-Learning, statische Snapshots und begrenzte Debug-/Statuspuffer; maximal 4 GiB. |
@@ -2713,31 +2729,77 @@ Sofern eine Zeile nichts anderes angibt, werden Timeouts in Sekunden angegeben u
| `carrier_negotiation_deadlines_secs` | `[u64; 4]` | `[3, 5, 8, 12]` | `✔` | Streng steigende kumulative Offsets: Die Bridge verwendet sie vor ihrem ersten `/session`-Request, der Server bei Annahme des ersten automatischen Versuchs. Die Checkpoints für ein bis vier Kandidaten sind `[d3]`, `[d0, d3]`, `[d0, d1, d3]` und `[d0, d1, d2, d3]`; der letzte Kandidat verwendet immer `d3`. |
| `carrier_learning_secs` | `u64` | `600` | `✔` | Feste Lebensdauer zweier prozesslokaler Evidenzfenster; Bereich `2..=86400`. |
| `bootstrap_lifetime_secs` | `u64` | `120` | `✔` | Lebensdauer ungenutzter Bootstraps und geschlossener Token-Replay-Marker. |
| `reconnect_grace_secs` | `u64` | `120` | `✔` | Maximale Carrier-Inaktivität bis zum Schließen der Sitzung. |
| `reconnect_grace_secs` | `u64` | `120` | `✔` | Maximale bestätigte Peer-Inaktivität bis zum Schließen der Sitzung; leere Polls und alleiniger Backend-Fortschritt verlängern diese Frist nicht. |
| `http_idle_secs` | `u64` | `75` | `✔` | Idle-Grenze zwischen HTTP-Austauschvorgängen und bei ausbleibendem Fortschritt eines bereits ausgegebenen Response-Bodys. Explizit begrenzte Request-Body-, Long-Poll-, Decoy- und ausstehende Upgrade-Phasen behalten ihre eigenen Deadlines und werden nicht durch diesen Timer verkürzt. Der Wert wird beim Annehmen der Verbindung fixiert. |
| `http_overload_timeout_ms` | `u64` | `250` | `✔` | Deadline je Phase in Millisekunden, um bei akzeptierter Überlast auf Kapazität zu warten oder die wiederholbare Antwort zu schreiben; Bereich `1..=60000`. Wait-Timeout und Response-Write erhalten jeweils höchstens ein Phasenbudget. |
| `shutdown_secs` | `u64` | `15` | `✔` | Ein absolutes Budget für das Beenden des Prozesses, das von allen Listener-Acceptoren und Verbindungen sowie WEB-Sitzungs- und Hilfstask-Drains gemeinsam verwendet wird. Der aktive Wert wird beim Start des Shutdowns einmalig erfasst. |
| `decoy_header_secs` | `u64` | `30` | `✔` | Deadline für Verbindung und Response-Head eines HTTP-Decoys. |
| `decoy_resolve_secs` | `u64` | `5` | `✔` | Deadline je DNS-Abfrage für ein eindeutiges Host-/Portpaar während der Vorbereitung; `1..=3600` Sekunden, keine Gesamtdeadline der Vorbereitung oder des HTTP-Austauschs. |
# [web.limits.wildcard]
Diese für die Prozesslaufzeit festen Limits gelten für automatische Wildcard-Hosts.
Siehe [Betriebs- und Sicherheitsvertrag](../WEB/WEB_PROXY.de.md#automatische-wildcard-hosts).
Das Frontend muss Host-Werte begrenzen; unbekannte/verdrängte fremde Capabilities
können Decoys erreichen. Nur Diagnosekopien verwenden vorsorgliche formbasierte Maskierung.
| Schlüssel | Typ | Standard | Bedeutung |
| --- | --- | --- | --- |
| `max_rules` | `usize` | `32` | Gesamtzahl der Suffix-/Catch-all-Selektoren. |
| `max_profiles_per_request` | `usize` | `256` | Obergrenze für vollständige Wildcard-Gruppenscans und kalte HMAC-Berechnungen. |
| `cache_hosts` | `usize` | `256` | Positive FIFO-Einträge; `0` deaktiviert die Speicherung. |
| `cache_bytes` | `usize` | `8388608` | Berechnete Bytes für Einträge/Indizes, auch verdrängte, noch referenzierte Einträge. |
| `cold_hmac_per_minute` | `u32` | `1000000` | Dauerhaftes prozessweites Budget für kalte HMAC-Auswertungen. |
| `cold_hmac_burst` | `u32` | `65536` | Auswertungsburst, mindestens `max_profiles_per_request`. |
Außer `cache_hosts` müssen die Grenzen bei aktiviertem Wildcard-Modus positiv sein.
Kalte Requests reservieren die gesamte Profilzahl atomar; Erschöpfung ergibt sofort
HTTP 503. Nur erfolgreiche Bootstrap-Ausgaben füllen den Cache. Authentifizierte
Carrier umgehen diesen Cache und das kalte Budget. Verdrängung verändert keine
Quoten. Runtime-Snapshots grenzen Cachetreffer bei allen Reload-Arten voneinander ab.
Zusätzliche geprüfte Speicherreservierungen gelten nur im Wildcard-Modus; bei
Standardlimits sind mindestens 1290 MiB statt der bisherigen 1280 MiB nötig.
`memory_envelope_bytes = 1610612736` setzt für das Beispiel einen Rahmen von 1536 MiB.
# [[web.vhosts]]
| Schlüssel | Typ | Erforderlich | Hot-Reload | Beschreibung |
| --- | --- | --- | --- | --- |
| `host` | `String` | ja | `✔` | Eindeutiger, kanonischer ACE-FQDN in Kleinbuchstaben ohne Port, Pfad, Zugangsdaten oder abschließenden Punkt. |
| `host` | `String` | ein Selektor | `✔` | Exakter Hostname oder aktivierter Wildcard-Selektor; schließt `hosts` aus. |
| `hosts` | `String[]` | ein Selektor | `✔` | Nicht leere Selektorliste mit gemeinsamen Profilen, Decoy, `base_path` und `public_addr`. |
| `base_path` | `String` | nein | `✔` | Exaktes, groß-/kleinschreibungssensitives WEB-Präfix ohne führenden oder abschließenden Schrägstrich; standardmäßig leer. Höchstens 128 ASCII-Bytes in durch Schrägstriche getrennten Segmenten `[A-Za-z0-9][A-Za-z0-9_-]*`. |
| `public_addr` | `SocketAddr` | ja | `✔` | Konkrete öffentliche IP auf Port `443`; wird im Ziel-Tupel des inneren Relays verwendet. |
| `decoy` | Tabelle | ja | `✔` | Gewöhnlicher Site-Fallback für nicht authentifizierten oder ungültigen Datenverkehr. |
| `profiles` | Tabellen-Array | bei aktiviertem WEB | `✔` | Explizite Benutzer und Client-Secret-Modi für diesen Hostnamen. |
| `profiles` | Tabellen-Array | bei aktiviertem WEB | `✔` | Explizite Benutzer und Secret-Modi, unabhängig auf jedem Hostnamen der Gruppe verfügbar. |
Der Hostname wird bei der Validierung normalisiert und muss von Telegram Desktop akzeptiert werden. Ein leerer `base_path` behält die Root-Capability v1 und das bisherige hexadezimale Link-Secret. Ein nicht leerer Pfad verwendet die v2-Host/Pfad-Capability und einen Telegram-Desktop-Pfadlink mit percent-encoded `HOST/BASE` sowie dem base64url-Secret-Marker `0x70`. Das Routing verlangt das exakte Präfix mit abschließendem Schrägstrich und leitet es nie um, normalisiert oder entfernt es. Ein Bootstrap ist ein Bearer-Token: Client-Adresse und IP-Familie dürfen sich vor der Sitzungserstellung ändern. Ein ungenutzter Bootstrap bleibt über einen Konfigurations-Reload hinweg nur gültig, solange dieselbe Profilidentität aktiv bleibt.
Genau einer der Selektoren `host` oder `hosts` ist erforderlich, auch bei
deaktiviertem WEB. Leere oder gleichzeitig angegebene Selektoren sind unzulässig.
Jeder Hostname muss ein von Telegram Desktop akzeptierter FQDN ohne IP-Literal,
Port, Pfad, Zugangsdaten oder abschließenden Punkt sein. Die Validierung normalisiert
ihn zu lowercase ACE; kanonische Duplikate innerhalb und zwischen Gruppen werden
abgewiesen. `*.suffix` und `*` erfordern `web.allow_wildcard_hosts = true` und einen
Neustart. Vorrang haben exakte Selektoren, danach das längste Suffix, danach Catch-all.
Suffixe erfassen Nachfahren beliebiger Tiefe, aber nicht den Stammnamen. Eingebettete
Wildcards und Regex sind nicht unterstützt. Die normalisierte Konfiguration behält
den gewählten Selektor; Listen werden beim Serialisieren nicht expandiert. Ein
leerer `base_path` behält die v1-Capability und das bisherige hexadezimale Link-Secret.
Ein nicht leerer Pfad verwendet die v2-Host-/Pfad-Capability und einen Telegram-
Desktop-Pfadlink mit percent-encoded `HOST/BASE` sowie dem base64url-Secret-Marker
`0x70`. Routing verlangt das exakte Präfix mit abschließendem Schrägstrich und leitet
es weder um noch normalisiert oder entfernt es. Ein Bootstrap ist ein Bearer-
Credential: Clientadresse und Adressfamilie dürfen vor Sitzungserstellung wechseln.
Ein unbenutzter Bootstrap bleibt nach Reload nur bei weiterhin aktiver
Profilidentität gültig.
# [web.vhosts.decoy]
Für `http_upstream` akzeptiert `resolve` die Werte `"never"` (Standard) und `"startup"`. Standardmäßig ist ein Loopback-, Link-Local- oder privates IP-Literal erforderlich. `startup` erlaubt zusätzlich DNS-Namen: Die asynchrone Konfigurationsvorbereitung löst jedes eindeutige Host-/Portpaar auf, prüft alle Antworten gegen erlaubte Adressklassen und effektive WEB-Listener und fixiert die erste Adresse in Resolver-Reihenfolge. Fehler, leere Antworten, öffentliche Adressen oder direkte Listener-Schleifen verwerfen die Vorbereitung. Der konfigurierte Hostname bleibt in der Upstream-Authority erhalten. Es gibt weder DNS während der Request-Verarbeitung noch automatischen Wechsel zu weiteren Antworten; Reload erstellt einen frischen Snapshot. `web.timeouts.decoy_resolve_secs` begrenzt jede DNS-Abfrage für ein eindeutiges Host-/Portpaar. Die Abfragen laufen nacheinander, ohne gemeinsame Deadline für die gesamte Vorbereitung. Indirekte Schleifen über weitere Weiterleitungsschichten müssen Betreiber ausschließen.
Genau ein Decoy-Modus ist erforderlich:
| Modus | Erforderliche Schlüssel | Validierung |
| --- | --- | --- |
| `http_upstream` | `upstream` | Ein `http://`-Origin mit Loopback-, Link-Local- oder privater IP-Adresse als Literal; keine Zugangsdaten, kein Pfad, Query oder Fragment. |
| `http_upstream` | `upstream` | Ein `http://`-Origin ohne Zugangsdaten, Pfad, Query oder Fragment. Standardmäßig privates IP-Literal; DNS-Name nur mit `resolve = "startup"`. |
| `static_directory` | `directory`; optional `index = "index.html"` | Absolutes reales Verzeichnis und ein sicherer Index-Dateiname. Symlinks und Pfade außerhalb des Verzeichnisses werden abgelehnt; der unveränderliche Snapshot wird innerhalb von `[web.limits]` geladen. |
# [[web.vhosts.profiles]]
@@ -2746,18 +2808,20 @@ Genau ein Decoy-Modus ist erforderlich:
| --- | --- | --- | --- | --- |
| `user` | `String` | ja | — | Vorhandener Schlüssel mit 1–64 Byte aus `[access.users]`; die Grenze hält Runtime-Status und Filter beschränkt. |
| `secret_mode` | `"plain"` oder `"dd"` | ja | — | Exakte Secret-Darstellung für Telegram Desktop. `ee` wird nicht unterstützt. |
| `max_sessions` | `usize` | nein | `web.limits.max_sessions_global` | Aktive Sitzungen für dieses Profil. |
| `max_streams` | `usize` | nein | `web.limits.max_streams_global` | Aktive logische Streams für dieses Profil. |
| `max_sessions` | `usize` | nein | `web.limits.max_sessions_global` | Lebende Profilsitzungen je konkretem Hostnamen. |
| `max_streams` | `usize` | nein | `web.limits.max_streams_global` | Lebende logische Profilstreams je konkretem Hostnamen. |
| `max_streams_per_session` | `usize` | nein | `web.limits.max_streams_per_session` | Aktive logische Streams in einer Profilsitzung. |
Profilgrenzen müssen ungleich null sein und dürfen die zugehörigen globalen Grenzen nicht überschreiten. Doppelte `(user, secret_mode)`-Profile in einem vhost werden abgelehnt.
Profillimits müssen positiv sein und dürfen die entsprechenden globalen Limits nicht überschreiten. Doppelte `(user, secret_mode)`-Profile innerhalb einer Gruppe sind unzulässig. Jeder konkrete Hostname behält seine eigene Quotenidentität; Prozess-, IP- und Zugangsbenutzerlimits gelten weiterhin. Profilobergrenzen reservieren keine Sitzungen oder Streams vorab. Eine Gruppe teilt unveränderliche Richtlinien und einen Decoy-Snapshot; hostgebundene Capabilities und ihre begrenzten Indizes zur Vermeidung von Credential-Lecks bleiben getrennt.
## WEB-Lebenszyklus und API-Verwaltung
Vor dem Hinzufügen von Mustern über die API muss `web.allow_wildcard_hosts` im aktiven Prozess bereits aktiviert sein. Das Speichern des gewünschten Parameters aktiviert Muster nicht sofort; verfrühte Änderungen werden bis zum Neustart atomar abgewiesen.
- Config-Watcher und Generations-Reload wenden `web.enabled`, Carrier- und Negotiation-Richtlinie, `web.debug`, `web.timeouts`, vhosts, Profile und Decoy-Snapshots ohne Prozessneustart an. Ein einzelner unveränderlicher expandierter Source-Snapshot wird validiert und aktiviert; der Watcher einer Kandidatengeneration startet erst nach deren Aktivierung. Bestehende Sitzungen und laufende Negotiation-Ketten behalten Carrier-Kandidaten, Grenzen, Timeouts und absolute Deadlines ihres Ausgabezeitpunkts; neue Bridge-Sitzungen verwenden genau eine fixierte aktive Generation.
- Eine Änderung von `base_path` ersetzt atomar sowohl die Route für neue Requests als auch die abgeleitete Capability. Geben Sie zuerst neue Links aus und beenden Sie betroffene aktive Sitzungen: etablierte WebSockets und bereits geroutete Austauschvorgänge laufen weiter; spätere Requests an die alte Basis mit einem prozessauthentischen Bootstrap- oder Session-Token erhalten ein lokales, nicht cachebares `404`, während die nun inaktive alte Capability der gewöhnlichen Decoy-Behandlung folgt.
- Bestand und Vertrauensrichtlinie der WEB-Listener unter `server.listeners` sowie alle Werte in `web.limits` sind prozesseigen und erfordern einen Neustart.
- `GET /v1/config` liefert den vollständigen verfassten `[web]`-Baum außer dem abgeleiteten Snapshot `web.runtime`. `PATCH /v1/config` akzeptiert ein dünn besetztes `web`-Objekt, führt Tabellen tief zusammen, ersetzt Arrays vollständig, validiert den gesamten Kandidaten und meldet `web.limits` bis zum Neustart in `deferred_process_fields`.
- Bestand und Vertrauensrichtlinie der WEB-Listener unter `server.listeners`, der Parameter `web.allow_wildcard_hosts` und alle Werte in `web.limits` sind prozesseigen und erfordern einen Neustart.
- `GET /v1/config` liefert den vollständigen verfassten `[web]`-Baum außer dem abgeleiteten Snapshot `web.runtime`. `PATCH /v1/config` akzeptiert ein dünn besetztes `web`-Objekt, führt Tabellen tief zusammen, ersetzt Arrays vollständig, validiert den gesamten Kandidaten und meldet `web.allow_wildcard_hosts`, `web.limits` bis zum Neustart in `deferred_process_fields`.
- `GET /v1/runtime/web/status`, `/sessions`, `/sessions/{session_ref}` und `/operations/{operation_id}` stellen begrenzten, nicht geheimen Runtime-Zustand bereit. POST-Steuerungen schließen ausgewählte Sitzungen, löschen Debugdaten oder setzen Carrier-Learning zurück und verlangen die aktuelle zufällige `runtime_instance`.
- `web.enabled = false` stoppt nach der Aktivierung neue Bootstrap-/Session-Ausgaben, schließt aber keine aktiven Sitzungen. Warten Sie für Close-all auf `manager.issuance_enabled = false`, senden Sie den asynchronen Selektor `all` und fragen Sie dessen Operation ab.
- Vorhandene Access-Benutzer können über `/v1/users` erstellt, geändert, rotiert, aktiviert, deaktiviert und gelöscht werden. Das Erstellen eines Benutzers fügt kein WEB-Profil hinzu. Das Deaktivieren aktualisiert die Admission sofort und beendet die aktiven Sitzungen dieses Benutzers.
@@ -3439,6 +3503,7 @@ Wenn Backend oder Netzwerk stark bandbreitenbeschränkt sind, reduzieren Sie zue
| [`user_max_tcp_conns_global_each`](#user_max_tcp_conns_global_each) | `usize` | `0` | `✔` |
| [`user_expirations`](#user_expirations) | `Map<String, DateTime<Utc>>` | `{}` | `✔` |
| [`user_data_quota`](#user_data_quota) | `Map<String, u64>` | `{}` | `✔` |
| [`user_quota_periods`](#user_quota_periods) | `Map<String, "daily" / "weekly" / "monthly">` | `{}` | `✔` |
| [`user_max_unique_ips`](#user_max_unique_ips) | `Map<String, usize>` | `{}` | `✔` |
| [`user_max_unique_ips_global_each`](#user_max_unique_ips_global_each) | `usize` | `0` | `✔` |
| [`user_max_unique_ips_mode`](#user_max_unique_ips_mode) | `"active_window"`, `"time_window"` oder `"combined"` | `"active_window"` | `✔` |
@@ -3524,6 +3589,26 @@ Wenn Backend oder Netzwerk stark bandbreitenbeschränkt sind, reduzieren Sie zue
# Alice receives a 1 GiB quota.
alice = 1073741824
```
## user_quota_periods
- **Einschränkungen / Validierung**: Jeder Schlüssel muss einen vorhandenen `access.users`-Eintrag mit positivem `access.user_data_quota` benennen. Zulässige Werte sind genau `"daily"`, `"weekly"` oder `"monthly"`. Bei nicht leerer Tabelle darf die gesamte konfigurierte Benutzerzahl 65.536 nicht überschreiten.
- **Standard**: Leer. Benutzer ohne Periode behalten die bisherige manuell zurückgesetzte Quote ohne automatische Erneuerung.
- **Kalender**: Alle Grenzen gelten in UTC: täglich um Mitternacht, wöchentlich montags um Mitternacht, monatlich am ersten Tag um Mitternacht. Ein Monat ist ein Kalendermonat, keine feste Tageszahl.
- **Aktivierung**: Aktivieren oder Ändern erhält den Verbrauch und beginnt automatische Rücksetzungen an der nächsten Grenze. Entfernen erhält Verbrauch und dauerhafte Kalendermarke. Hot Reload übernimmt die Richtlinie ohne Secret-Wechsel oder Prozessneustart.
- **Lebenszyklus**: Secret-Rotation erhält Verbrauch und Kalender. Deaktivierte und abgelaufene Konten durchlaufen weiter den Kalender, bleiben aber gesperrt/abgelaufen. Löschen und Neuerstellen beginnt frisch; CREATE muss vor Speicherung des neuen Benutzers den alten dauerhaften Zustand erfolgreich bereinigen, auch ohne periodische Quote. Manuelles `reset-quota` verschiebt keine UTC-Grenzen. Eine rückwärts laufende Uhr wiederholt keine verarbeitete Grenze, auch nach Entfernen und Wiederherstellen einer Periode.
- **Persistenz**: Der Worker speichert den gesamten Rücksetzungsbatch vor Veröffentlichung neuer Zähler. Bei Schreibfehlern wird der Verbrauch nicht zurückgesetzt und Kalendermarken werden nicht vorgerückt; laufender Verkehr wird weiter gezählt. Wiederholungen verwenden monotone Verzögerungen von 1, 2, 4, 8, 16, 32 und danach 60 Sekunden. Nach Verzögerung oder Neustart wird einmal auf die letzte fällige Periode zurückgesetzt, ohne jedes verpasste Intervall nachzuspielen. Eine reine Metadatenaktivierung überschreibt keinen während des Schreibens entstandenen Verbrauch.
- **Start**: Bei konfigurierten Perioden verhindert ein vorhandener unlesbarer, ungültiger, zu großer oder nicht unterstützter Kalender-Checkpoint den Start. Eine fehlende Datei beginnt frisch; ein gültiger bisheriger Zustand behält den Verbrauch und erhält Kalendermetadaten. Die Grenzen bleiben 16 MiB und 65.536 Einträge. Auch bei gültiger oder fehlender Datei muss die erste dauerhafte Veröffentlichung gelingen. `general.quota_state_path` muss auf dauerhaftem beschreibbarem Speicher liegen.
- **Abrechnung**: Rücksetzungen ersetzen die Abrechnungsgeneration; bereits gehaltene Relay-Reservierungen behalten ihre ursprüngliche Generation. Eine alte Rückerstattung kann den Verbrauch der neuen Periode nicht verringern. Kumulative Verkehrstelemetrie bleibt unverändert. Es wird kein Traffic-Write-Ahead-Log eingeführt: Bei einem Absturz kann Verbrauch seit dem letzten Checkpoint weiterhin verloren gehen.
- **API**: Benutzer-Create/PATCH akzeptiert `quota_period`; der Status unterscheidet gewünschte Konfiguration und wirksamen dauerhaften Kalender. Siehe [API-Vertrag](../Architecture/API/API.md#recurring-quota-periods).
- **Beispiel**:
```toml
[access.user_data_quota]
alice = 1073741824
[access.user_quota_periods]
alice = "monthly"
```
## user_max_unique_ips
- **Einschränkungen / Validierung**: `Map<String, usize>`.
- **Beschreibung**: Eindeutige Source-IP-Limits pro User.
+24 -6
View File
@@ -333,7 +333,7 @@ This document lists all configuration keys accepted by `config.toml`.
data_path = "/var/lib/telemt"
```
## quota_state_path
- **Recurring quotas**: This file also stores per-user UTC calendar watermarks. When [`user_quota_periods`](#user_quota_periods) is nonempty, invalid existing state is a startup error; a missing file starts fresh.
- **Recurring quotas**: This file also stores per-user UTC calendar watermarks. When [`user_quota_periods`](#user_quota_periods) is nonempty, invalid existing state is a startup error; a missing file starts fresh. Initial durable publication must also succeed before startup can complete.
- **Constraints / validation**: `Path`. Relative paths are resolved from the process working directory.
- **Description**: JSON state file used to persist runtime per-user quota consumption.
- **Example**:
@@ -698,6 +698,7 @@ This document lists all configuration keys accepted by `config.toml`.
direct_relay_copy_buf_c2s_bytes = 65536
```
## direct_relay_copy_buf_s2c_bytes
- **Flush behavior**: In both directions, direct adaptive copy drives buffered output flushes when the source becomes pending, without waiting for EOF or another source packet. TLS framing, configured buffer limits and quota enforcement remain unchanged.
- **Constraints / validation**: Must be within `8192..=2097152` (bytes).
- **Description**: Copy buffer size for DC->client direction in direct relay.
- **Example**:
@@ -1121,6 +1122,9 @@ This document lists all configuration keys accepted by `config.toml`.
disable_colors = false
```
## me_socks_kdf_policy
A valid public SOCKS `BND.ADDR/BND.PORT` with a nonzero port supplies the complete KDF source tuple, ahead of configured/discovered NAT information. Its family must match the logical ME destination, independently of the TCP family used to reach SOCKS. IPv4-mapped IPv6 destinations count as IPv4 and require a native IPv4 BND tuple; IPv4-compatible destinations remain IPv6. `strict` rejects unavailable/invalid BND; `compat` retains the existing fallback. This does not change KDF fields, ports, SOCKS CONNECT destination encoding, public-address checks, or STUN behavior.
- **Constraints / validation**: `"strict"` or `"compat"`.
- **Description**: SOCKS-bound KDF fallback policy for Middle-End handshake.
- **Example**:
@@ -2064,6 +2068,9 @@ This document lists all configuration keys accepted by `config.toml`.
proxy_protocol_trusted_cidrs = ["127.0.0.1/32", "10.0.0.0/8"]
```
## metrics_port
With only `metrics_port`, Telemt attempts IPv4 and IPv6 loopback binds. `metrics_listen` overrides that binding. Docker port publishing alone does not expose a loopback-only listener: configure an explicit container-reachable bind and whitelist the immediate scraper/Docker peer.
- **Constraints / validation**: `u16` (optional).
- **Description**: Prometheus-compatible metrics endpoint port. When set, enables the metrics listener (bind behavior can be overridden by `metrics_listen`).
- **Example**:
@@ -2082,6 +2089,9 @@ This document lists all configuration keys accepted by `config.toml`.
metrics_listen = "127.0.0.1:9090"
```
## metrics_whitelist
Authorization checks the immediate peer IP in its original form and, for IPv4-mapped IPv6, its canonical IPv4 form. Explicit IPv6 CIDRs remain valid. Forwarded headers do not grant access.
- **Constraints / validation**: `IpNetwork[]`.
- **Description**: CIDR whitelist for metrics endpoint access.
- **Example**:
@@ -2458,6 +2468,9 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
synlimit_burst = 24
```
## synlimit_ios_seconds (server.listeners)
This is a packet-shape heuristic, not authenticated Apple-device detection. IPv4 SYN packets of length `60` use the generic bucket; no universal iOS/background-resume fix is implied.
- **Constraints / validation**: `u32`, must be `> 0`. Default is `1`.
- **Description**: Token-bucket interval for SYN packets matching the iOS-like classifier. IPv4 matches packet length `64` and TTL `< 65`; IPv6 matches packet length `84` and hop limit `< 65`.
- **Example**:
@@ -2676,7 +2689,7 @@ These process-wide ceilings make every WEB registry, queue, request body, capabi
| `control_bytes_global` | `usize` | `16777216` | Process-wide byte reserve available only to control frames. |
| `max_bootstraps_global` | `usize` | `512` | Live bootstrap credentials process-wide. |
| `max_bootstraps_per_ip` | `usize` | `64` | Live bootstrap credentials per client IP. |
| `max_vhosts` | `usize` | `8` | Concrete WEB hostnames across all groups; each `hosts` entry counts separately. |
| `max_vhosts` | `usize` | `8` | Concrete exact WEB hostnames across all groups; each exact `hosts` entry counts separately. Wildcard selectors count toward `max_rules`. |
| `max_profiles` | `usize` | `32` | Exact host/profile bindings plus one shared profile set per wildcard source group. |
| `max_static_files` | `usize` | `4096` | Static snapshot entries across all groups; a shared group snapshot counts once. |
| `max_static_file_bytes` | `usize` | `8388608` | Maximum bytes in one static file. |
@@ -2721,6 +2734,7 @@ Unless a row states otherwise, timeouts are measured in seconds and must be with
| `http_overload_timeout_ms` | `u64` | `250` | `✔` | Per-phase deadline in milliseconds for an accepted saturated socket to wait for capacity or write its retryable response; validated within `1..=60000`. A timed-out wait and its response write each receive at most one phase budget. |
| `shutdown_secs` | `u64` | `15` | `✔` | One absolute process-shutdown budget shared by all listener acceptors and connections plus WEB session and auxiliary-task drains. The active value is captured once when shutdown starts. |
| `decoy_header_secs` | `u64` | `30` | `✔` | Connect and response-head deadline for an HTTP decoy. |
| `decoy_resolve_secs` | `u64` | `5` | `✔` | Deadline per distinct hostname/port DNS lookup during preparation; `1..=3600` seconds, not an aggregate preparation or HTTP exchange deadline. |
# [web.limits.wildcard]
@@ -2762,11 +2776,13 @@ Exactly one of `host` or `hosts` is required, including when WEB is disabled. Em
# [web.vhosts.decoy]
For `http_upstream`, `resolve` accepts `"never"` (default) or `"startup"`. The default requires a loopback, link-local, or private IP literal. `startup` additionally permits DNS names: asynchronous configuration preparation resolves each distinct hostname/port, validates every answer against the allowed address classes and effective WEB listeners, and pins the first resolver-order address. Failure, no answers, a public address, or a direct listener loop rejects preparation. The configured hostname is preserved in the upstream authority. There is no request-time DNS or automatic answer failover; reload prepares a fresh snapshot. `web.timeouts.decoy_resolve_secs` bounds each distinct hostname/port DNS lookup during preparation; lookups are sequential, with no aggregate preparation deadline. Operators must exclude indirect loops through other forwarding layers.
Exactly one decoy mode is required:
| Mode | Required keys | Validation |
| --- | --- | --- |
| `http_upstream` | `upstream` | An `http://` origin using a loopback, link-local, or private IP literal; no credentials, path, query, or fragment. |
| `http_upstream` | `upstream` | An `http://` origin without credentials, path, query, or fragment. Private IP literal by default; DNS name only with `resolve = "startup"`. |
| `static_directory` | `directory`; optional `index = "index.html"` | Absolute real directory and one safe index file name. Symlinks and escaping paths are rejected; the immutable snapshot is loaded under `[web.limits]`. |
# [[web.vhosts.profiles]]
@@ -2783,6 +2799,8 @@ Profile limits must be non-zero and no greater than their corresponding global l
## WEB lifecycle and API management
The process must already have `web.allow_wildcard_hosts` enabled before a patch can introduce patterns. Saving the desired flag does not enable patterns immediately; premature updates fail atomically until restart.
- The config watcher and generation reload apply `web.enabled`, carrier and negotiation policy, `web.debug`, `web.timeouts`, vhosts, profiles, and decoy snapshots without a process restart. One immutable expanded source snapshot is validated and activated; a candidate generation's watcher starts only after that generation becomes active. Existing sessions and in-flight negotiation chains keep their issuance-time carrier candidates, limits, timeouts, and absolute deadlines; newly issued bridge sessions use one pinned active generation.
- Changing `base_path` atomically replaces both the new-request route and derived capability. Reissue links and drain affected live sessions first: established WebSockets and already routed exchanges continue; later old-base requests carrying a process-authentic bootstrap or session token receive a local no-store `404`, while the now-inactive old capability follows ordinary decoy handling.
- WEB listener inventory and trust policy under `server.listeners`, `web.allow_wildcard_hosts`, and every `web.limits` value, are process-owned and restart-required.
@@ -3559,9 +3577,9 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
- **Default**: Empty. Users without a period retain the existing manual/lifetime quota behavior.
- **Calendar**: All boundaries are UTC: daily at midnight, weekly on Monday at midnight, monthly on the first day at midnight. A month is a calendar month, not a fixed number of days.
- **Activation**: Enabling or changing a period preserves consumption and starts automatic resets at the next boundary. Removing a period preserves consumption and the durable calendar watermark. Hot reload applies the policy without changing the secret or restarting the process.
- **Lifecycle**: Secret rotation preserves consumption and calendar state. Disabled and expired accounts continue their calendar but remain disabled/expired. Deleting and recreating an account starts fresh. Manual `reset-quota` does not shift UTC cadence. A clock rollback cannot repeat an already processed boundary, including after removing and restoring a period.
- **Persistence**: The worker checkpoints the complete reset batch before publishing new counters. On write failure, live consumption and cursors stay unchanged; retries use monotonic delays of 1, 2, 4, 8, 16, 32, then 60 seconds. Delayed or restarted workers reset once to the latest due period, rather than replaying every missed interval. Successful metadata-only activation does not overwrite traffic accumulated during the write.
- **Startup**: With periods configured, an existing unreadable, invalid, oversized, or unsupported calendar checkpoint fails startup. A missing file initializes fresh state; a valid legacy file preserves consumption while acquiring calendar metadata. The checkpoint remains limited to 16 MiB and 65,536 rows. Keep `general.quota_state_path` on persistent writable storage.
- **Lifecycle**: Secret rotation preserves consumption and calendar state. Disabled and expired accounts continue their calendar but remain disabled/expired. Deleting and recreating an account starts fresh; CREATE requires successful durable cleanup before saving the new user, including users without a recurring period. Manual `reset-quota` does not shift UTC cadence. A clock rollback cannot repeat an already processed boundary, including after removing and restoring a period.
- **Persistence**: The worker checkpoints the complete reset batch before publishing new counters. On write failure, consumption is not reset and calendar cursors do not advance; ongoing traffic continues to accrue. Retries use monotonic delays of 1, 2, 4, 8, 16, 32, then 60 seconds. Delayed or restarted workers reset once to the latest due period, rather than replaying every missed interval. Successful metadata-only activation does not overwrite traffic accumulated during the write.
- **Startup**: With periods configured, an existing unreadable, invalid, oversized, or unsupported calendar checkpoint fails startup. A missing file initializes fresh state; a valid legacy file preserves consumption while acquiring calendar metadata. The checkpoint remains limited to 16 MiB and 65,536 rows. Startup also requires successful durable publication of the initial state, even if the checkpoint was valid or missing. Keep `general.quota_state_path` on persistent writable storage.
- **Accounting**: Resets replace the existing accounting generation; reservations already held by relays retain their original generation. Refunding an old reservation cannot reduce new-period consumption. Lifetime traffic telemetry is unchanged. This feature does not add a traffic write-ahead log: consumption since the last checkpoint retains the existing crash-loss window.
- **API**: User create/PATCH accepts `quota_period`; quota status distinguishes desired configuration from the effective durable calendar. See the [API contract](../Architecture/API/API.md#recurring-quota-periods).
- **Example**:
+98 -14
View File
@@ -27,6 +27,7 @@
- [web](#web)
- [web.debug](#webdebug)
- [web.limits](#weblimits)
- [web.limits.wildcard](#weblimitswildcard)
- [web.timeouts](#webtimeouts)
- [web.vhosts](#webvhosts)
- [web.vhosts.decoy](#webvhostsdecoy)
@@ -332,6 +333,7 @@
data_path = "/var/lib/telemt"
```
## quota_state_path
- **Периодические квоты**: Файл также хранит календарные отметки UTC по пользователям. Если [`user_quota_periods`](#user_quota_periods) не пуст, некорректное существующее состояние останавливает запуск; отсутствие файла создаёт новое состояние. Для завершения запуска также обязательна успешная первоначальная запись состояния на диск.
- **Ограничения / валидация**: `Path`. Относительные пути разрешаются от рабочего каталога процесса.
- **Описание**: JSON-файл состояния для сохранения runtime-расхода квот по пользователям.
- **Пример**:
@@ -696,6 +698,7 @@
direct_relay_copy_buf_c2s_bytes = 65536
```
## direct_relay_copy_buf_s2c_bytes
- **Сброс буфера**: В обоих направлениях direct adaptive copy продвигает сброс накопленного вывода, когда источник переходит в ожидание, не дожидаясь EOF или следующего входящего пакета. Формирование TLS-записей, настроенные пределы буферов и контроль квот сохраняются.
- **Ограничения / валидация**: Должно быть в пределах `8192..=2097152` (байт).
- **Описание**: CoРазмер буфера копирования для направления DC > клиент в режиме прямой пересылки (direct relay).
- **Пример**:
@@ -1119,6 +1122,9 @@
disable_colors = false
```
## me_socks_kdf_policy
Корректные публичные SOCKS `BND.ADDR/BND.PORT` с ненулевым портом задают полный исходный кортеж KDF и имеют приоритет над настроенными или обнаруженными NAT-данными. Семейство должно совпадать с логическим ME-назначением, независимо от семейства TCP-соединения к SOCKS. IPv4-mapped IPv6 назначения считаются IPv4 и требуют обычного IPv4 BND; IPv4-compatible назначения остаются IPv6. `strict` отклоняет отсутствующий или некорректный BND, `compat` сохраняет прежний fallback. Поля и порты KDF, кодирование назначения SOCKS CONNECT, проверка публичного адреса и STUN не меняются.
- **Ограничения / валидация**: `"strict"` или `"compat"`.
- **Описание**: Политика fallback-поведения KDF, привязанная к SOCKS, для Middle-End-handshake.
- **Пример**:
@@ -2063,6 +2069,9 @@
proxy_protocol_trusted_cidrs = ["127.0.0.1/32", "10.0.0.0/8"]
```
## metrics_port
Если задан только `metrics_port`, Telemt пытается слушать IPv4 и IPv6 loopback. `metrics_listen` заменяет этот вариант привязки. Одной публикации Docker-порта недостаточно для listener на loopback: задайте адрес, доступный через сеть контейнера, и добавьте непосредственный адрес сборщика метрик или Docker peer в whitelist.
- **Ограничения / валидация**: `u16` (необязательный параметр).
- **Описание**: Порт для Prometheus-совместимого endpoint’а метрик. При задании включает прослушивание метрик (поведение прослушивания может быть переопределено через `metrics_listen`).
- **Пример**:
@@ -2081,6 +2090,9 @@
metrics_listen = "127.0.0.1:9090"
```
## metrics_whitelist
Авторизация проверяет IP непосредственного peer в исходном виде и, для IPv4-mapped IPv6, в каноническом IPv4-виде. Явные IPv6 CIDR продолжают действовать. Forwarded-заголовки не предоставляют доступ.
- **Ограничения / валидация**: `IpNetwork[]`.
- **Описание**: Белый список CIDR для доступа к endpoint’у метрик.
- **Пример**:
@@ -2462,6 +2474,9 @@
synlimit_burst = 24
```
## synlimit_ios_seconds (server.listeners)
Это эвристика по форме пакета, а не достоверное распознавание устройств Apple. IPv4 SYN длиной `60` попадает в общий bucket; универсального исправления iOS или восстановления после фонового режима здесь нет.
- **Ограничения / валидация**: `u32`, должно быть `> 0`. Значение по умолчанию: `1`.
- **Описание**: Token-bucket interval для SYN packets, совпавших с iOS-like classifier. IPv4 match: packet length `64` и TTL `< 65`; IPv6 match: packet length `84` и hop limit `< 65`.
- **Пример**:
@@ -2595,6 +2610,7 @@ WEB-режим переносит MTProxy-трафик Telegram Desktop внут
| Ключ | Тип | По умолчанию | Hot-Reload |
| --- | --- | --- | --- |
| `enabled` | `bool` | `false` | `✔` |
| `allow_wildcard_hosts` | `bool` | `false` | `✘` |
| `carrier` | `"https"`, `"https-lanes"`, `"websocket"` или `"websocket-lanes"` | `"https"` | `✔` |
| `carriers` | `false` или непустой массив уникальных carrier | `false` | `✔` |
| `carrier_learning` | `bool` | `true` | `✔` |
@@ -2679,11 +2695,11 @@ WEB-режим переносит MTProxy-трафик Telegram Desktop внут
| `control_bytes_global` | `usize` | `16777216` | Process-wide резерв только для control frames. |
| `max_bootstraps_global` | `usize` | `512` | Активные bootstrap credentials на весь процесс. |
| `max_bootstraps_per_ip` | `usize` | `64` | Активные bootstrap credentials на один client IP. |
| `max_vhosts` | `usize` | `8` | Настроенные WEB virtual hosts. |
| `max_profiles` | `usize` | `32` | WEB-профили всех vhosts. |
| `max_static_files` | `usize` | `4096` | Элементы static snapshot всех vhosts. |
| `max_vhosts` | `usize` | `8` | Точные имена WEB-доменов во всех группах; каждое точное имя в `hosts` учитывается отдельно. Шаблоны учитывает `max_rules`. |
| `max_profiles` | `usize` | `32` | Точные пары домен/профиль и один общий набор профилей на каждую группу wildcard. |
| `max_static_files` | `usize` | `4096` | Записи статических снимков всех групп; общий снимок группы учитывается один раз. |
| `max_static_file_bytes` | `usize` | `8388608` | Максимальный размер одного статического файла. |
| `max_static_bytes` | `usize` | `67108864` | Размер static snapshots всех vhosts. |
| `max_static_bytes` | `usize` | `67108864` | Байты статических снимков всех групп; общий снимок группы учитывается один раз. |
| `debug_records_capacity` | `usize` | `65536` | Максимальное число сохранённых WEB debug records. |
| `debug_bytes_global` | `usize` | `67108864` | Глобальная байтовая граница сохранённых и находящихся в обработке WEB debug данных; минимум 4096. |
| `memory_envelope_bytes` | `usize` | `1342177280` | Заявленный envelope для HTTP heads, bodies, общих queues/WebSocket I/O, состояния lanes, carrier learning, static snapshots и bounded debug/status buffers; максимум 4 GiB. |
@@ -2719,31 +2735,76 @@ WEB-режим переносит MTProxy-трафик Telegram Desktop внут
| `carrier_negotiation_deadlines_secs` | `[u64; 4]` | `[3, 5, 8, 12]` | `✔` | Строго возрастающие cumulative offsets: bridge применяет их перед первым запросом `/session`, сервер — при приёме первой automatic attempt. Checkpoints для одного—четырёх кандидатов: `[d3]`, `[d0, d3]`, `[d0, d1, d3]` и `[d0, d1, d2, d3]`; последний кандидат всегда использует `d3`. |
| `carrier_learning_secs` | `u64` | `600` | `✔` | Фиксированный срок двух process-local окон evidence; диапазон `2..=86400`. |
| `bootstrap_lifetime_secs` | `u64` | `120` | `✔` | Срок неиспользованного bootstrap и replay-marker закрытого token. |
| `reconnect_grace_secs` | `u64` | `120` | `✔` | Максимальная неактивность carrier до закрытия сессии. |
| `reconnect_grace_secs` | `u64` | `120` | `✔` | Максимальная подтверждённая неактивность peer до закрытия сессии; пустые опросы и активность только со стороны backend не продлевают этот срок. |
| `http_idle_secs` | `u64` | `75` | `✔` | Лимит простоя между HTTP-обменами и при отсутствии прогресса уже выданного response body. Явно ограниченные фазы request body, long poll, decoy и ожидания Upgrade сохраняют собственные deadlines и не обрываются этим таймером. Значение фиксируется при приёме connection. |
| `http_overload_timeout_ms` | `u64` | `250` | `✔` | Deadline каждой фазы в миллисекундах для ожидания capacity или записи повторяемого ответа после принятия перегруженного socket; диапазон `1..=60000`. Timeout ожидания и запись ответа получают не более одного бюджета фазы каждый. |
| `shutdown_secs` | `u64` | `15` | `✔` | Один абсолютный бюджет завершения процесса, общий для всех listener acceptors и connections, а также для WEB sessions и auxiliary tasks. Активное значение фиксируется один раз при начале shutdown. |
| `decoy_header_secs` | `u64` | `30` | `✔` | Deadline подключения и получения response head от HTTP decoy. |
| `decoy_resolve_secs` | `u64` | `5` | `✔` | Таймаут DNS-запроса для каждой уникальной пары имя/порт: `1..=3600` секунд; не общий срок подготовки и не таймаут HTTP-обмена. |
# [web.limits.wildcard]
Эти лимиты фиксируются на срок жизни процесса и применяются к wildcard-доменам.
См. [правила развёртывания и безопасности](../WEB/WEB_PROXY.ru.md#wildcard-хосты).
Frontend должен ограничивать Host: неизвестные или вытесненные чужие capability
могут попасть в decoy. Консервативное маскирование по форме применяется только
к диагностическим копиям.
| Ключ | Тип | По умолчанию | Значение |
| --- | --- | --- | --- |
| `max_rules` | `usize` | `32` | Общее число суффиксных селекторов и catch-all. |
| `max_profiles_per_request` | `usize` | `256` | Предел полного перебора группы wildcard и стоимости холодного HMAC. |
| `cache_hosts` | `usize` | `256` | Записи положительного FIFO-кэша; `0` отключает хранение. |
| `cache_bytes` | `usize` | `8388608` | Байты записей и индексов, включая вытесненные записи, ещё используемые запросами. |
| `cold_hmac_per_minute` | `u32` | `1000000` | Устойчивый бюджет холодных HMAC-вычислений на процесс. |
| `cold_hmac_burst` | `u32` | `65536` | Допустимый всплеск вычислений, не меньше `max_profiles_per_request`. |
При включённом wildcard все пределы, кроме `cache_hosts`, положительны. Холодный
запрос атомарно резервирует бюджет для всех профилей группы; при нехватке сразу
возвращается HTTP 503. Кэш заполняется только успешной выдачей bootstrap.
Аутентифицированные carriers обходят этот кэш и холодный бюджет. Вытеснение не
меняет квоты. Снимки runtime отделяют записи кэша при любом виде reload.
Дополнительные проверяемые резервы памяти действуют только в wildcard-режиме;
со стандартными лимитами нужно не менее 1290 MiB вместо прежних 1280 MiB.
`memory_envelope_bytes = 1610612736` задаёт для примера бюджет 1536 MiB.
# [[web.vhosts]]
| Ключ | Тип | Обязательный | Hot-Reload | Описание |
| --- | --- | --- | --- | --- |
| `host` | `String` | да | `✔` | Уникальный канонический lowercase ACE FQDN без порта, пути, credentials и завершающей точки. |
| `host` | `String` | один селектор | `✔` | Точное имя или разрешённый wildcard; несовместим с `hosts`. |
| `hosts` | `String[]` | один селектор | `✔` | Непустой список селекторов с общими profiles, decoy, `base_path` и `public_addr`. |
| `base_path` | `String` | нет | `✔` | Точный регистрозависимый WEB-prefix без начального и завершающего слеша; по умолчанию пуст. Не более 128 ASCII-байт в разделённых слешами сегментах `[A-Za-z0-9][A-Za-z0-9_-]*`. |
| `public_addr` | `SocketAddr` | да | `✔` | Конкретный публичный IP на порту `443`, используемый во внутреннем destination tuple relay. |
| `decoy` | таблица | да | `✔` | Обычный сайт для неаутентифицированного или некорректного трафика. |
| `profiles` | массив таблиц | при включённом WEB | `✔` | Явные пользователи и client secret modes для этого hostname. |
| `profiles` | массив таблиц | при включённом WEB | `✔` | Явные пользователи и режимы secret, доступные независимо на каждом домене группы. |
Hostname нормализуется при валидации и должен приниматься Telegram Desktop. Пустой `base_path` сохраняет root-capability v1 и прежний шестнадцатеричный secret ссылки. Непустой путь использует capability v2 по host/path и Telegram Desktop path-ссылку с percent-encoded `HOST/BASE` и base64url-маркером secret `0x70`. Маршрутизация требует точного prefix с завершающим слешем и никогда не перенаправляет, не нормализует и не удаляет его. Bootstrap является bearer credential: адрес клиента и его IP-семейство могут измениться до создания session. Неиспользованный bootstrap остаётся действительным после reload конфигурации, только пока активен профиль с той же identity.
Нужен ровно один селектор `host` или `hosts`, даже при выключенном WEB. Пустые
селекторы и одновременное указание обоих запрещены. Каждое имя должно быть
поддерживаемым Telegram Desktop FQDN, не IP-адресом, без порта, пути, credentials
и завершающей точки. При проверке оно нормализуется в lowercase ACE; дубли внутри
групп и между ними запрещены. Для `*.suffix` и `*` требуется
`web.allow_wildcard_hosts = true`, вступающий в силу после перезапуска. Приоритет:
точное имя, самый длинный суффикс, catch-all. Суффикс охватывает потомков любой
глубины, но не сам домен. Встроенные wildcard и regex не поддерживаются.
Нормализованная конфигурация сохраняет выбранный селектор; при сериализации
списки не разворачиваются. Пустой `base_path` сохраняет capability v1 и прежний
hexadecimal secret ссылки. Непустой путь использует capability v2 для host/path
и ссылку Telegram Desktop с percent-encoded `HOST/BASE` и маркером base64url
secret `0x70`. Маршрутизация требует точного префикса с завершающим слешем и не
перенаправляет, не нормализует и не удаляет его. Bootstrap — bearer credential:
до создания сессии IP клиента и семейство адреса могут измениться. Неиспользованный
bootstrap переживает reload только при сохранении активной идентичности профиля.
# [web.vhosts.decoy]
Для `http_upstream` параметр `resolve` принимает `"never"` (по умолчанию) или `"startup"`. По умолчанию нужен IP-адрес из loopback, link-local или private диапазона. `startup` также разрешает DNS-имена: асинхронная подготовка конфигурации разрешает каждую уникальную пару имя/порт, проверяет все ответы по допустимым классам адресов и действующим WEB listeners и закрепляет первый адрес в порядке ответа resolver. Ошибка, пустой ответ, публичный адрес или прямая петля к listener отклоняют подготовку. В upstream authority сохраняется настроенное имя. DNS при обработке запросов и автоматического перехода к другим ответам нет; reload готовит новый снимок. `web.timeouts.decoy_resolve_secs` ограничивает отдельный DNS-запрос для каждой уникальной пары имя/порт. Запросы выполняются последовательно, общего таймаута подготовки нет. Косвенные петли через другие прокси оператор должен исключить самостоятельно.
Обязателен ровно один decoy mode:
| Mode | Обязательные ключи | Валидация |
| --- | --- | --- |
| `http_upstream` | `upstream` | `http://` origin с loopback, link-local или private IP literal; без credentials, path, query и fragment. |
| `http_upstream` | `upstream` | `http://` origin без credentials, пути, query и fragment. По умолчанию private IP literal; DNS-имя разрешено только с `resolve = "startup"`. |
| `static_directory` | `directory`; необязательный `index = "index.html"` | Абсолютный реальный каталог и одно безопасное имя index-файла. Symlinks и выход за пределы каталога запрещены; immutable snapshot загружается в пределах `[web.limits]`. |
# [[web.vhosts.profiles]]
@@ -2752,18 +2813,20 @@ Hostname нормализуется при валидации и должен п
| --- | --- | --- | --- | --- |
| `user` | `String` | да | — | Существующий ключ длиной 1–64 байта из `[access.users]`; ограничение сохраняет bounded runtime-status и фильтры. |
| `secret_mode` | `"plain"` или `"dd"` | да | — | Точное представление секрета для Telegram Desktop. `ee` не поддерживается. |
| `max_sessions` | `usize` | нет | `web.limits.max_sessions_global` | Активные сессии этого профиля. |
| `max_streams` | `usize` | нет | `web.limits.max_streams_global` | Активные logical streams этого профиля. |
| `max_sessions` | `usize` | нет | `web.limits.max_sessions_global` | Живые сессии профиля на каждом конкретном домене. |
| `max_streams` | `usize` | нет | `web.limits.max_streams_global` | Живые логические потоки профиля на каждом конкретном домене. |
| `max_streams_per_session` | `usize` | нет | `web.limits.max_streams_per_session` | Активные logical streams в одной сессии профиля. |
Лимиты профиля должны быть ненулевыми и не превышать соответствующие глобальные границы. Повторяющиеся профили `(user, secret_mode)` в одном vhost запрещены.
Пределы профиля должны быть положительными и не превышать соответствующие глобальные лимиты. Повторные `(user, secret_mode)` внутри группы запрещены. У каждого конкретного домена отдельная идентичность квоты; ограничения процесса, IP и пользователя доступа продолжают действовать. Максимумы профиля не выделяют сессии или потоки заранее. Группа разделяет неизменяемую политику и один снимок decoy, а привязанные к домену capability и ограниченные индексы защиты от утечки остаются раздельными.
## Lifecycle WEB и управление через API
До добавления шаблонов через API в действующем процессе уже должен быть включён `web.allow_wildcard_hosts`. Запись желаемого флага не включает шаблоны немедленно; преждевременное обновление отклоняется атомарно до перезапуска.
- Config watcher и generation reload применяют `web.enabled`, policy carrier/negotiation, `web.debug`, `web.timeouts`, vhosts, profiles и decoy snapshots без перезапуска процесса. Валидируется и активируется один immutable expanded source snapshot; watcher candidate generation запускается только после активации этого поколения. Существующие сессии и начатые negotiation chains сохраняют issuance-time carrier candidates, limits, timeouts и абсолютные deadlines; новые bridge sessions используют одно зафиксированное активное поколение.
- Изменение `base_path` атомарно заменяет и маршрут новых запросов, и производную capability. Сначала выпустите новые ссылки и завершите затронутые активные sessions: установленные WebSockets и уже маршрутизированные обмены продолжаются; последующие запросы к старому base с подлинным для процесса bootstrap- или session-token получают локальный no-store `404`, а ставшая неактивной прежняя capability обрабатывается как обычный decoy traffic.
- Состав WEB-listeners и их trust policy в `server.listeners`, а также все значения `web.limits` принадлежат процессу и требуют перезапуска.
- `GET /v1/config` возвращает полное авторское дерево `[web]`, кроме производного snapshot `web.runtime`. `PATCH /v1/config` принимает sparse object `web`, глубоко сливает tables, целиком заменяет arrays, валидирует полный candidate и указывает `web.limits` в `deferred_process_fields` до перезапуска.
- Состав WEB-listeners и их trust policy в `server.listeners`, флаг `web.allow_wildcard_hosts` и все значения `web.limits` принадлежат процессу и требуют перезапуска.
- `GET /v1/config` возвращает полное авторское дерево `[web]`, кроме производного snapshot `web.runtime`. `PATCH /v1/config` принимает sparse object `web`, глубоко сливает tables, целиком заменяет arrays, валидирует полный candidate и указывает `web.allow_wildcard_hosts`, `web.limits` в `deferred_process_fields` до перезапуска.
- `GET /v1/runtime/web/status`, `/sessions`, `/sessions/{session_ref}` и `/operations/{operation_id}` предоставляют bounded несекретное runtime-состояние. POST controls закрывают выбранные сессии, очищают debug или сбрасывают carrier learning и требуют текущий случайный `runtime_instance`.
- `web.enabled = false` после активации прекращает новую выдачу bootstrap/session credentials, но не закрывает активные сессии. Для close-all дождитесь `manager.issuance_enabled = false`, отправьте асинхронный selector `all` и опрашивайте его operation.
- Существующих access users можно создавать, изменять, ротировать, включать, выключать и удалять через `/v1/users`. Создание пользователя не добавляет WEB-профиль. Отключение пользователя немедленно обновляет admission и завершает его активные сессии.
@@ -3446,6 +3509,7 @@ Hostname нормализуется при валидации и должен п
| [`user_max_tcp_conns_global_each`](#user_max_tcp_conns_global_each) | `usize` | `0` | `✔` |
| [`user_expirations`](#user_expirations) | `Map<String, DateTime<Utc>>` | `{}` | `✔` |
| [`user_data_quota`](#user_data_quota) | `Map<String, u64>` | `{}` | `✔` |
| [`user_quota_periods`](#user_quota_periods) | `Map<String, "daily" / "weekly" / "monthly">` | `{}` | `✔` |
| [`user_max_unique_ips`](#user_max_unique_ips) | `Map<String, usize>` | `{}` | `✔` |
| [`user_max_unique_ips_global_each`](#user_max_unique_ips_global_each) | `usize` | `0` | `✔` |
| [`user_max_unique_ips_mode`](#user_max_unique_ips_mode) | `"active_window"`, `"time_window"`, or `"combined"` | `"active_window"` | `✔` |
@@ -3531,6 +3595,26 @@ Hostname нормализуется при валидации и должен п
# Alice receives a 1 GiB quota.
alice = 1073741824
```
## user_quota_periods
- **Ограничения / валидация**: Каждый ключ должен соответствовать существующему `access.users` с положительным `access.user_data_quota`. Значения строго `"daily"`, `"weekly"` или `"monthly"`. Если таблица не пуста, общее число настроенных пользователей не должно превышать 65 536.
- **По умолчанию**: Пустая таблица. Без периода сохраняется прежняя квота с ручным сбросом и без автоматического обновления.
- **Календарь**: Все границы в UTC: ежедневно в полночь, еженедельно в понедельник в полночь, ежемесячно первого числа в полночь. Месяц календарный, а не фиксированное число дней.
- **Активация**: Включение или смена периода сохраняет расход и запускает автоматические сбросы со следующей границы. Удаление периода сохраняет расход и записанную календарную отметку. Hot reload применяет политику без смены secret и перезапуска процесса.
- **Жизненный цикл**: Ротация secret сохраняет расход и календарь. У отключённых пользователей и пользователей с истёкшим сроком календарь продолжает работать, но доступ не возобновляется. Удаление и повторное создание начинают учёт заново; CREATE требует успешной очистки сохранённого состояния до записи нового пользователя, в том числе без периодической квоты. Ручной `reset-quota` не сдвигает границы UTC. Откат часов не повторяет уже обработанный сброс, даже после удаления и возврата периода.
- **Хранение**: Worker записывает всю группу сбросов до публикации новых счётчиков. При ошибке записи расход не сбрасывается и календарные отметки не продвигаются; текущий трафик продолжает учитываться. Повторы используют монотонные задержки 1, 2, 4, 8, 16, 32, затем 60 секунд. После задержки или перезапуска выполняется один сброс к последнему наступившему периоду, без воспроизведения каждого пропущенного интервала. Запись только метаданных не затирает трафик, накопленный во время записи.
- **Запуск**: При включённых периодах существующий нечитаемый, некорректный, слишком большой или неподдерживаемый календарный checkpoint останавливает запуск. Отсутствующий файл создаёт новое состояние; корректный прежний формат сохраняет расход и получает календарные метаданные. Пределы файла — 16 MiB и 65 536 записей. Даже при корректном или отсутствующем файле первоначальная запись состояния должна завершиться успешно. Храните `general.quota_state_path` на постоянном носителе, доступном для записи.
- **Учёт**: Сброс заменяет поколение учёта; уже выданные relay-резервы остаются в своём поколении. Возврат старого резерва не уменьшает расход нового периода. Накопительная телеметрия трафика не меняется. Журнал предварительной записи трафика не добавляется: сохраняется прежний риск потери расхода после последнего checkpoint при аварии.
- **API**: Create/PATCH пользователя принимает `quota_period`; статус различает желаемую конфигурацию и действующий сохранённый календарь. См. [контракт API](../Architecture/API/API.md#recurring-quota-periods).
- **Пример**:
```toml
[access.user_data_quota]
alice = 1073741824
[access.user_quota_periods]
alice = "monthly"
```
## user_max_unique_ips
- **Ограничения / валидация**: `Map<String, usize>`.
- **Описание**: Ограничение на количество уникальных IP-адресов, с которых выполняется подключение, для каждого пользователя.