Documentation menu

Architecture

Architecture

How AutoTelegramForward is put together: 4 services, a 78-method gRPC contract, the ingest-to-delivery pipeline, ordered lanes and the durable queue.

Verified against the checkout built for v2.2.4 · updated 20 Oct 2026

On this page

Four services, one contract, one isolation boundary. The split exists for a reason: the Python core owns the Telegram conversation, the Go API owns trust, the logger owns history, and the web client owns presentation. Each can be replaced without touching the others.

Production Architecture

Multi-Service Resilient Forwarding Engine

Decoupled microservice topology with asynchronous non-blocking event loops, lease-based durable queues, and fail-closed tenant boundaries.

1. Ingestion

Telegram MTProto

Asynchronous client pool connects to DC2/DC4 via user sessions and bot tokens.

• Raw Updates
• Album Aggregator
• FloodWait Detector
2. Core Pipeline

Python Core (50051)

Strict stage-by-stage pipeline executing domain filtering, cleaning & shortcodes.

• [PIPELINE:NORMALIZE]
• [PIPELINE:ROUTE]
• [PIPELINE:FILTER]
• [PIPELINE:TRANSFORM]
3. Durable Queue

Lease Workers

Lease-based jobs with exponential backoff, jitter, and automatic account resume.

• Backpressure control
• Restricted bypass
• [PIPELINE:DELIVERED]
4. APIs & UI

Go API & Web (8080/8088)

High-performance REST API, interactive Telegram Bot UI, and React dashboard.

• Fail-Closed Auth
• Bot Manager FSM
• Logger (50052)
Storage Support:SQLite (WAL Mode with Busy Timeout) & PostgreSQL (Connection Pooling)
Multi-Tenant Isolation: Enforced Fail-Closed

Services

atf-core

50051 (gRPC)Data plane

Python core: MTProto sessions, the message pipeline, the durable queue and all persistence.

  • ClientPool keeps user and bot sessions alive, reconnects, and parks an account on FloodWait.
  • Content pipeline: normalize → route → filter → clean → transform → (optional) rewrite.
  • DurableQueue with leases, retries with jittered backoff, dead-letter capture.
  • Album aggregation inside a debounce window, so a media group is one job.

atf-api

8080 (HTTP)Control plane

Go service that owns authentication, tenancy, task CRUD, log queries, DLQ control and admin.

  • Registers 102 routes across system, auth, control-plane and admin groups.
  • Resolves identity once, then scopes every downstream gRPC call to that identity.
  • Mirrors every request — success and failure — into the logger with actor and correlation id.
  • Exposes readiness that answers in booleans, never configuration values.

atf-logger

50052 (gRPC)Observability

Structured log and audit store, kept off the delivery path so logging never slows a forward.

  • Stores attributable history: actor, service, level, subject task, correlation id.
  • Backs the dashboard log explorer, atf logs queries and the export endpoints.
  • Queries are paged and tenant-filtered server-side.

atf-web

8088 (HTTP)Client

React dashboard: tasks, per-task rules, sessions, AI configs, log explorer and queue control.

  • Talks only to the REST API — no direct database access.
  • Per-task rule editors render from the settings schema the API publishes.
  • Live log stream, delivery statistics, dead-letter inspection and replay.

Message pipeline

Ten stages, strictly ordered, each producing a log line you can query later. A message that stops early stops with a reason — never silently.

  1. 1

    INGEST

    A Telegram update arrives over MTProto and is normalized into one internal shape (UTF-16 handled, album fragments buffered).

  2. 2

    ROUTE

    The task list is resolved for the source chat and owner. A task that is paused or disabled is skipped here, not halfway through.

  3. 3

    AUTHORIZE

    Ownership and subscription state are checked before any work is performed on the message.

  4. 4

    FILTER

    Media-type gates, link rules, whitelist and blacklist — Unicode-folded, word-boundary keyword matching with ReDoS-refused patterns.

  5. 5

    CLEAN

    URLs, mentions, hashtags, phone numbers, emojis and custom word lists are stripped deterministically in a single pass.

  6. 6

    TRANSFORM

    Replacements, then the header and footer with shortcode interpolation resolved per delivery.

  7. 7

    REWRITE

    Optional AI pass for this task only. Provider failure is non-fatal: the transformed text is delivered as-is.

  8. 8

    QUEUE

    The delivery job is persisted with a lease, then handed to a worker. Nothing is delivered straight from the event handler.

  9. 9

    DELIVER

    MTProto send, album as one atomic group, with copy-protection fallback to download-and-re-upload.

  10. 10

    REPORT

    Outcome is recorded: SUCCEEDED, FAILED (retry), or OUTCOME_UNCONFIRMED when Telegram did not confirm.

Routing resolution

A task holds a list of sources and a list of targets. The first entry of each list is mirrored into the legacy scalar column so older code paths keep working, and the sanctioned write path keeps both in step. Overrides are additive only: a per-target rule can add to the task policy, never silently replace it.

Sources (M)

Channel A
Channel B
Group C · topic 12

Targets (N)

Mirror channel 1
Mirror channel 2
Archive · topic 4

Queue & durability

Durability guarantees
PropertyBehaviourWhy it matters
PersistenceJobs live in SQLite or PostgreSQL, not in memoryA restart does not lose accepted work
LeasesA worker holds a job under a lease while deliveringA crashed worker's job is reclaimed, not stranded
RetriesTransient errors retry with jittered exponential backoffOrdered backoff instead of a retry storm
Dead-letterJobs past their retry budget land in the DLQ with the last errorFailures are inspectable, replayable and purgable
IdempotencyMessageMap links source message to delivered messageA reclaim cannot double-post
UnconfirmedOUTCOME_UNCONFIRMED is a first-class state"Maybe delivered" is never reported as success

Ordering

Without help, a queue that runs jobs in parallel reorders a burst: post 3 finishes before post 1 and your mirror reads backwards. Ordered lanes serialize per task, so the sequence a channel published is the sequence the target shows, while different tasks still run concurrently.

Per-task lane

Jobs of one task never overtake each other.

Parallel across tasks

Throughput scales with the number of tasks, not the number of posts.

Edits keep their place

An edited post updates in order instead of jumping to the end.

Isolation boundary

The boundary sits in the API, not in the client. Identity is resolved once from a verified token, attached to the request, and every storage operation below that point is scoped by it. A caller who is not linked to Telegram resolves to id 0, which owns nothing — the failure direction is closed, never open.

Read the security model