# License & Activation

:::note[In one sentence]

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

In 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](/docs/v2.0/concepts/attested-bootstrap/) for the handshake and [Prerequisites](/docs/v2.0/getting-started/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](https://octetproof.com/terms/mobile/). 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. The SDK does not contact the backend again until the lease needs to refresh.

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

A key authorises your app. It does not meter it: one key covers every device you install on.

License keys renew automatically. The SDK reports a count of active devices and nothing else: no identity, no coordinates. Verifying happens in your own code, offline. See [pricing](https://octetproof.com/pricing). You should not need to replace a working key.

Install on as many devices as you like. There is no per-license device cap.

## License states

Octet maintains active licenses, so a key in normal use stays in `ACTIVE`. The `GRACE_PERIOD` and `EXPIRED` states below handle prolonged offline periods and revocation -- a maintained key is not turned off for getting old.

```mermaid
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
    EXPIRED --> [*]
    INVALID --> [*]
```

Read the state at any time:

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

```kotlin
// 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.

## Failure modes

`Octet.start(...)` throws a typed `LicenseError`. The complete taxonomy is in [License Types](/docs/v2.0/api-reference/license-types/). The common cases:

- `MalformedKey`. The key isn't a valid signed token. Fix: re-copy the key.
- `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, privacy-preserving 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 at most
once a day.

It is on by default. Disable it with `telemetryEnabled = false` on
[`OctetConfig`](/docs/v2.0/api-reference/octet-start/). Disabling deletes any buffered file.

## 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 are inert in 1.2, because the backend gates no version yet. `upgradeRequired` is not thrown, and `upgradeRecommended` stays `false`. See [License Types](/docs/v2.0/api-reference/license-types/) for the field and case shapes.

## Where to go next

- [Prerequisites](/docs/v2.0/getting-started/prerequisites/) for how to register your app.
- [License Types API Reference](/docs/v2.0/api-reference/license-types/) for the exact types of `LicenseStatus` and `LicenseError`.
- [Troubleshooting FAQ](/docs/v2.0/troubleshooting/faq/) for what to do when a key fails.
