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.jsonon Linux,~/Library/Application Support/leaf-api/...on macOS,%APPDATA%\leaf-api\...on Windows. - Android:
<app files dir>/leaf-api/preferences/default.json(the SDK setsLEAF_DATA_PATHtogetFilesDir()inLeafVPNService.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) orreloadLeaf()onServiceManagement(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.