# How Telegram Desktop Implements Privacy Settings Using GlobalPrivacy and UserPrivacy Classes

> Explore how Telegram Desktop uses GlobalPrivacy and UserPrivacy classes to manage account wide flags and per feature rules for seamless UI synchronization with server state.

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

---

**Telegram Desktop implements privacy settings through two dedicated API-wrapper classes—`Api::GlobalPrivacy` for account-wide flags and `Api::UserPrivacy` for per-feature rules—both exposing reactive streams via the RPL library to keep the UI synchronized with server state.**

The `telegramdesktop/tdesktop` repository separates privacy concerns into distinct C++ classes that abstract the MTProto API complexity while providing reactive interfaces for the Qt-based UI layer. This architecture ensures that settings like archive-and-mute behavior or last-seen visibility remain consistent across the application without manual polling.

## Architecture Overview

Telegram Desktop organizes privacy management into two complementary scopes:

- **`Api::GlobalPrivacy`** manages account-wide configurations that apply universally, such as auto-archiving new chats, hiding read receipts, premium-only messaging requirements, and disallowed gift types.
- **`Api::UserPrivacy`** handles granular, per-key privacy rules including phone number visibility, last seen status, call permissions, and story viewership.

Both classes maintain references to `Main::Session` and use `MTP::Sender` (via `ApiWrap`) to communicate with Telegram's servers. They expose state through **Reactive Programming Library (RPL)** streams, allowing UI components to subscribe to changes rather than querying state manually.

## GlobalPrivacy Implementation

Located in [`Telegram/SourceFiles/api/api_global_privacy.h`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/api/api_global_privacy.h) and `.cpp`, the `GlobalPrivacy` class encapsulates the `account.getGlobalPrivacySettings` and `account.setGlobalPrivacySettings` MTProto methods.

### Reactive State Management

The class stores mutable state in `rpl::variable<T>` members, providing both instantaneous access and subscription capabilities:

```cpp
rpl::variable<bool> _archiveAndMute = false;
rpl::variable<UnarchiveOnNewMessage> _unarchiveOnNewMessage = UnarchiveOnNewMessage::None;
rpl::variable<bool> _hideReadTime = false;
rpl::variable<DisallowedGiftTypes> _disallowedGiftTypes;

```

Public getters return `rpl::producer<T>` streams that emit the current value upon subscription and subsequent updates:

```cpp
[[nodiscard]] bool archiveAndMuteCurrent() const;
[[nodiscard]] rpl::producer<bool> archiveAndMute() const;

```

### Loading and Updating Global Settings

The `reload()` method dispatches `account.getGlobalPrivacySettings` and invokes `apply(const MTPGlobalPrivacySettings &settings)` on success, which extracts flags and updates the reactive variables.

For modifications, methods like `updateArchiveAndMute(bool value)` forward to a private `update()` function that:

1. Constructs an `MTP_flags` bitmap mirroring the server's `globalPrivacySettings` structure
2. Cancels any pending request via stored `mtpRequestId`
3. Sends `account.setGlobalPrivacySettings` with the combined flag set
4. Updates local `rpl::variable` members optimistically
5. Handles rollback on errors such as `PREMIUM_ACCOUNT_REQUIRED`

## UserPrivacy Implementation

The `UserPrivacy` class in [`Telegram/SourceFiles/api/api_user_privacy.h`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/api/api_user_privacy.h) manages the `account.getPrivacy` and `account.setPrivacy` methods for specific privacy keys.

### Rule Structure and Conversion

Privacy rules use a structured `Rule` type containing an `Option` enum (Everyone, Contacts, CloseFriends, Nobody) and two `Exceptions` lists:

```cpp
struct Rule {
    Option option = Option::Everyone;
    Exceptions always;
    Exceptions never;
    bool ignoreAlways = false;
    bool ignoreNever = false;
};

struct Exceptions {
    std::vector<not_null<PeerData*>> peers;
    bool premiums = false;
    bool miniapps = false;
};

```

The class uses free functions `RulesToTL(const Rule&)` and `TLToRules()` to translate between the internal C++ representation and the `MTPVector<MTPPrivacyRule>` expected by the MTProto API.

### Saving and Applying Privacy Rules

The `save(Key key, const Rule &rule)` method:

1. Converts the key to its TL equivalent using `KeyToTL(key)`
2. Translates the rule via `RulesToTL(rule)`
3. Dispatches `account.setPrivacy` and stores the request ID in `_privacySaveRequests`
4. On success, calls `apply()` to update local caches

