Docs/Mobile SDK/Concepts

License & Activation

In short

The SDK gets its license by attesting your app on the first call to Octet.start(...), then works from a local lease. The lease lifecycle below (refresh, offline grace, states, revocation) is unchanged from 1.x.

Getting a license

Since 2.0, the SDK obtains its license by attesting your app at first launch, not by carrying a key. Register your app once against your Octet account, then call Octet.start(...). See Attested bootstrap for the handshake and Prerequisites for the registration steps. For local development, CI, and simulators, use a sandbox bypass token.

Use of the SDK is governed by the Mobile SDK terms. The current terms live at /terms/mobile/. A dated snapshot lives at /terms/mobile/vMM.DD.YYYY/.

The first call bootstraps the license over the network. Subsequent launches read from a local lease and do not bootstrap again until the lease needs to refresh. After a successful activation the SDK keeps working offline for up to 14 days (the offline grace).

As of 1.2, when a lease response returns a refreshed license token, the SDK persists it. The device then carries a fresh token across restarts throughout the offline-grace window, rather than falling back to the token it first activated with.

Validity

Octet bills per active device: the SDK sends one usage record per device per UTC day, with no identity and no coordinates (see Metrics). Aggregate usage counters are on by default and are described under Usage telemetry. Verifying happens in your own code, offline. See pricing.

Licenses renew automatically, so you do not need to replace a working license.

By default a license has no device cap, so you can install on as many devices as you like. Octet can set a device cap on a license. When the cap is reached, a new device fails to activate with deviceLimitReached. Devices that already activated keep working and keep refreshing their lease.

License states

Octet maintains active licenses, so a license in normal use stays in ACTIVE. The GRACE_PERIOD, EXPIRED and REVOKED states below handle prolonged offline periods and revocation. Age alone does not expire a maintained license.

stateDiagram-v2
    [*] --> NOT_ACTIVATED: license verified locally
    NOT_ACTIVATED --> ACTIVE: activation succeeds
    ACTIVE --> RENEWAL_RECOMMENDED: < 30 days to expiry
    ACTIVE --> GRACE_PERIOD: cached past exp, offline
    RENEWAL_RECOMMENDED --> GRACE_PERIOD: same trigger
    GRACE_PERIOD --> EXPIRED: past grace period
    NOT_ACTIVATED --> INVALID: signature / server reject
    ACTIVE --> INVALID: server reject
    ACTIVE --> REVOKED: license withdrawn
    ACTIVE --> EXPIRED: license expired
    EXPIRED --> [*]
    REVOKED --> [*]
    INVALID --> [*]

Read the state at any time:

// iOS
if let status = sdk.licenseStatus {
    print("state: \(status.state), days left: \(status.daysUntilHardStop ?? -1)")
}
// Android
sdk.licenseStatus?.let { status ->
    println("state: ${status.state}, days left: ${status.daysUntilHardStop ?: -1}")
}

LicenseStatus.state drives in-app UI only. It never drives the cryptographic gate. Forcing state = ACTIVE in your own code accomplishes nothing.

If the license is withdrawn or expires while the app runs, the session stops: predicates return INDETERMINATE with reason LICENSE_INACTIVE and make no proof, and the state reads REVOKED or EXPIRED. The next Octet.start throws a LicenseError.

Failure modes

Octet.start(...) throws a typed LicenseError. The complete taxonomy is in License Types. The common cases:

  • BootstrapFailed(reason:). Attested bootstrap failed. reason names the cause. Fix: see BootstrapReason.
  • MalformedKey. The license token the SDK holds did not verify. Fix: confirm the SDK is on a supported version, and contact support if it persists.
  • NoActivation. First launch, and the device is offline. Fix: retry when network returns.
  • Expired. Cached activation past the offline grace, backend unreachable. Fix: reconnect so the SDK can re-activate.
  • ActivationWindowClosed. Not returned under the current maintained-license model. Retained for compatibility. Fix: contact support.
  • Revoked. Admin revoke (key leaked, abuse). Fix: contact support.
  • Network(message:). Transient. Fix: retry.
  • ServerRejected(httpStatus:, reason:). Anything else from the backend. Fix: see reason.

Usage telemetry

The SDK reports aggregate usage counters to the license backend, indexed by your license: how many proofs were generated, uploaded, or could not be produced, by coarse level and region type. The counters carry no location data: no coordinates, no region IDs, no proof contents. They are buffered in an encrypted file in the app's private storage and uploaded once a day, plus a best-effort flush when the app enters the background.

Telemetry is on by default. Disable it with telemetryEnabled = false on OctetConfig. Disabling deletes any buffered file.

Separately, the SDK sends one usage record per device per UTC day so Octet can count active devices for billing. That record is always on. See Metrics and usage reporting.

SDK version gating

Added in 1.2. The SDK reports its version and platform on every backend request. The backend can act on that in two ways:

  • A hard gate. Octet.start(...) throws LicenseError.upgradeRequired(minVersion:message:) when the backend rejects an out-of-support version.
  • A soft hint. LicenseStatus.upgradeRecommended and minSupportedVersion flag a version behind the recommended floor without stopping the SDK.

Both paths were inert when they were added in 1.2, because the backend gated no version yet. Octet turns gating on server-side, without an SDK release, so handle upgradeRequired and read upgradeRecommended now. See License Types for the field and case shapes.

Where to go next

TermsPrivacy© 2026 Understone, Inc. d/b/a Octet