This commit is contained in:
onysd 2026-07-28 05:28:58 +03:00
parent b6217d5191
commit d524e67b8a
2 changed files with 369 additions and 180 deletions

View file

@ -1,20 +1,236 @@
# Optional local config file for telesrv. # Optional local config file for telesrv.
# Copy to .env for local development. Do not commit real tokens or passwords. # Copy to .env for local development. Do not commit real tokens or passwords.
# Complete reference / 完整参数手册: # Complete reference:
# docs/configuration.en.md # docs/configuration.en.md
# docs/configuration.zh-CN.md # docs/configuration.zh-CN.md
#
# This file has two parts:
# 1. Everyday settings (below) -- IPs, ports, passwords, login methods,
# SMTP, links, calls/streaming. These are the ones the server-panel.py
# TUI shows and lets you edit, grouped the same way as here.
# 2. "Advanced / internal tuning" (further down) -- queue sizes, cache
# limits, retention windows and similar performance knobs. Sensible
# defaults are already set; most self-hosters never need to touch this
# part, so it's left out of the panel to keep that screen readable.
## Server & Network -- Where the server listens and what address it tells clients to connect to.
# Address and port the server listens on for MTProto client connections.
TELESRV_LISTEN=0.0.0.0:2398 TELESRV_LISTEN=0.0.0.0:2398
# Public IP or hostname clients should use to reach this server. Set this to
# your server's real public IP (or domain) once you're past local testing.
TELESRV_ADVERTISE_IP=127.0.0.1 TELESRV_ADVERTISE_IP=127.0.0.1
# Which "data center" number this server presents itself as. There's only
# ever one physical server, so this normally stays 2 -- no need to change it.
TELESRV_DC=2 TELESRV_DC=2
## Phone Login Codes -- How a login code gets to a phone number when someone signs in.
# development: every phone number accepts the same fixed code below (fine
# for local testing, or when only email/Telegram sign-in is used). webhook:
# sends a real random code to TELESRV_OTP_WEBHOOK_URL for actual delivery.
TELESRV_PHONE_CODE_DELIVERY_PROVIDER=development
# The fixed code accepted for every phone number when the provider above is
# "development". Change this if you keep phone login open in that mode.
TELESRV_DEV_AUTH_CODE=12345 TELESRV_DEV_AUTH_CODE=12345
# How many digits a real (webhook-delivered) login code has.
TELESRV_PHONE_CODE_LENGTH=5
# How long a login code stays valid after being sent.
TELESRV_AUTH_CODE_TTL=5m TELESRV_AUTH_CODE_TTL=5m
# How many wrong guesses are allowed before a code is rejected outright.
TELESRV_AUTH_CODE_MAX_ATTEMPTS=5 TELESRV_AUTH_CODE_MAX_ATTEMPTS=5
# Unauthenticated login-code issuance uses the same limits for existing and # Where webhook-delivered codes are POSTed, and the shared secret used to
# unknown phones. Phone numbers are SHA-256 digested before becoming Redis keys. # sign that request (see docs/otp-delivery.md for the exact payload).
TELESRV_AUTH_CODE_PHONE_RATE_LIMIT=5 TELESRV_OTP_WEBHOOK_URL=
TELESRV_AUTH_CODE_AUTH_KEY_RATE_LIMIT=20 TELESRV_OTP_WEBHOOK_SECRET=
TELESRV_AUTH_CODE_RATE_WINDOW=10m TELESRV_OTP_WEBHOOK_TIMEOUT=5s
## Email Login & Signup -- Signing in (or registering) with an email address instead of a phone number.
# Lets an existing phone-number account also add an email address for login
# codes. REQUIRE_SETUP forces every account without one to set it up.
TELESRV_LOGIN_EMAIL_ENABLE=false
TELESRV_LOGIN_EMAIL_REQUIRE_SETUP=false
# How many digits an email login code has.
TELESRV_LOGIN_EMAIL_CODE_LENGTH=6
# How an email login code is actually delivered: smtp (send it yourself,
# see the SMTP section below) or webhook (reuses the phone webhook above).
TELESRV_EMAIL_CODE_DELIVERY_PROVIDER=smtp
# Lets people register and log in with just an email address, no phone
# number at all. Uses the same SMTP/webhook delivery as email login above.
TELESRV_EMAIL_SIGNUP_ENABLE=false
# Comma-separated phone-number prefixes randomly assigned as the visible
# "phone number" for email-signup accounts, e.g. "888,380,373". Purely
# cosmetic -- doesn't need a client update to change.
TELESRV_EMAIL_SIGNUP_PHONE_PREFIXES=888
## SMTP (Outgoing Email) -- The mail server used to send login/signup codes by email.
TELESRV_SMTP_HOST=
TELESRV_SMTP_PORT=587
TELESRV_SMTP_USERNAME=
TELESRV_SMTP_PASSWORD=
# Sender address and display name shown on outgoing emails.
TELESRV_SMTP_FROM=
TELESRV_SMTP_FROM_NAME=OwpenGram
# Encryption mode: starttls, tls, or none (use "none" for a local test
# server like Mailpit that doesn't support encryption at all).
TELESRV_SMTP_TLS=starttls
TELESRV_SMTP_TIMEOUT=10s
## Public Links & Branding -- What clients show/open for links, and your product's name.
# Public web address for links this server generates (invite links, sticker
# packs, etc). Use your real domain once you have one, e.g. https://example.com.
TELESRV_PUBLIC_BASE_URL=https://telesrv.net
# Custom URL scheme (like "owpg://") that public pages use to open your
# patched client. Must match what your client builds were compiled with.
TELESRV_PUBLIC_APP_SCHEME=telesrv
# Optional: use "scheme://yourdomain.com/..." links instead of plain
# "scheme://...". Leave empty unless you specifically need this.
TELESRV_PUBLIC_APP_LINK_BASE=
# Address of your web client (if you have one) and the product name shown
# on public landing pages.
TELESRV_PUBLIC_WEB_BASE_URL=https://web.telesrv.net
TELESRV_PUBLIC_APP_NAME=telesrv
# Where the "Download" button on public pages links to.
TELESRV_PUBLIC_DOWNLOAD_URL=https://owpengram.org
# Warning text shown on a profile/channel the admin panel flagged as
# scam/fake. Leave empty to use the built-in English text.
TELESRV_SCAM_WARNING=
TELESRV_FAKE_WARNING=
## Admin Panel -- Login and access for the web-based admin dashboard.
# Secret token shared between the main server and the admin panel process --
# both must use the exact same value, or the admin panel can't save changes.
TELESRV_ADMIN_API_TOKEN=
# Password to log into the admin panel (or use a token below instead; set
# at least one of the two).
TELESRV_ADMIN_UI_PASSWORD=
TELESRV_ADMIN_UI_TOKEN=
# Encrypts the admin panel's login session cookie. Use a random string of
# at least 32 characters; changing it logs everyone out.
TELESRV_ADMIN_SESSION_KEY=
# Address the main server exposes its admin API on. Leave empty to disable
# the admin panel entirely; set to a loopback address (127.0.0.1:...) to
# enable it without exposing it outside this machine.
TELESRV_ADMIN_API_ADDR=
# Address the admin panel's own web UI listens on.
TELESRV_ADMIN_UI_ADDR=127.0.0.1:2600
## Bot API Gateway -- Optional HTTP gateway for bot libraries (e.g. python-telegram-bot).
# Leave empty to disable. Set to an address like 127.0.0.1:2500 to enable.
TELESRV_BOT_API_ADDR=
## Database -- Where the server stores its data.
TELESRV_POSTGRES_DSN=postgres://owpengram:owpengram@127.0.0.1:5432/owpengram?sslmode=disable
TELESRV_REDIS_ADDR=127.0.0.1:6399
TELESRV_REDIS_PASSWORD=
TELESRV_REDIS_DB=0
## Public Web Listener -- Serves public profile pages, avatars, and sticker/emoji pack previews.
# Host:port this listens on (not a URL). In production, keep this on
# loopback and let nginx (or similar) proxy public traffic to it.
TELESRV_PUBLIC_LINK_WEB_ADDR=127.0.0.1:2401
## Telegram Login (OpenID Connect) -- Self-hosted "Log in with Telegram" for third-party sites.
# Keep disabled until you've generated the required key files with:
# go run ./cmd/telegramloginkeygen -mode init
TELESRV_TELEGRAM_LOGIN_ENABLE=false
# The public HTTPS origin this login provider identifies itself as.
TELESRV_TELEGRAM_LOGIN_ISSUER=https://telesrv.net
# Allow plain HTTP instead of HTTPS (only for local testing).
TELESRV_TELEGRAM_LOGIN_ALLOW_HTTP=false
## Passkey Login (WebAuthn) -- Signing in with a device passkey/fingerprint instead of a code.
# Must match the real public domain you serve this from, or passkey login
# will fail (browsers check this against their own address bar).
TELESRV_PASSKEY_RP_ID=telesrv.net
## Calls -- Voice/video calls (1-to-1 and group), and the relay servers they need to work across networks.
# Turns off the TURN relay for 1-to-1 calls (falls back to direct
# connections only, which fail across most home/mobile networks).
TELESRV_TURN_ENABLE=true
# Port the built-in TURN/STUN relay listens on. Must be open in your firewall.
TELESRV_TURN_UDP_PORT=12400
# Public IP the relay tells clients to connect to. Leave empty to reuse
# TELESRV_ADVERTISE_IP above.
TELESRV_TURN_ADVERTISE_IP=
# Secret used to sign relay credentials. Leave empty for a random one
# generated at startup (fine for a single server instance).
TELESRV_TURN_SECRET=
# Port range the relay hands out for active call media. Must be open in
# your firewall.
TELESRV_TURN_RELAY_MIN_PORT=12500
TELESRV_TURN_RELAY_MAX_PORT=12999
# Turns off the group-call media server (SFU) -- group calls become
# signaling-only, with no actual audio/video.
TELESRV_SFU_ENABLE=true
# Single UDP port the group-call media server listens on. Must be open in
# your firewall.
TELESRV_SFU_UDP_PORT=12399
# Public IP the group-call media server tells clients to connect to. Leave
# empty to reuse TELESRV_ADVERTISE_IP above. Warning: setting this to
# 127.0.0.1 silently breaks calls from real devices.
TELESRV_SFU_ADVERTISE_IP=
# Maximum number of people allowed in one group call.
TELESRV_GROUPCALL_MAX_PARTICIPANTS=32
## Live Streaming -- Broadcasting live video into a channel (RTMP ingest, e.g. from OBS).
TELESRV_LIVESTREAM_ENABLE=true
# TCP address the RTMP ingest listens on for incoming streams.
TELESRV_LIVESTREAM_RTMP_ADDR=:2400
# Path to the ffmpeg executable used to process incoming streams. Leave as
# "ffmpeg" if it's already on your system PATH.
TELESRV_LIVESTREAM_FFMPEG_PATH=ffmpeg
## Maps -- Optional map previews (e.g. for shared locations) in patched clients.
# Mapbox access token. Leave empty to disable map previews.
TELESRV_MAPBOX_TOKEN=
## AI Features -- Optional AI-assisted message composing, business auto-replies, and translation.
# Master switch for the "improve my message" AI button in patched clients.
TELESRV_AI_ENABLED=true
# Auto-reply generator for Business accounts: "echo" just echoes the
# message back (safe default to verify the feature works end-to-end).
TELESRV_BUSINESS_AI_PROVIDER=echo
# Master switch for in-app message/chat translation.
TELESRV_TRANSLATION_ENABLED=true
# ==============================================================================
# Advanced / internal tuning
#
# Everything below this line is NOT shown in the server-panel.py TUI. These
# are performance/capacity knobs (queue sizes, cache limits, retention
# windows, economy tuning, etc.) with defaults that work fine for a single
# self-hosted server. Edit them here directly if you actually need to.
# ==============================================================================
# RSA key: server identity used in the MTProto key exchange. Auto-generated
# on first run if the file doesn't exist yet.
TELESRV_RSA_KEY=data/server_rsa.pem
# Enforces exact DC-ID matching during key exchange. Leave off (default):
# this server is always a single physical backend, but client forks
# intentionally send DC IDs 1..5 for it, which is expected, not an attack.
TELESRV_STRICT_DC_CHECK=false
# Enables MTProto-over-WebSocket on the same port (for web-based clients),
# and which browser page origins are allowed to open that connection.
TELESRV_WEBSOCKET_ENABLE=true
TELESRV_WEBSOCKET_ALLOWED_ORIGINS=http://localhost:1234,http://127.0.0.1:1234
# pprof debug/profiling endpoint (CPU/heap/goroutine snapshots). Keep this
# on loopback; use an SSH tunnel to reach it remotely. Empty disables it.
TELESRV_DEBUG_ADDR=127.0.0.1:6060
# MTProto admission and shared inbound RPC budgets. Negative connection/handshake limits disable # MTProto admission and shared inbound RPC budgets. Negative connection/handshake limits disable
# that gate; non-positive RPC values fall back to the built-in safe defaults. # that gate; non-positive RPC values fall back to the built-in safe defaults.
@ -27,7 +243,8 @@ TELESRV_MTPROTO_RPC_TIMEOUT=30s
TELESRV_MTPROTO_RPC_GLOBAL_WORKERS=256 TELESRV_MTPROTO_RPC_GLOBAL_WORKERS=256
TELESRV_MTPROTO_RPC_GLOBAL_MAX_TASKS=8192 TELESRV_MTPROTO_RPC_GLOBAL_MAX_TASKS=8192
TELESRV_MTPROTO_RPC_GLOBAL_MAX_BYTES=536870912 TELESRV_MTPROTO_RPC_GLOBAL_MAX_BYTES=536870912
# In-process 331s rpc_result ownership budgets: global >= auth >= session. # In-memory cache of recent RPC results, used to safely retry a request the
# client resends. Keep the limits ordered global >= auth >= session.
TELESRV_MTPROTO_RPC_RESULT_CACHE_MAX_ENTRIES=262144 TELESRV_MTPROTO_RPC_RESULT_CACHE_MAX_ENTRIES=262144
TELESRV_MTPROTO_RPC_RESULT_CACHE_MAX_BYTES=67108864 TELESRV_MTPROTO_RPC_RESULT_CACHE_MAX_BYTES=67108864
TELESRV_MTPROTO_RPC_RESULT_CACHE_AUTH_MAX_ENTRIES=32768 TELESRV_MTPROTO_RPC_RESULT_CACHE_AUTH_MAX_ENTRIES=32768
@ -44,140 +261,96 @@ TELESRV_MTPROTO_OUTBOUND_TRACKED_GLOBAL_MAX_BYTES=536870912
# Concurrent encrypted wire/codec/obfuscation scratch (shared bounded pool, not per connection). # Concurrent encrypted wire/codec/obfuscation scratch (shared bounded pool, not per connection).
TELESRV_MTPROTO_OUTBOUND_WRITE_GLOBAL_MAX_BYTES=536870912 TELESRV_MTPROTO_OUTBOUND_WRITE_GLOBAL_MAX_BYTES=536870912
# OTP delivery routing. "development" preserves the fixed phone code. "webhook" # Unauthenticated login-code issuance rate limits, by phone number digest and by connection.
# generates a random SMS code and sends it with the versioned protocol documented TELESRV_AUTH_CODE_PHONE_RATE_LIMIT=5
# in docs/otp-delivery.md. For an existing account, both modes first create the TELESRV_AUTH_CODE_AUTH_KEY_RATE_LIMIT=20
# durable 777000 message; Webhook is an additional delivery channel for that code. TELESRV_AUTH_CODE_RATE_WINDOW=10m
TELESRV_PHONE_CODE_DELIVERY_PROVIDER=development
TELESRV_PHONE_CODE_LENGTH=5
TELESRV_OTP_WEBHOOK_URL=
TELESRV_OTP_WEBHOOK_SECRET=
TELESRV_OTP_WEBHOOK_TIMEOUT=5s
# Optional login-email verification. EMAIL_CODE_DELIVERY_PROVIDER may be smtp # Postgres connection pool sizing.
# or webhook. Existing-account login codes are also mirrored into 777000 before TELESRV_POSTGRES_MAX_CONNS=50
# provider delivery; setup/change codes are provider-only. REQUIRE_SETUP forces TELESRV_POSTGRES_MIN_CONNS=16
# accounts without a login email to set one during the phone login flow.
TELESRV_LOGIN_EMAIL_ENABLE=false
TELESRV_LOGIN_EMAIL_REQUIRE_SETUP=false
TELESRV_LOGIN_EMAIL_CODE_LENGTH=6
TELESRV_EMAIL_CODE_DELIVERY_PROVIDER=smtp
TELESRV_SMTP_HOST=
TELESRV_SMTP_PORT=587
TELESRV_SMTP_USERNAME=
TELESRV_SMTP_PASSWORD=
TELESRV_SMTP_FROM=
TELESRV_SMTP_FROM_NAME=OwpenGram
TELESRV_SMTP_TLS=starttls
TELESRV_SMTP_TIMEOUT=10s
# Email-as-identity signup mode. When enabled, patched clients let the user # Cache linking each temporary encryption key to the permanent account key it
# register/log in with an email address instead of a phone number: the client # belongs to (part of Perfect Forward Secrecy). Entries are removed as soon
# encodes the email into a synthetic "888"-prefixed number and drives the # as a key is revoked or replaced, so this is just a performance cache.
# existing sendCode/signUp/signIn/changePhone flow unchanged; the server TELESRV_TEMP_KEY_CACHE_MAX_ENTRIES=262144
# decodes 888-numbers back to the email and delivers the code over SMTP TELESRV_TEMP_KEY_CACHE_TTL=30m
# (same TELESRV_SMTP_* settings as login email above) instead of SMS.
# account.changePhone is blocked from moving such an account to a number that
# doesn't decode back to a valid email, since this server has no real SMS
# delivery at all.
TELESRV_EMAIL_SIGNUP_ENABLE=false
# Comma-separated list of phone-number prefixes randomly assigned as the # In-memory read caches for channel rows/members/dialogs/boosts, kept fresh
# account's actual, visible short display number (unrelated to the internal # by database change notifications. Lower values use less RAM.
# "888" wire encoding above, which is fixed and never shown to anyone). TELESRV_CHANNEL_ROW_CACHE_MAX=50000
# Defaults to "888" alone. Change this to make freshly registered accounts TELESRV_CHANNEL_MEMBER_CACHE_MAX=100000
# look like they have a locally-flavored number, e.g. "888,380,373" — no TELESRV_CHANNEL_DIALOG_CACHE_MAX=100000
# client update needed, since clients never construct this number themselves, TELESRV_CHANNEL_BOOST_CACHE_MAX=100000
# they only ever display whatever the server assigns. TELESRV_CHANNEL_BOOST_CACHE_TTL=10s
TELESRV_EMAIL_SIGNUP_PHONE_PREFIXES=888
# Client-visible telesrv links. This is an HTTP(S) URL, not a listen address. # Background delivery workers that push new-message/update notifications to clients.
# Production uses https://telesrv.net. For local link/deeplink smoke tests use TELESRV_OUTBOX_WORKERS=4
# http://127.0.0.1:2401. Invalid schemes, credentials, query strings, fragments, TELESRV_OUTBOX_BATCH=100
# or a missing host fail startup instead of silently falling back. TELESRV_OUTBOX_INTERVAL=200ms
TELESRV_PUBLIC_BASE_URL=https://telesrv.net TELESRV_OUTBOX_LEASE_TIMEOUT=30s
# Terminal failed outbox heads are kept briefly for diagnosis, then only the online
# Public landing pages auto-open this custom scheme. It must match the scheme # delivery task is removed. The durable update remains available to getDifference.
# registered by every patched client build; tg/http/https are rejected. TELESRV_OUTBOX_POISON_RETENTION=1m
TELESRV_PUBLIC_APP_SCHEME=telesrv TELESRV_OUTBOX_POISON_CLEANUP_INTERVAL=15s
TELESRV_OUTBOUND_PUSH_TIMEOUT=200ms
# Optional host-based app-link root for multi-server clients. When set, public # How many messages an account may send per rate-limit window; <=0 disables this limit.
# links use e.g. owpg://example.com/oauth and owpg://example.com/username while TELESRV_SEND_RATE_LIMIT=30
# the scheme above remains accepted for existing/in-flight links. The value TELESRV_SEND_RATE_WINDOW=1m
# must be exactly <custom-scheme>://<host>, without port/path/query/fragment. # Rate limit for "catch up on missed updates" requests; <=0 disables it.
TELESRV_PUBLIC_APP_LINK_BASE= TELESRV_CATCHUP_RATE_LIMIT=0
TELESRV_CATCHUP_RATE_WINDOW=1m
# Web client target and display brand used by public landing pages. # Cap on how many members get individually notified when a large channel changes; <=0 uses the built-in default.
TELESRV_PUBLIC_WEB_BASE_URL=https://web.telesrv.net TELESRV_CHANNEL_NUDGE_MAX_TARGETS=0
TELESRV_PUBLIC_APP_NAME=telesrv
# Product site/download page linked from the "Download" button in the public
# landing pages' header.
TELESRV_PUBLIC_DOWNLOAD_URL=https://owpengram.org
# Profile warning text injected into getFullUser/getFullChannel About for peers
# flagged SCAM/FAKE from the admin panel. Empty keeps built-in English defaults.
# Clients cannot localize server text, so set your audience language here. The
# stored bio/description is never overwritten; the warning is re-applied from the
# flag on every read and survives the owner editing their description.
TELESRV_SCAM_WARNING=
TELESRV_FAKE_WARNING=
# Admin API / Admin UI 配置
#
# TELESRV_ADMIN_API_TOKEN 是主服务 (cmd/telesrv) 暴露 Admin REST API 的鉴权 token
# 也是 Admin UI (cmd/telesrv-admin) 调用 Admin API 时使用的凭证。
# 重要:主服务与 Admin UI 必须使用完全相同的 TELESRV_ADMIN_API_TOKEN否则管理后台无法执行写操作。
TELESRV_ADMIN_API_TOKEN=
# TELESRV_ADMIN_UI_PASSWORD 是登录管理后台的密码;
# 也可改用 TELESRV_ADMIN_UI_TOKEN二者至少填一个。
TELESRV_ADMIN_UI_PASSWORD=
TELESRV_ADMIN_UI_TOKEN=
# TELESRV_ADMIN_SESSION_KEY 用于加密 Admin UI 的登录 session cookie。
# 生产环境请使用至少 32 字节的强随机字符串,修改后会导致已登录会话失效。
TELESRV_ADMIN_SESSION_KEY=
# 主服务 Admin API 默认关闭Admin UI 写操作需要同时配置强 token 并显式开启该 loopback 监听地址。
TELESRV_ADMIN_API_ADDR=
# Admin UI 监听地址默认值通常无需修改RTMP ingest 保留 2400。
TELESRV_ADMIN_UI_ADDR=127.0.0.1:2600
TELESRV_POSTGRES_DSN=postgres://owpengram:owpengram@127.0.0.1:5432/owpengram?sslmode=disable
TELESRV_REDIS_ADDR=127.0.0.1:6399
TELESRV_REDIS_PASSWORD=
TELESRV_REDIS_DB=0
# Bounded retention/GC. User/channel update rows are only pruned behind protocol-safe floors. # Bounded retention/GC. User/channel update rows are only pruned behind protocol-safe floors.
TELESRV_UPDATE_EVENT_RETENTION=168h TELESRV_UPDATE_EVENT_RETENTION=168h
TELESRV_BOT_API_UPDATE_RETENTION=24h TELESRV_BOT_API_UPDATE_RETENTION=24h
TELESRV_ORPHAN_AUTH_KEY_RETENTION=24h TELESRV_ORPHAN_AUTH_KEY_RETENTION=24h
# Terminal failed outbox heads are kept briefly for diagnosis, then only the online
# delivery task is removed. The durable update remains available to getDifference.
TELESRV_OUTBOX_POISON_RETENTION=1m
TELESRV_OUTBOX_POISON_CLEANUP_INTERVAL=15s
TELESRV_RETENTION_INTERVAL=1h TELESRV_RETENTION_INTERVAL=1h
TELESRV_RETENTION_BATCH=10000 TELESRV_RETENTION_BATCH=10000
# Cleanup of abandoned (never-finished) file upload fragments.
TELESRV_UPLOAD_PART_TTL=24h
TELESRV_UPLOAD_PART_GC_INTERVAL=30m
TELESRV_UPLOAD_PART_GC_BATCH=10000
TELESRV_UPLOAD_INFLIGHT_MAX_BYTES=4194304000
TELESRV_UPLOAD_INFLIGHT_MAX_PARTS=8000
TELESRV_UPLOAD_INFLIGHT_MAX_FILES=64
# PFS temp->perm binding cache; write-side revoke/rebind invalidates entries precisely. # Server-side fetching of external media links and link-preview cards (SSRF-safe fetch + size/rate limits).
TELESRV_TEMP_KEY_CACHE_MAX_ENTRIES=262144 TELESRV_EXTERNAL_MEDIA_ENABLE=true
TELESRV_TEMP_KEY_CACHE_TTL=30m TELESRV_EXTERNAL_MEDIA_MAX_BYTES=10485760
TELESRV_EXTERNAL_MEDIA_RATE_PER_MIN=60
TELESRV_WEBPAGE_PREVIEW_ENABLE=true
TELESRV_WEBPAGE_PREVIEW_MAX_BYTES=5242880
TELESRV_WEBPAGE_PREVIEW_RATE_PER_MIN=300
# Optional. Enables Mapbox-backed map previews and TDesktop map picker config. # Map tile disk cache location (paired with TELESRV_MAPBOX_TOKEN above).
TELESRV_MAPBOX_TOKEN=
TELESRV_MAPTILE_CACHE_DIR=data/maptiles TELESRV_MAPTILE_CACHE_DIR=data/maptiles
# Data directories the server seeds/serves media and content from.
TELESRV_LANGPACK_SEED_DIR=data/langpack TELESRV_LANGPACK_SEED_DIR=data/langpack
TELESRV_OFFICIAL_GIFTS_DIR=data/official-gifts TELESRV_OFFICIAL_GIFTS_DIR=data/official-gifts
# Built-in original demo Star Gifts (geometric Lottie we author ourselves — not TELESRV_BLOB_DIR=data/blobs
# Telegram's copyrighted assets) are available to import from the admin console's TELESRV_STICKER_SEED_DIR=data/sticker-seed
# "Default gifts" tab; nothing is imported automatically and no config is needed. # Caps how many built-in sticker sets get imported on startup; <=0 means no limit.
# An operator-supplied official Star Gift snapshot (see cmd/giftfetch) can also be TELESRV_STICKER_SEED_MAX_SETS=300
# dropped into TELESRV_OFFICIAL_GIFTS_DIR for manual import — empty by default. # Sticker set auto-installed for every newly registered account; <=0 disables this.
# Star Gift expiry/auction worker. TON values are handled by the local ledger; TELESRV_DEFAULT_STICKER_SET_ID=0
# no wallet, Fragment or chain node endpoint is configured or contacted.
# New-account perks: free Telegram Premium months and starting Stars balance.
TELESRV_PREMIUM_GRANT_MONTHS=3
TELESRV_STARS_STARTING_GRANT=1000
TELESRV_PREMIUM_SWEEP_INTERVAL=1m
TELESRV_PREMIUM_SWEEP_BATCH=500
# Origins allowed for WebAuthn/passkey requests; empty means any origin is accepted
# (the server usually can't predict a mobile app's origin ahead of time).
TELESRV_PASSKEY_ALLOWED_ORIGINS=
# Star Gifts: unique collectible gifts, transfer/resale, crafting/upgrades, auctions. TON
# values are a purely local ledger inside the server -- no real wallet, Fragment, or
# blockchain is ever contacted.
TELESRV_STARGIFT_SWEEP_INTERVAL=15s TELESRV_STARGIFT_SWEEP_INTERVAL=15s
TELESRV_STARGIFT_SWEEP_BATCH=1000 TELESRV_STARGIFT_SWEEP_BATCH=1000
# Internal nanoton granted once per user on first local-ledger access. # Internal nanoton granted once per user on first local-ledger access.
@ -192,24 +365,31 @@ TELESRV_STARGIFT_TRANSFER_DELAY=0s
TELESRV_STARGIFT_RESELL_DELAY=0s TELESRV_STARGIFT_RESELL_DELAY=0s
TELESRV_STARGIFT_CRAFT_DELAY=0s TELESRV_STARGIFT_CRAFT_DELAY=0s
TELESRV_STARGIFT_CRAFT_CHANCE_PERMILLE=250 TELESRV_STARGIFT_CRAFT_CHANCE_PERMILLE=250
TELESRV_BLOB_DIR=data/blobs
TELESRV_STICKER_SEED_DIR=data/sticker-seed
# Optional public-link Web listener for /<username>, profile avatars, # 1-to-1 call timing/limits.
# sticker/custom emoji sets, and shared folders. This is a host:port bind TELESRV_CALL_RING_TIMEOUT=90s
# address without a URL scheme. It is not replaced by TELESRV_PUBLIC_BASE_URL. TELESRV_CALL_TOMBSTONE_TTL=60s
# Production should keep it on loopback and reverse-proxy the documented routes TELESRV_CALL_MAX_ACTIVE_PER_USER=4
# through nginx; public canonical URLs use TELESRV_PUBLIC_BASE_URL. TELESRV_CALL_SIGNALING_MAX_BYTES=65536
TELESRV_PUBLIC_LINK_WEB_ADDR=127.0.0.1:2401 TELESRV_CALL_SIGNALING_RATE=50
TELESRV_CALL_EXPIRY_INTERVAL=1s
TELESRV_CALL_TURN_CREDENTIAL_TTL=6h
# Forces calls through the TURN relay even when a direct connection would work (debugging only).
TELESRV_CALL_FORCE_RELAY=false
# Self-hosted Telegram Login / OpenID Connect. The provider is mounted on the # Group call housekeeping (participant liveness checks, stale-entry cleanup).
# public-link listener above. Keep disabled until all three local key files TELESRV_GROUPCALL_CHECK_TTL=45s
# have been generated with `go run ./cmd/telegramloginkeygen -mode init`. TELESRV_GROUPCALL_SWEEP_INTERVAL=10s
TELESRV_TELEGRAM_LOGIN_ENABLE=false
TELESRV_TELEGRAM_LOGIN_ISSUER=https://telesrv.net # Extra live-streaming options: where the "connect OBS here" URL points (auto-derived if
# Set true to permit an HTTP issuer and HTTP registered origins/redirect URIs # empty), the working directory for stream segments, and how many seconds of each stream
# on any hostname or IP address. HTTPS remains the default when false. # are kept in memory.
TELESRV_TELEGRAM_LOGIN_ALLOW_HTTP=false TELESRV_LIVESTREAM_RTMP_URL=
TELESRV_LIVESTREAM_WORK_DIR=
TELESRV_LIVESTREAM_SEGMENT_KEEP=32
# Telegram Login (OpenID Connect) advanced settings: local key files (generate with
# `go run ./cmd/telegramloginkeygen -mode init`), token lifetimes, and cleanup.
TELESRV_TELEGRAM_LOGIN_SIGNING_KEYS_FILE=data/telegram-login/signing-keys.json TELESRV_TELEGRAM_LOGIN_SIGNING_KEYS_FILE=data/telegram-login/signing-keys.json
TELESRV_TELEGRAM_LOGIN_CODE_KEYS_FILE=data/telegram-login/code-keys.json TELESRV_TELEGRAM_LOGIN_CODE_KEYS_FILE=data/telegram-login/code-keys.json
TELESRV_TELEGRAM_LOGIN_SECRET_PEPPER_FILE=data/telegram-login/client-secret-pepper TELESRV_TELEGRAM_LOGIN_SECRET_PEPPER_FILE=data/telegram-login/client-secret-pepper
@ -222,27 +402,27 @@ TELESRV_TELEGRAM_LOGIN_RETENTION=168h
TELESRV_TELEGRAM_LOGIN_SWEEP_INTERVAL=5m TELESRV_TELEGRAM_LOGIN_SWEEP_INTERVAL=5m
TELESRV_TELEGRAM_LOGIN_SWEEP_BATCH=500 TELESRV_TELEGRAM_LOGIN_SWEEP_BATCH=500
# AI compose for TDesktop/Android input box rewrite/polish. # AI compose provider chain (tried in order; "local" is deterministic and never leaves
# The local provider is deterministic and does not call external services. # the server) and its limits.
TELESRV_AI_ENABLED=true
TELESRV_AI_PROVIDERS=local TELESRV_AI_PROVIDERS=local
TELESRV_AI_TIMEOUT=15s TELESRV_AI_TIMEOUT=15s
TELESRV_AI_RATE_LIMIT=20 TELESRV_AI_RATE_LIMIT=20
TELESRV_AI_RATE_WINDOW=1m TELESRV_AI_RATE_WINDOW=1m
# When false (default), logs only length/provider/status for AI calls -- never the
# user's actual input or generated text.
TELESRV_AI_LOG_CONTENT=false TELESRV_AI_LOG_CONTENT=false
# Chat/message translation reuses the remote providers declared above. The # Chat/message translation reuses the remote providers declared above. The
# deterministic "local" AI provider is excluded because it cannot translate. # deterministic "local" AI provider is excluded because it cannot translate.
# Leave TRANSLATION_PROVIDERS empty to use all configured remote AI providers, # Leave TRANSLATION_PROVIDERS empty to use all configured remote AI providers,
# or provide a comma-separated subset such as "openai,gemini". # or provide a comma-separated subset such as "openai,gemini".
TELESRV_TRANSLATION_ENABLED=true
TELESRV_TRANSLATION_PROVIDERS= TELESRV_TRANSLATION_PROVIDERS=
TELESRV_TRANSLATION_TIMEOUT=15s TELESRV_TRANSLATION_TIMEOUT=15s
# Counts translated text items, not RPC envelopes (one RPC may contain 20). # Counts translated text items, not RPC envelopes (one RPC may contain 20).
TELESRV_TRANSLATION_RATE_LIMIT=60 TELESRV_TRANSLATION_RATE_LIMIT=60
TELESRV_TRANSLATION_RATE_WINDOW=1m TELESRV_TRANSLATION_RATE_WINDOW=1m
# External providers are optional. Keep API keys in TELESRV_* variables here; # External AI providers are optional. Keep API keys in TELESRV_* variables here;
# the loader rejects non-TELESRV keys from .env files by design. # the loader rejects non-TELESRV keys from .env files by design.
# TELESRV_AI_OPENAI_KIND=openai_responses # TELESRV_AI_OPENAI_KIND=openai_responses
# TELESRV_AI_OPENAI_API_KEY= # TELESRV_AI_OPENAI_API_KEY=
@ -268,10 +448,3 @@ TELESRV_TRANSLATION_RATE_WINDOW=1m
# TELESRV_AI_KIMI_MODEL=kimi-k2.6 # TELESRV_AI_KIMI_MODEL=kimi-k2.6
# TELESRV_AI_KIMI_THINKING=disabled # TELESRV_AI_KIMI_THINKING=disabled
# TELESRV_AI_KIMI_TEMPERATURE=0.6 # TELESRV_AI_KIMI_TEMPERATURE=0.6
# Business automation reply provider:
# echo (default/empty), template/quick_reply/quick-reply, or
# ai/compose_ai/ai_compose/aicompose/kimi to reuse TELESRV_AI_PROVIDERS.
# Custom provider names such as "ollama" are selected through TELESRV_AI_PROVIDERS;
# use TELESRV_BUSINESS_AI_PROVIDER=ai for those.
TELESRV_BUSINESS_AI_PROVIDER=echo

