Skip to content

Preferences Reference

LeafPreferences is the unified persisted configuration object exposed by every SDK. It controls how the Leaf core routes traffic, which DNS mode to use, which GeoIP/Geosite buckets to bypass or reject, and where the runtime HTTP API listens.

The reference implementation is the Preferences struct provided by the SDKs. All SDKs mirror the same fields.


File Location

LeafPreferences is persisted as JSON at <LEAF_DATA_PATH>/leaf-api/preferences/default.json.

  • Desktop (default): $XDG_CONFIG_HOME/leaf-api/preferences/default.json on Linux, ~/Library/Application Support/leaf-api/... on macOS, %APPDATA%\leaf-api\... on Windows.
  • Android: <app files dir>/leaf-api/preferences/default.json (the SDK sets LEAF_DATA_PATH to getFilesDir() in LeafVPNService.onCreate).

Fields

Field Type Default Description
client_id String? null Unique client identifier used by updateSubscription(clientId).
last_update_time i64? null Epoch seconds when the subscription was last refreshed.
traffic u64 0 Total allowance reported by the panel, in bytes. 0 = unlimited.
used_traffic u64 0 Traffic used so far, in bytes.
expire_time String? null Expiration date in yyyy-MM-dd HH:mm:ss.
enable_ipv6 bool true Enable IPv6 inside the TUN.
prefer_ipv6 bool false Resolve AAAA before A when both are available.
memory_logger bool false Keep logs in RAM and expose them via the runtime HTTP API instead of writing to disk.
log_level int 2 (Info) 0=Trace, 1=Debug, 2=Info, 3=Warn, 4=Error.
api_port u16 10001 (desktop), 9090 (Android sample) Port for the local Runtime HTTP API.
auto_reload bool true Watch the rendered config and reload on change.
user_agent String? null Custom UA for subscription fetches. Leave empty to use the default.
bypass_lan bool true Do not route LAN traffic through the VPN.
bypass_lan_in_core bool false If true, LAN bypass is done by the Leaf router instead of the system route table.
fake_ip bool false Answer DNS from a local fake-IP pool (198.18.0.0/16) to prevent leaks.
force_resolve_domain bool false Resolve hostnames to IPs before applying routing rules.
bypass_geoip_list Vec<String>? [] GeoIP region codes (e.g. cn, ir, ru) to bypass.
bypass_geosite_list Vec<String>? [] Geosite categories (e.g. category-ads) to bypass.
reject_geoip_list Vec<String>? [] GeoIP region codes whose traffic should be dropped.
reject_geosite_list Vec<String>? [] Geosite categories whose traffic should be dropped.
internal_dns_server bool false Use the core's built-in DNS server.

Internal (read-only from SDKs; updated by the backend wrapper):

Field Description
latest_assets_version Asset bundle version installed (x.y.z).
update_interval Subscription refresh interval in seconds (default 3600).
last_successful_tls / last_successful_fragment / last_successful_endpoint_type Last known-good flags used to auto-select next time.
subscription_domain / subscription_external_domain Primary and fallback domains used to fetch the subscription.
enable_speedtest / enable_try_all Persisted copies of the last updateSubscription flags.

Reading and Writing

val sm = ServiceManagement.getInstance()

// Read
val prefs: LeafPreferences = sm.preferences

// Write (use UpdateLeafPreferences DTO)
val update = UpdateLeafPreferences().apply {
    enableIpv6 = true
    logLevel = LogLevel.INFO
    apiPort = 10001
    bypassLan = true
    fakeIp = true
    bypassGeoipList = listOf("cn", "ir", "ru")
}
sm.setPreferences(update)
let mut prefs = leaf_sdk_desktop::get_preferences()?;
prefs.fake_ip = true;
prefs.bypass_geoip_list = Some(vec!["cn".into(), "ir".into()]);

leaf_sdk_desktop::set_preferences(
    prefs.enable_ipv6, prefs.prefer_ipv6, prefs.memory_logger,
    prefs.log_level, prefs.api_port, prefs.auto_reload,
    prefs.user_agent.unwrap_or_default(),
    prefs.bypass_lan, prefs.bypass_lan_in_core,
    prefs.fake_ip, prefs.force_resolve_domain,
    prefs.bypass_geoip_list, prefs.bypass_geosite_list,
    prefs.reject_geoip_list, prefs.reject_geosite_list,
    prefs.internal_dns_server,
)?;
LeafPreferences* p = get_preferences();
set_preferences(
    p->enable_ipv6, p->prefer_ipv6, p->memory_logger,
    p->log_level, p->api_port, p->auto_reload,
    p->user_agent,
    p->bypass_lan, p->bypass_lan_in_core,
    /*fake_ip=*/true, p->force_resolve_domain,
    "cn,ir,ru",        // bypass_geoip_list as comma-separated
    NULL, NULL, NULL,
    p->internal_dns_server
);
free_preferences(p);
LeafPreferences prefs = LeafWrapper.getInstance().getPreferences();

UpdateLeafPreferences update = new UpdateLeafPreferences();
update.setEnableIpv6(prefs.isEnableIpv6());
update.setApiPort(prefs.getApiPort());
update.setFakeIp(true);
update.setBypassGeoipList(List.of("cn", "ir", "ru"));
// ...copy remaining fields...
LeafWrapper.getInstance().setLeafPreferences(update);

When to Reload

Changes to preferences are not picked up automatically by the running core. After calling setPreferences:

  • If the core is stopped, nothing more to do — it will use the new values on the next start.
  • If the core is running, call reloadLeaf (Rust / Java / FFI) or reloadLeaf() on ServiceManagement (Android). The SDK re-renders the config and hot-swaps it without dropping the TUN device.

Traffic Accounting

traffic / used_traffic / expire_time are read-only mirrors written by the wrapper every time updateSubscription succeeds. They reflect the panel's view of the client. If you need live per-connection stats use the Runtime HTTP API.