Documentation menu
Getting started
New in v2.0
Feature deep dives
Reference
Connections
Live Service Connection Map (3D)
Simulate AutoTelegramForward in 3D: verified component links, protocol boundaries and the request paths you can replay packet by packet.
Verified against the checkout built for v2.2.4 · updated 20 Oct 2026
On this page
Imports tell you how one language is arranged. Connections tell you how the system behaves: 23 links across 16 components, every one of them derived from a real call site in the checkout. Orbit the map, then replay one of the 3 traced paths and watch a packet cross the same boundaries a live message crosses.
3D connection simulation
A post is forwarded
From an incoming Telegram update to a delivered copy in every target chat, with the durable queue absorbing rate limits.
- 1
Update arrives @ telegram-cloud
MTProto push from the source chat the session is authorised for.
PIPELINE:INGESTPIPELINE:DIALOG_FETCH - 2
Normalise @ core-ingest
Album items are aggregated and the update is turned into a domain event.
- 3
Resolve policy @ core-pipeline
Matching tasks, per-source/policy overrides, whitelists and blacklists.
PIPELINE:POLICYPIPELINE:POLICY_TARGETPIPELINE:NO_RULESPIPELINE:NO_MATCH - 4
Filter & rewrite @ core-pipeline
Filters, cleaner, shortcodes, replacements — and the optional AI pass.
PIPELINE:FILTERPIPELINE:CLEANERPIPELINE:TRANSFORMPIPELINE:AI_REWRITE - 5
Rewrite (optional) @ ai-rewriter
Only when the task enables it; an unreachable provider passes the text through.
PIPELINE:AI_REWRITE - 6
Enqueue per target @ core-queue
One durable job per target with an ordering key, so bursts keep their order.
PIPELINE:ENQUEUE - 7
Lease & deliver @ core-queue
Lease-based workers with exponential backoff and FloodWait-aware rescheduling.
PIPELINE:RETRYPIPELINE:FLOODWAIT - 8
Delivered @ telegram-cloud
Copy mode or native forward, into the target chat or forum topic.
PIPELINE:DELIVERED - 9
Recorded @ logger-svc
Every stage, drop and outcome lands in the log store with a correlation id.
PIPELINE:DROPPIPELINE:FILTER_DROPPIPELINE:DUP_FILTERPIPELINE:DELIVERY_FAILPIPELINE:DELIVERY_TIMEOUT
- 1
Operator saves @ web-ui
A tenant-scoped token is attached to the request; no API key ever reaches the browser.
- 2
Gateway proxies @ web-backend
The Rust gateway forwards to the API and keeps upstream addressing server-side.
- 3
Guardrails @ api-guardrails
Correlation id, access log, log-policy gate and audit event are applied first.
- 4
Authorise @ api-rest
Fail-closed tenant check: the rule is only touched if it belongs to the caller.
- 5
RPC @ core-grpc
The write becomes a protobuf call on the engine surface.
- 6
Apply @ core-pipeline
Optimistic concurrency on the rule version; metadata[content_policy] carries the per-task rules.
- 7
Persist @ core-storage
Transactional write plus the outbox entry used for audit.
- 8
Audit @ logger-svc
Before/after images are written to the log store for attribution.
- 1
Open the log explorer @ web-ui
Filters by task, stage, severity and correlation id.
- 2
Stream @ web-backend
SSE feed so the view updates without polling.
- 3
Query @ api-rest
Bounded window, tenant-scoped, with redaction already applied upstream.
- 4
Read @ logger-svc
Field-aware redaction happens at write time, so even an admin read is already safe.
- 5
Return page @ logger-store
Bounded-label stats and slow-query events are exposed alongside the rows.
- 6
Dead-letter actions @ core-queue
Inspect, replay or purge failed deliveries for the tenant.
PIPELINE:ENQUEUE_DROP
Trace
- Pick a scenario to replay a real request path.
A post is forwarded
From an incoming Telegram update to a delivered copy in every target chat, with the durable queue absorbing rate limits.
9 steps · 6 components touched
An operator edits a task
A dashboard write travels through the gateway, the REST control plane and gRPC into the engine — with optimistic concurrency and an audit trail.
8 steps · 8 components touched
An event is investigated
Live logs, the diagnostic window and the dead-letter queue: how an operator answers "what happened to that message?".
6 steps · 6 components touched
Protocol boundaries
12 boundaries, and the file that proves each one. A boundary is where a version mismatch, a timeout or a serialization change becomes visible — which is exactly why they are worth listing instead of hiding behind "microservices".
api → core
gRPC :50051
Every rule, session and delivery command crosses this boundary as a protobuf RPC.
verified in: api/internal/grpcclient/grpcclient.go, api/internal/grpcclient/rpc_auth_test.go, api/internal/handlers/system.go
api → logger
gRPC :50052
The API writes its own logs, audits and latency events to the same central store.
verified in: api/internal/handlers/system.go, api/internal/handlers/system_test.go, api/main.go
core → logger
gRPC :50052
Pipeline stages emit [PIPELINE:*] events through the shared log client.
verified in: core/application/pipeline.py, core/config.py, core/main.py
core → AI rewriter
HTTP OpenAI-compatible
Optional rewrite pass. Unreachable provider degrades to pass-through, never to data loss.
verified in: core/infrastructure/ai/providers.py, core/infrastructure/ai/transformer.py
Web gateway → api
HTTP reverse proxy + SSE
Server-side proxy keeps the API key out of the browser and streams the live log feed.
verified in: web/backend/src/main.rs, web/frontend/.catalog-build/fr.js, web/frontend/src/locales/fr.ts
web-frontend → Web gateway
fetch /api/v1/*
The React app only ever talks to its own origin; the gateway owns upstream addressing.
verified in: web/frontend/src/api.ts
mobile → api
HTTPS + bearer token
Same REST contract as the dashboard, with offline-tolerant error surfaces.
verified in: mobile/lib/core/api_client.dart, mobile/lib/core/app_state.dart, mobile/lib/core/env.dart
DevOps CLI → api
HTTP health + config probes
Diagnostics read the same endpoints an operator would curl.
verified in: atfcli/application/gateways.py, atfcli/application/use_cases/logs.py, atfcli/application/use_cases/ops.py
root → Web gateway
Caddy reverse proxy
Optional --profile tls terminates TLS and forwards to the gateway.
verified in: Caddyfile
contracts → api
protoc / buf
The Go stubs and the Python servicers are both generated from one .proto file.
verified in: api/proto/autoforward.pb.go, api/proto/autoforward_grpc.pb.go
contracts → core
protoc / buf
One schema, two runtimes; a mismatch fails at the gRPC boundary, not in production.
verified in: core/infrastructure/grpc_server/account_servicer.py, core/infrastructure/grpc_server/admin_servicer.py, core/infrastructure/grpc_server/internal_auth.py
website → root
build-time ingestion
The site derives its numbers from the same files a contributor edits.
verified in: website/scripts/sync-architecture.mjs
Container topology
The compose file is the deployment truth, and it is where the dependency order actually lives: the engine waits for the logger, the API waits for the engine, the gateway waits for the API. The TLS edge is an optional profile, so a minimal install runs four containers instead of five.
| Service | Container | Build | Ports | Waits for | Network |
|---|---|---|---|---|---|
redis | atf-redis | redis:7.4.11-alpine3.21 | internal only | — | atf-net |
logger | atf-logger | Dockerfile.logger-rust | internal only | — | atf-net |
core | atf-core | Dockerfile.core | internal only | logger | atf-net |
api | atf-api | Dockerfile.api | 8080:8080 | core | atf-net |
web | atf-web | Dockerfile.web | 8088:8088 | api | atf-net |
caddy | atf-caddy | caddy:2-alpine | 80:80 443:443 | web | atf-net |
38 environment keys are declared across the stack, and 5 service defines a healthcheck. The compose gate on this site also verifies that no credential is committed as a literal default — the one place it found fallback values is listed under open items on the atlas overview.
gRPC surface
One .proto file, 13 services, 78 methods and 141 message types — generated into Go stubs for the control plane and Python servicers for the engine. A mismatch cannot reach production silently: the call simply does not exist.
| Service | Methods | Domain | Sample methods |
|---|---|---|---|
SessionControlService | 9 | Session | ListSessions StartLogin SubmitCode SubmitPassword |
ForwardRuleControlService | 11 | ForwardRule | ListRules GetRule CreateRule UpdateRule |
DeliveryControlService | 4 | Delivery | GetDeliveryStats ListDeadLetter ReplayDeadLetter PurgeDeadLetter |
FilterControlService | 4 | Filter | ListFilters CreateFilter UpdateFilter DeleteFilter |
AIControlService | 11 | AI | ListAIConfigs CreateAIConfig UpdateAIConfig DeleteAIConfig |
SystemStatusControlService | 3 | SystemStatus | GetSystemStats GetLogPolicy SetLogPolicy |
AuditControlService | 1 | Audit | QueryAuditEvents |
LogControlService | 4 | Log | Log QueryLogs LogStats PurgeLogs |
BackupControlService | 2 | Backup | BackupExport BackupImport |
AccountControlService | 8 | Account | RegisterAccount LoginAccount ValidateToken IssueWebToken |
AdminControlService | 11 | Admin | GetOverview ListUsers SetUserStatus Broadcast |
BotTokenControlService | 5 | BotToken | ListBotTokens CreateBotToken DeleteBotToken GetBotDefaults |
TaskControlService | 5 | Task | CreateTask GetTask ListTasks CancelTask |
Log vocabulary
63 distinct stage tags, 175 occurrences, parsed out of the engine's source. This is the vocabulary an operator greps for when something did not arrive — which is why the flows above quote these tags instead of inventing their own names.
Links that do not exist
The topology is defined as much by what is missing. Each of the following is a search over the checkout that came back empty on this build — a negative claim the site re-checks rather than asserts once.
Mobile speaks no gRPC
The mobile client never dials the engine directly: every call goes through the REST control plane.
searched: mobile/lib
No secrets in the browser bundle
Neither the API key, the master key nor the AI provider address is shipped to the browser.
searched: web/frontend/src
No label chosen by a language branch
A localised label is never picked by a hardcoded ternary — the catalogue lookup does it, and it falls back to English. (The language switcher itself still shows the target language natively, which is the point of a switcher.)
searched: core/infrastructure/telegram , core/application , atfcli
Two separate databases
The logger never opens the engine database — the log store is its own file.
searched: logger , rust/logger/src
The engine opens no HTTP port
The engine is reachable only over gRPC; there is no HTTP server inside the core.
searched: core
The links here are the runtime view. For the code-level view — which module imports which, and where the coupling is concentrated — open the dependency atlas, or go back to the atlas overview.