TgWitness / Documentation

Reference / Security & project

Architecture

Components of the collector, panel, journal, workers and witness, and how data moves between them.

One Python process hosts the FastAPI panel and any number of isolated workspaces, each with a Telethon user-account client. SQLite holds an append-only signed event journal and mutable operational settings. An encrypted vault holds API credentials, the StringSession, and signing key. The frontend is plain bundled HTML/CSS/JavaScript: no Node build, CDNs, or runtime frontend downloads are needed.

Module Responsibility
collector.py Authorization, dialog selection, updates, snapshots, media queue, connection monitoring
interactions.py / telegram_client.py Explicit panel actions, signed receipts, persistent request IDs and task-local callback/join retry policy
message_content.py Observable message comparison, excluding transport access metadata
workers.py Wait for export file workers before cancellation cleanup
store.py WAL transactions, ordered event chain, query filters, operational settings
security.py Private-file creation, encrypted vault, Ed25519 signing, single-process lock
evidence.py Complete-journal export, derivatives, checkpoints, attachment verification
verify.py Standalone, read-only verifier (stdlib + cryptography): archives, coverage, witness journals and channel posts, profile files
app.py Authenticated API, lifecycle, request validation, static panel
profiles.py Multiple account collectors, shared access controller, per-workspace lifecycle
access.py Scoped grants, permission checks, IP restrictions, session limits and administrator console
proxy_catalog.py Public feed snapshots, transport checks, shared proxy pool and health-based selection
provenance.py Signed acquisition-run identity, code and dependency measurements
network.py Validated per-workspace routes; redacted status and evidence; Telethon transport options
sessions.py Offline read-only conversion of reviewed Telethon SQLite schemas
account_import.py In-memory detection and normalization of supported account files and JSON
tdata.py Bounded modern TData ZIP reader, checksum/decryption validation, no extraction
doctor.py Local readiness checks without collection, login or package installation
download.py Authenticated streaming export download with bounded reads and no overwrites
monitoring.py Validated per-workspace event policy and per-type attachment rules, shared by handlers, status and signed receipts
scope.py Observation scope (chosen chats, Telegram folder, whole account), source types, exclusions, recheck tiers
dossier.py People profiles per chat built from saved records: facts with record references, avatar SHA-256/pHash, mention extraction
witness_api.py Read-only witness tokens and the signed journal head they may fetch
witness.py Independent witness process: own key and journal, head checks, signed channel posts, export
alerts.py Apprise notifications and the panel's notification list
logging_config.py Console logging and rotating server diagnostics for windowless launches
static/ Responsive interface, local access gate, filters, details, exports, account setup
demo.py Clearly labeled synthetic journal, isolated from real collection

Observation handlers are registered before connecting. Live collection is gated by the active client identity, explicit running state, enabled sources or the observation scope (folder or whole-account modes add a new chat as a source on its first update), and event policy. Rejected and superseded clients cannot contribute live updates. Raw reactions and membership updates preserve visibility limits. Incoming message content and its durable media job are committed together. Jobs preserve the original TL descriptor and bind its hash to the signed message observation. Sender/editor/deleter semantics are separated.

Each workspace accepts one account and has its own directory and signing identity. The main workspace remains at the root for compatibility; additional workspaces live in profiles/<id>/. One bearer token controls the whole installation; workspaces are not separate authorization tenants. Process locks protect directories; database locks serialize event insertion. Each append commits synchronously to SQLite WAL with FULL synchronous mode. Event update/delete triggers prevent ordinary application edits; filesystem administrators remain inside the trust boundary.

API authorization is a bearer token. Login operations are serialized and time-bounded. Account uploads use a dedicated 12 MiB encoded-body limit and an 8 MiB decoded-file limit; normal API requests retain the 16 KiB limit. Conversion runs in a worker thread, keeps uploads in memory, and returns normalized input to the authenticated browser. QR login has a per-workspace candidate, short-lived identifier, waiter task, and explicit cancellation. Only accepted authorization becomes the active client. Telegram FloodWait is returned with a Retry-After value; credentials and raw RPC error messages are not exposed in API errors. Exports run outside the async event loop and are serialized per workspace. They stream a stable read snapshot, and can coexist with continuing WAL writes. Workspace collectors share the process/event loop.

Current history snapshots and live updates can overlap. Both are separate observations, labeled by source; duplicates are not silently collapsed. No inference from missing IDs, fabricated deleted text, forced member enumeration, or automatic group joining is implemented.

For the operator-facing explanation, see how collection works and accounts and session formats.

Network configuration lives in the encrypted vault. Applying a route closes old clients and cancels unfinished login; later connections use that route with no direct fallback. A new external-key installation derives each workspace's Fernet key with HKDF-SHA256 from a master key file and a stored random salt. This protects secrets separately from the data directory, not plaintext journal/media contents.

Local demo entry uses a separately generated process capability, never the configured panel token. Issuance and acceptance require explicit demo mode, a synthetic root without additional workspaces or an authorized session, loopback peer/Host, no proxy forwarding headers, and no cross-origin browser request. Live APIs retain bearer authentication and reject collection changes in demo mode.