View file

@ -324,6 +324,8 @@ def read_env_value(key: str) -> str | None:
_ACTIVE_FIELD_RE = re.compile(r"^(TELESRV_[A-Z0-9_]+)=(.*)$") _ACTIVE_FIELD_RE = re.compile(r"^(TELESRV_[A-Z0-9_]+)=(.*)$")
_COMMENTED_FIELD_RE = re.compile(r"^#\s*(TELESRV_[A-Z0-9_]+)=(.*)$") _COMMENTED_FIELD_RE = re.compile(r"^#\s*(TELESRV_[A-Z0-9_]+)=(.*)$")
_SENSITIVE_KEY_RE = re.compile(r"(PASSWORD|SECRET|_TOKEN|API_KEY)") _SENSITIVE_KEY_RE = re.compile(r"(PASSWORD|SECRET|_TOKEN|API_KEY)")
_GROUP_HEADER_RE = re.compile(r"^##\s*(.+?)\s*--\s*(.+)$")
_SECTION_BREAK_RE = re.compile(r"^#\s*={10,}\s*$")
@dataclass @dataclass
@ -341,40 +343,46 @@ class EnvField:
@dataclass @dataclass
class EnvGroup: class EnvGroup:
title: str title: str
description: str = ""
fields: list[EnvField] = field(default_factory=list) fields: list[EnvField] = field(default_factory=list)
def parse_env_template() -> list[EnvGroup]: def parse_env_template() -> list[EnvGroup]:
"""Parses .env.example into groups of fields for the editor. Grouping """Parses .env.example into panel-visible groups.
follows the file's own blank-line-separated blocks; a group's title is
the first sentence of whichever field in it has a comment (falling back Only fields inside an explicit "## Title -- description." header belong
to its first key when none of them do, e.g. the bare Postgres/Redis DSN to a group and show up in the editor. A "# ====...====" banner line (the
pair that has no comment of its own).""" "Advanced / internal tuning" divider) ends panel-group collection for
the rest of the file -- those fields are still perfectly valid config
the server reads normally, they're just left out of the TUI on purpose
to keep it to what a self-hoster actually needs to touch."""
if not ENV_EXAMPLE_FILE.exists(): if not ENV_EXAMPLE_FILE.exists():
return [] return []
groups: list[EnvGroup] = [] groups: list[EnvGroup] = []
current_fields: list[EnvField] = [] current: EnvGroup | None = None
pending: list[str] = [] pending: list[str] = []
in_comment_run = False in_comment_run = False
seen_keys: set[str] = set() seen_keys: set[str] = set()
def flush_group() -> None:
nonlocal current_fields
if current_fields:
described = next((f.description for f in current_fields if f.description), "")
if described:
title = described.split(". ", 1)[0].strip().rstrip(".")
else:
title = current_fields[0].key
groups.append(EnvGroup(title=title[:70], fields=current_fields))
current_fields = []
for raw_line in ENV_EXAMPLE_FILE.read_text(encoding="utf-8", errors="replace").splitlines(): for raw_line in ENV_EXAMPLE_FILE.read_text(encoding="utf-8", errors="replace").splitlines():
stripped = raw_line.strip() stripped = raw_line.strip()
if not stripped: if not stripped:
flush_group() pending = []
in_comment_run = False
continue
header = _GROUP_HEADER_RE.match(stripped)
if header:
current = EnvGroup(title=header.group(1).strip(), description=header.group(2).strip())
groups.append(current)
pending = []
in_comment_run = False
continue
if _SECTION_BREAK_RE.match(stripped):
current = None
pending = [] pending = []
in_comment_run = False in_comment_run = False
continue continue
@ -388,10 +396,11 @@ def parse_env_template() -> list[EnvGroup]:
# occurrence wins and later repeats fold into descriptive text. # occurrence wins and later repeats fold into descriptive text.
if active.group(1) not in seen_keys: if active.group(1) not in seen_keys:
seen_keys.add(active.group(1)) seen_keys.add(active.group(1))
current_fields.append(EnvField( if current is not None:
key=active.group(1), default_value=active.group(2), current.fields.append(EnvField(
description=" ".join(pending), enabled_by_default=True, key=active.group(1), default_value=active.group(2),
)) description=" ".join(pending), enabled_by_default=True,
))
in_comment_run = False in_comment_run = False
continue continue
@ -399,10 +408,11 @@ def parse_env_template() -> list[EnvGroup]:
commented = _COMMENTED_FIELD_RE.match(stripped) commented = _COMMENTED_FIELD_RE.match(stripped)
if commented and commented.group(1) not in seen_keys: if commented and commented.group(1) not in seen_keys:
seen_keys.add(commented.group(1)) seen_keys.add(commented.group(1))
current_fields.append(EnvField( if current is not None:
key=commented.group(1), default_value=commented.group(2), current.fields.append(EnvField(
description=" ".join(pending), enabled_by_default=False, key=commented.group(1), default_value=commented.group(2),
)) description=" ".join(pending), enabled_by_default=False,
))
in_comment_run = False in_comment_run = False
continue continue
text = stripped.lstrip("#").strip() text = stripped.lstrip("#").strip()
@ -417,8 +427,7 @@ def parse_env_template() -> list[EnvGroup]:
# whatever comment run was in progress without touching fields. # whatever comment run was in progress without touching fields.
in_comment_run = False in_comment_run = False
flush_group() return [g for g in groups if g.fields]
return groups
def current_env_values(groups: list[EnvGroup]) -> dict[str, str]: def current_env_values(groups: list[EnvGroup]) -> dict[str, str]:
@ -717,6 +726,8 @@ class EnvEditorScreen(Screen):
with VerticalScroll(id="env-scroll"): with VerticalScroll(id="env-scroll"):
for i, group in enumerate(self._groups): for i, group in enumerate(self._groups):
with Collapsible(title=f"{group.title} ({len(group.fields)})", id=f"env-group-{i}"): with Collapsible(title=f"{group.title} ({len(group.fields)})", id=f"env-group-{i}"):
if group.description:
yield Static(group.description, classes="env-group-desc")
for f in group.fields: for f in group.fields:
with Vertical(classes="env-field"): with Vertical(classes="env-field"):
badge = "" if f.enabled_by_default else " [dim i](optional, currently disabled)[/]" badge = "" if f.enabled_by_default else " [dim i](optional, currently disabled)[/]"
@ -1098,6 +1109,11 @@ class ServerPanelApp(App):
height: 1fr; height: 1fr;
padding: 1 2; padding: 1 2;
} }
.env-group-desc {
color: $text-muted;
text-style: italic;
margin: 1 0 0 2;
}
.env-field { .env-field {
height: auto; height: auto;
margin: 1 0 0 2; margin: 1 0 0 2;