# Architecture of the Passcode Lock System in Telegram Desktop's MainWindow

> Explore the passcode lock system architecture in Telegram Desktop's MainWindow. Understand its UI subsystem, state management, cryptographic verification and biometric integration.

- Repository: [Telegram Desktop/tdesktop](https://github.com/telegramdesktop/tdesktop)
- Tags: architecture
- Published: 2026-04-05

---

**The passcode lock system in Telegram Desktop is a centralized UI subsystem coordinated by MainWindow, involving Application-level state management, cryptographic verification via Domain, and optional biometric integration through SystemUnlock.**

The passcode lock architecture in `telegramdesktop/tdesktop` secures the application through a multi-layered design that separates global state management from UI presentation. When the global lock flag is set, **MainWindow** instantiates a **PasscodeLockWidget** that handles user input, cryptographic validation, and platform-specific biometric authentication. This architecture ensures that sensitive chat data remains encrypted and inaccessible until the correct passcode or system-approved biometric is provided.

## Core Components of the Passcode Lock Architecture

The system spans six primary components across the codebase:

- **Application** ([`core/application.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/core/application.cpp)): Maintains the global `_passcodeLock` flag and orchestrates lock/unlock operations across all window instances.
- **Window::Controller** ([`window/window_controller.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/window/window_controller.cpp)): Propagates Application commands to the UI layer via `_widget.setupPasscodeLock()`.
- **MainWindow** ([`mainwindow.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/mainwindow.cpp)): Creates, animates, and destroys the lock widget while coordinating visibility of underlying UI layers (intro, main view).
- **PasscodeLockWidget** ([`window/window_lock_widgets.cpp/.h`](https://github.com/telegramdesktop/tdesktop/blob/main/window/window_lock_widgets.cpp/.h)): The concrete UI implementation that collects input, validates credentials, and manages system-unlock integration.
- **Domain** ([`storage/storage_domain.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/storage/storage_domain.cpp)): Performs cryptographic verification of passcodes against stored encryption keys.
- **SystemUnlock** ([`base/system_unlock.h/.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/base/system_unlock.h/.cpp)): Provides platform-specific biometric prompts including Windows Hello, Touch ID, and Apple Watch.

## Global Lock State Management

The **Application** class maintains the canonical lock state. When the system detects inactivity or an explicit lock request, `lockByPasscode()` sets the internal flag and broadcasts the lock command to every active window.

```cpp
// core/application.cpp
void Application::lockByPasscode() {
    _passcodeLock = true;
    enumerateWindows([&](not_null<Window::Controller*> w) {
        w->setupPasscodeLock();          // triggers UI creation
    });
    if (_mediaView) { _mediaView->close(); }
}

```

Conversely, `unlockPasscode()` clears the flag and destroys all lock widgets. This method is invoked both after successful passcode entry and after biometric authentication.

```cpp
// core/application.cpp
void Application::unlockPasscode() {
    clearPasscodeLock();                 // resets flag
    enumerateWindows([&](not_null<Window::Controller*> w) {
        w->clearPasscodeLock();          // destroys UI
    });
}

```

## Window Coordination Layer

The **Window::Controller** acts as a bridge between the global Application state and the concrete MainWindow UI. It forwards setup requests to its internal widget instance.

```cpp
// window/window_controller.cpp
void Controller::setupPasscodeLock() {
    _widget.setupPasscodeLock();   // creates PasscodeLockWidget
}

```

This delegation pattern allows the Application to remain agnostic of UI implementation details while ensuring every window receives the lock command simultaneously.

## MainWindow UI Instantiation

Within `MainWindow::setupPasscodeLock()`, the lock widget is constructed as a child of the window's body widget. The method handles animated transitions by capturing the current view state before hiding the intro or main content layers.

```cpp
// mainwindow.cpp
void MainWindow::setupPasscodeLock() {
    auto animated = (_main || _intro);
    auto oldContentCache = animated ? grabForSlideAnimation() : QPixmap();
    _passcodeLock.create(bodyWidget(), &controller());   // PasscodeLockWidget
    updateControlsGeometry();
    ui_hideSettingsAndLayer(anim::type::instant);
    if (_main) { _main->hide(); }
    if (_intro) { _intro->hide(); }
    if (animated) {
        _passcodeLock->showAnimated(std::move(oldContentCache));
    } else {
        _passcodeLock->showFinished();
        setInnerFocus();
    }
}

```

When clearing the lock, `MainWindow::clearPasscodeLock()` destroys the widget and restores the previously active view layer (intro, main, or setup-email lock), reversing the animation process.

## Passcode Validation and Cryptography

The **PasscodeLockWidget** handles user input through its `submit()` method. It validates the entry against flood-control limits before delegating cryptographic verification to the **Domain** class.

```cpp
// window/window_lock_widgets.cpp
void PasscodeLockWidget::submit() {
    if (_passcode->text().isEmpty()) { _passcode->showError(); return; }
    if (!passcodeCanTry()) { _error = tr::lng_flood_error(tr::now); ... return; }
    const auto passcode = _passcode->text().toUtf8();
    auto &domain = Core::App().domain();
    const auto correct = domain.started()
        ? domain.local().checkPasscode(passcode)
        : (domain.start(passcode) == Storage::StartResult::Success);
    if (!correct) { cSetPasscodeBadTries(...); error(); return; }
    Core::App().unlockPasscode();   // destroys widget
}

```

The cryptographic check occurs in `Domain::checkPasscode()`, which recomputes the local encryption key from the supplied passcode and salt.

```cpp
// storage/storage_domain.cpp
bool Domain::checkPasscode(const QByteArray &passcode) const {
    Expects(!_passcodeKeySalt.isEmpty());
    const auto checkKey = CreateLocalKey(passcode, _passcodeKeySalt);
    return checkKey->equals(_passcodeKey);
}

```

Bad-try counters (`cSetPasscodeBadTries`) and timestamps persist in the settings system to enforce rate limiting via `passcodeCanTry()`.

## Biometric and System Unlock Integration

On supported platforms, **PasscodeLockWidget** exposes a system-unlock button that triggers platform-native biometric prompts. The `suggestSystemUnlock()` method initiates the flow, while `systemUnlockDone()` handles the callback.

```cpp
// window/window_lock_widgets.cpp
SuggestSystemUnlock(this,
    (::Platform::IsWindows()
        ? tr::lng_passcode_winhello_unlock(tr::now)
        : tr::lng_passcode_touchid_unlock(tr::now)),
    done);

```

A successful biometric validation immediately invokes `Core::App().unlockPasscode()`, bypassing manual passcode entry entirely. Flood errors from the system unlock layer are surfaced in the UI via the same error handling mechanism as manual entry failures.

## Practical Implementation Examples

### Manually Triggering a Lock After Inactivity

```cpp
// Detection logic for idle timeout
if (!Core::App().passcodeLocked() && Core::App().settings().autoLock()) {
    Core::App().maybeLockByPasscode();   // invokes the whole flow
}

```

### Programmatic Unlock After Biometric Success

```cpp
// Called from system-unlock callback
void onBiometricSuccess() {
    Core::App().unlockPasscode();   // clears flag and destroys the widget
}

```

### Extending Passcode Validation

To customize validation logic, extend the **Domain** class and override the check behavior. The widget calls this method consistently:

```cpp
auto &domain = Core::App().domain();
bool ok = domain.local().checkPasscode(userInput);

```

## Summary

- **Global coordination**: The Application class maintains the `_passcodeLock` flag and broadcasts state changes to all windows via `enumerateWindows()`.
- **UI separation**: MainWindow handles widget lifecycle and animations while PasscodeLockWidget manages input collection and error display.
- **Cryptographic isolation**: Actual passcode verification occurs in `Domain::checkPasscode()` within the storage layer, using salted key derivation.
- **Biometric support**: SystemUnlock provides native platform integration (Windows Hello, Touch ID, Apple Watch) that bypasses manual entry on success.
- **State persistence**: Bad-try counters and timestamps enable client-side flood control independent of the cryptographic verification.

## Frequently Asked Questions

### How does MainWindow animate the transition to the lock screen?

MainWindow captures the current view content using `grabForSlideAnimation()` before hiding the `_main` or `_intro` layers. It then passes this cache to `PasscodeLockWidget::showAnimated()`, which performs a smooth slide transition while the underlying content remains hidden but preserved in memory.

### Where is the passcode cryptographically verified?

Verification occurs in `Domain::checkPasscode()` within [`storage/storage_domain.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/storage/storage_domain.cpp). This method reconstructs the encryption key using `CreateLocalKey(passcode, _passcodeKeySalt)` and compares it against the stored `_passcodeKey`, ensuring the UI layer never handles raw cryptographic material directly.

### What triggers the lock across all open windows simultaneously?

`Application::lockByPasscode()` sets the global flag and iterates through all active windows using `enumerateWindows()`, calling `setupPasscodeLock()` on each controller. This ensures consistent locking behavior regardless of how many windows or chats are currently open.

### How does Telegram Desktop integrate with system biometric authentication?

The platform abstraction layer in [`base/system_unlock.h`](https://github.com/telegramdesktop/tdesktop/blob/main/base/system_unlock.h) exposes `SuggestSystemUnlock()`, which launches native prompts (Windows Hello, Touch ID, Apple Watch). On success, the callback invokes `Core::App().unlockPasscode()`, providing a seamless unlock experience that bypasses the traditional passcode input field.