The `apply(mtpTypeId type, const MTPVector<MTPPrivacyRule> &rules, bool allLoaded)` function parses server responses, stores the resulting `Rule` in `_privacyValues`, and pushes updates to `_privacyChanges` event streams. If data is incomplete, `reload(Key key)` fetches the complete rule set.

## Reactive Programming Integration

Both classes leverage RPL to drive UI updates without callback hell. The UI layer subscribes to specific privacy streams:

```cpp
// Global privacy subscription
globalPrivacy->archiveAndMute()
    | rpl::start_with_next([](bool enabled) {
        // Update toggle switch state
    }, lifetime);

// User privacy subscription
userPrivacy->value(Api::UserPrivacy::Key::LastSeen)
    | rpl::start_with_next([](const Api::UserPrivacy::Rule &rule) {
        // Update visibility label
    }, lifetime);

```

These streams automatically emit cached values immediately, then emit again whenever `apply()` or update methods modify the underlying state.

## Practical Usage Examples

Accessing and modifying global privacy settings:

```cpp
auto *apiWrap = session->api();
auto global = std::make_unique<Api::GlobalPrivacy>(apiWrap);

// Load current state
global->reload([]{
    qInfo() << "Global privacy configuration loaded";
});

// Toggle archive-and-mute
global->updateArchiveAndMute(!global->archiveAndMuteCurrent());

```

Managing per-key privacy rules:

```cpp
auto userPrivacy = std::make_unique<Api::UserPrivacy>(apiWrap);

// Subscribe to Last Seen changes
userPrivacy->value(Api::UserPrivacy::Key::LastSeen)
    | rpl::start_with_next([](const auto &rule) {
        qInfo() << "Visibility setting:" << static_cast<int>(rule.option);
    }, lifetime);

// Create rule: visible to contacts only, hide from premium users
Api::UserPrivacy::Rule newRule;
newRule.option = Api::UserPrivacy::Option::Contacts;
newRule.never.premiums = true;

// Persist changes
userPrivacy->save(Api::UserPrivacy::Key::LastSeen, newRule);

```

## Summary

- **`Api::GlobalPrivacy`** encapsulates account-wide settings via `account.getGlobalPrivacySettings` and `account.setGlobalPrivacySettings`, storing state in `rpl::variable` members.
- **`Api::UserPrivacy`** manages granular rules for specific keys (PhoneNumber, LastSeen, Calls) using the `account.setPrivacy` and `account.getPrivacy` MTProto methods.
- Both classes support request cancellation through stored `mtpRequestId` values and handle optimistic UI updates with server-error rollback.
- **RPL reactive streams** (`rpl::producer`) provide the primary interface for UI components, ensuring automatic synchronization when server responses modify local state.
- Rule conversion occurs through `RulesToTL()` and `TLToRules()` helper functions that map between C++ structs and MTProto vectors.

## Frequently Asked Questions

### How does Telegram Desktop handle privacy settings updates when the user is offline?

The implementation stores request IDs in maps like `_privacySaveRequests` and `_privacyRequestIds`, allowing cancellation of pending MTProto calls. When connectivity resumes, the reactive streams automatically emit the server-confirmed state upon receipt of the response, while local changes apply optimistically to maintain UI responsiveness.

### What is the difference between `GlobalPrivacy` and `UserPrivacy` in the tdesktop codebase?

`GlobalPrivacy` manages boolean flags and enumerations that apply account-wide (archive behavior, read receipt hiding, premium requirements), while `UserPrivacy` handles complex rule structures with exceptions for specific peers or user categories. The former uses `MTPGlobalPrivacySettings`, whereas the latter processes `MTPVector<MTPPrivacyRule>` for individual privacy keys.

### How are privacy exceptions (specific users) handled in the API wrapper?

The `UserPrivacy::Exceptions` struct contains a vector of `not_null<PeerData*>` pointers representing specific users or chats, plus boolean flags for premium users and mini-apps. These convert to `MTPPrivacyValueAllowUsers`, `MTPPrivacyValueDisallowUsers`, `MTPPrivacyValueAllowCloseFriends`, and similar TL constructors via the `RulesToTL()` function before transmission to the server.

### Where does the UI connect to these privacy classes in the source code?

The settings layer in [`Telegram/SourceFiles/settings/sections/settings_privacy_security.h`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/settings/sections/settings_privacy_security.h) instantiates these classes, while [`Telegram/SourceFiles/boxes/edit_privacy_box.h`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/boxes/edit_privacy_box.h) provides the dialog interface for modifying individual `UserPrivacy::Key` rules. Both UI components subscribe to the reactive producers exposed by `GlobalPrivacy` and `UserPrivacy` to update switches and labels in real-time.