Protobuf DataStore

SkillDev tools

App settings protobuf schema, new preferences, DataStore persistence, and Kotlin mapping.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Protobuf DataStore skill

What this skill tells your AI

The instructions your AI receives, as published by po4yka/ripdpi in .agents/skills/protobuf-datastore/SKILL.md and read by ahel’s review.

Overview

RIPDPI persists all app settings as a single Protobuf message (AppSettings) stored via Jetpack DataStore. The schema is the source of truth for all configurable values.

Key Files

FilePurpose
core/data/model/src/main/proto/app_settings.protoSchema definition for persisted app settings
core/data/settings/src/main/kotlin/com/poyka/ripdpi/data/AppSettingsSerializer.ktSerializer with default values
core/data/settings/src/main/kotlin/com/poyka/ripdpi/data/AppDataStore.ktDataStore extension property
core/engine/.../core/RipDpiProxyPreferences.ktProto -> native preferences conversion
app/.../activities/SettingsViewModel.ktProto -> UI state conversion
app/.../activities/ConfigViewModel.ktConfig draft validation and persistence

Adding a New Setting

  1. Add field to proto schema (app_settings.proto):

    bool your_new_setting = <next_field_number>;
    

    Use next available field number. Never reuse or renumber existing fields.

  2. Set default in serializer (AppSettingsSerializer.kt):

    override val defaultValue: AppSettings = AppSettings.newBuilder()
        // ... existing defaults ...
        .setYourNewSetting(false)
        .build()
    
  3. Expose in UI state (SettingsUiState or ConfigDraft):

    data class SettingsUiState(
        // ... existing fields ...
        val yourNewSetting: Boolean = false,
    )
    
  4. Map in conversion function (AppSettings.toUiState() or toConfigDraft()):

    fun AppSettings.toUiState() = SettingsUiState(
        // ... existing mappings ...
        yourNewSetting = this.yourNewSetting,
    )
    
  5. Persist changes via DataStore:

    context.settingsStore.updateData { current ->
        current.toBuilder().setYourNewSetting(value).build()
    }
    

Reading Settings

// Reactive (preferred): Flow<AppSettings>
context.settingsStore.data.collect { settings ->
    val value = settings.yourSetting
}

// In ViewModel: combine with other flows
val uiState = combine(application.settingsStore.data, otherFlow) { settings, other ->
    UiState(setting = settings.yourSetting, /* ... */)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), UiState())

Proto Field Groups

GroupFieldsNotes
Generalapp_theme, ripdpi_mode, dns_ip, ipv6_enableMode is "vpn" or "proxy"
Command modeenable_cmd_settings, cmd_argsRaw CLI args for command-line mode of the native RIPDPI proxy
Proxyproxy_ip, proxy_port, max_connections, buffer_sizeDefaults: 127.0.0.1:1080
Desyncdesync_method, split_position, fake_ttl, fake_sni, etc.Method: none/split/disorder/fake/oob/disoob
Protocolsdesync_http, desync_https, desync_udpBool flags for which protocols to desync
Hostshosts_mode, hosts_blacklist, hosts_whitelistMode: disable/blacklist/whitelist
TLStlsrec_enabled, tlsrec_position, tlsrec_at_sniTLS record splitting
Apponboarding_complete, biometric_enabled, backup_pinApp lifecycle settings

Preferences Conversion (Proto -> Native)

RipDpiProxyPreferences.fromSettingsStore(context) reads the proto and creates either:

  • RipDpiProxyCmdPreferences(args) if enable_cmd_settings is true
  • RipDpiProxyUIPreferences(...) mapping all 27 proxy parameters

Diagnostics Interaction

  • enable_cmd_settings = true is a hard stop for automatic probing/audit. Those workflows require UI-config JSON so they can launch isolated strategy trials.
  • If a change touches diagnostics availability or recommendation flow, also inspect core:diagnostics and the diagnostics UI layer. The proto flag alone is not the whole behavior.

Common Mistakes

MistakeFix
Reusing proto field numbersAlways use next available number; old numbers are reserved forever
Forgetting default in serializerEvery field needs an explicit default in AppSettingsSerializer.defaultValue
Reading DataStore on main threadAlways use Flow.collect or updateData (both suspend)
Not updating toUiState() mappingNew fields must be mapped in all conversion functions
String enums without validationProto stores strings ("vpn", "proxy") -- validate on read

Signals

GitHub stars
69
Forks
4
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
protobuf-datastore
Source
github.com/po4yka/ripdpi