Skip to main content

Licensed VPN features

Fifteen VPN features are gated by your SDK license. A setter for a feature you are not licensed for returns a refusal and persists nothing, so the value can never reach the tunnel. The tunnel checks again when it starts, and falls back to the licensed default for anything it is not licensed for.

Check what your license grants with KapeSdkHandle.licenseState — use it to hide UI rather than letting a control fail when tapped. See Licensed features for why that is a flow rather than a synchronous read.

The features​

FeatureGated entry pointWithout the license
VpnProtocolLightwaysetSelectedProtocol(Lightway)Refuses; Automatic skips Lightway endpoints
VpnProtocolWireGuardsetSelectedProtocol(Wireguard)Refuses; Automatic skips WireGuard endpoints
VpnProtocolOpenVpnsetSelectedProtocol(OpenVPN)Refuses; Automatic skips OpenVPN endpoints
VpnProtocolObfuscation—Obfuscated endpoints are dropped, no refusal. Separate from the user-facing amneziaEnabled toggle, which only narrows further
VpnLightwayBitripplesetBitrippleEnabledRefuses; tunnel uses false
VpnLightwaySnisetLightwaySNIRefuses; tunnel uses the SDK default SNI
VpnHttpsProxysetProxyConfigRefuses; tunnel uses no proxy
VpnKillSwitchAdvancedsetKillSwitchMode(Advanced)Refuses; tunnel uses Standard
VpnSplitTunnelsetSplitTunnelAppMode, addApplicationToCurrentSplitTunnelMode, removeApplicationFromCurrentSplitTunnelMode, resetCurrentSplitTunnelModeRefuses; every app is tunnelled
VpnCustomDnssetCustomDnsServersRefuses; tunnel uses the per-protocol default
VpnAdvancedProtectionsetAdvancedProtectionCategoriesRefuses; tunnel applies no filtering
VpnLightwayHeartbeatsetHeartbeatEnabledRefuses; tunnel uses false
VpnLightwayExpresslanesetExpresslaneEnabledRefuses; tunnel uses false
VpnSessionPauseResumepause(duration)Refuses. resume() is never gated, so a pause can always be released
VpnAutoConnectRulessetConnectOnDemand, setOnDemandRules, addOnDemandRule, removeOnDemandRule, resetOnDemandRules, setConnectOnStartupRefuses; no automatic connect or disconnect. Always-on VPN is a separate system setting the user controls

setSelectedProtocol(Automatic) is always allowed — it is narrowed to the protocols you are licensed for when the tunnel builds its endpoint list.

Handling a refusal​

The gated setters are suspend — they wait for the license verdict — and return a sealed result. Most return KapeVpnSettingResult, which names the feature:

when (val result = vpn.setSplitTunnelAppMode(KapeSplitTunnelAppMode.Allow(packages))) {
is KapeVpnSettingResult.Success -> Unit
is KapeVpnSettingResult.NotLicensed -> showError("Not licensed: ${result.feature.name}")
}

The settings that already had their own result type gained a NotLicensed case instead, so their validation errors keep their existing shape:

SetterResult
setLightwaySNIKapeLightwaySNIResult.NotLicensed
setCustomDnsServersKapeCustomDnsResult.NotLicensed
the three split-tunnel package editsKapeSplitTunnelEditResult.NotLicensed
pause(duration)PauseResult.NotLicensed

Nothing is persisted when a setter refuses, so there is no state to roll back.

transport, cipher, deferConnectTimeout and amneziaEnabled are still assignable vars: they carry no license key, so they can never refuse. amneziaEnabled reports your raw preference even on an SDK without VpnProtocolObfuscation, where obfuscated endpoints are dropped anyway — see WireGuard Obfuscation (Amnezia).

Reads report what the tunnel will do​

The matching flows — selectedProtocolFlow, killSwitchModeFlow, bitrippleEnabledFlow, heartbeatEnabledFlow, expresslaneEnabledFlow, lightwaySNIFlow, advancedProtectionCategoriesFlow, customDnsServersFlow, splitTunnelAppModeFlow, proxyConfigsFlow, connectOnDemandFlow, onDemandRulesFlow, connectOnStartupFlow — never fail. On an unlicensed SDK they emit the licensed default, so your UI shows what the tunnel will actually apply rather than a value it will ignore. selectedProtocolFlow reports Automatic when the persisted protocol is no longer licensed, and killSwitchModeFlow reports Standard in place of Advanced.

They emit the licensed default while the verdict is still resolving too, and re-emit the real value once it lands — so a gate is never open before the license has been checked.

This matters after a license downgrade or expiry: a value persisted by a build with a broader license is never re-set through a setter, so the flows and the tunnel are what correct it.

Obfuscation is different​

Obfuscation filters rather than refuses. Without VpnProtocolObfuscation, obfuscated endpoints are dropped from the connection's pecking order and the connection proceeds on the remaining ones — so Automatic still connects. Nothing is refused, and each dropped endpoint is logged.

If a location has only obfuscated endpoints, the connection fails. The tunnel reports KapeVpnTunnelError.EndpointsExcludedByLicense on KapeVpnManager.lastTunnelError — distinct from a location that serves no endpoints at all, so you can name the license as the cause. An Anonymous Dedicated IP connection reports the same error when VpnProtocolLightway is missing, since it is Lightway-only.

Expiry and revocation​

The verdict comes from the Low Level SDK's licenseStatus(), which verifies the signature and reads valid_until. Any problem grants nothing — an expired or revoked license refuses every gated feature, exactly as an absent one does. KapeSdkHandle.licenseProblems (also on KapeLicenseState.Resolved.problems) says which:

for (problem in handle.awaitLicenseState().problems) {
when (problem) {
is KapeLicenseProblem.Expired -> renewPrompt(problem.validUntil)
is KapeLicenseProblem.Revoked -> contactSupport(problem.licensedTo)
else -> log(problem.toString())
}
}

A license the Low Level SDK rejects outright never reaches licenseStatus() — building its manager fails first, and every call that needs it fails with it.

Scope​

These checks stop a feature being used by mistake, and they are backed by a signature the Low Level SDK verified. They are not a tamper-proof control inside a process an attacker controls; the Low Level SDK's own per-request enforcement remains the authority.

Editing the license is self-defeating: a license that fails verification makes every call that needs the manager fail — sign-in, location discovery and connecting included.