Attested bootstrap
The SDK activates by attesting the app: Apple App Attest on iOS, hardware key attestation on Android. No license key is embedded. Register your app at sdk.octetproof.com/apps (below). A developer dashboard at dashboard.octetproof.com is on the way.
The model
In 2.0 the SDK proves the app's identity at first launch with a platform attestation, so no secret ships in the app:
| Platform | Anchor | Verified against |
|---|---|---|
| iOS | Apple App Attest | Apple's App Attest root |
| Android | Hardware key attestation | Google's hardware-attestation root (TEE / StrongBox) |
| macOS / Windows | Code-signing metadata | The platform signing authority |
The attestation names the app (its bundle identifier and Apple Team ID on iOS, its package name and signing-certificate SHA-256 on Android). Octet reads that identity from the verified attestation, looks it up in a registry of apps you own, and mints a license bound to the device. No secret ships in the app.
Registering your app
You register each app once against your Octet account. Sign up at sdk.octetproof.com/signup, then add your app identities at sdk.octetproof.com/apps (the onboarding email links there). The Add an app identity dialog records the bundle id and Apple Team ID on iOS, or the package name and signing-certificate SHA-256 on Android, with separate Debug (sandbox) and Release (production) certificate fields.
The first valid attestation that arrives for an unclaimed identity binds it to the account that registered it (race-to-attest). A contested identity is refused at bootstrap. A developer dashboard at dashboard.octetproof.com is on the way. /apps is the live surface today.
The bootstrap handshake
Octet.start(...) runs this at first launch. It is a two-call flow so the server controls the anti-replay challenge:
- Challenge. The SDK calls
POST /v1/bootstrap/challengeand receives{ challenge, expires_at }(the challenge is single-use and expires in about 60 seconds). - Attest. The SDK folds the challenge into the attestation (
clientDataHashon iOS, the key-attestation challenge extension on Android) and produces a signed attestation payload. - Bootstrap. The SDK calls
POST /v1/bootstrapwith the channel, the attestation, the key id, the challenge, the app identity, and the device fingerprint. - Verify + mint. The backend confirms the challenge is one it issued and burns it, verifies the attestation against the bundled platform root, looks up the app identity in your account's registry, checks the account is active, and mints a device-bound license plus an activation lease and bearer.
The bootstrap response is the same shape the old /v1/activate returned: a lease, an activation bearer, a license token, and the policy trust roots. Everything after bootstrap is unchanged from 1.x: the lease refresh, offline grace, and revocation all work exactly as before. Bootstrap replaces one entry point, not the lifecycle.
sequenceDiagram
participant App as App + OctetSDK
participant HW as App Attest / Key attestation
participant LB as Octet license backend
App->>LB: POST /v1/bootstrap/challenge
LB-->>App: { challenge, expires_at }
App->>HW: attest, challenge folded in
HW-->>App: signed attestation
App->>LB: POST /v1/bootstrap (attestation, identity, device_fp)
LB->>LB: verify attestation + look up app + check account
LB-->>App: { lease, activation_bearer, license_token, policy_trust_roots }
Devices that cannot attest
An emulator, a CI runner, and a locally built debug app have no hardware attestation to offer. For those, register your Debug certificate under an app at /apps (the Debug field creates a sandbox identity), mint a sandbox bypass token for it there (a 7-, 14-, 30-, or 90-day lifetime, revocable), and pass it as OctetConfig.sandboxBypassToken. When it is set, the SDK bootstraps against that sandbox identity instead of attesting. A store or release build cannot use one: the SDK refuses a sandbox token in a production build, and the server refuses it for a production app.
A real device with no attestation and no sandbox token fails closed at start with a LicenseError. That is by design: without an attestation there is nothing to bind the license to.
When bootstrap fails
Octet.start(...) throws a typed LicenseError. A bootstrap-specific failure carries a BootstrapReason naming the cause: the challenge was not recognized, the challenge expired before use, the attestation did not verify, or the app identity is not registered. See License Types for the full case list.
Where to go next
- Prerequisites: register your app and set up attestation.
- Device Attestation: what App Attest and key attestation prove.
- License & Activation: the lease, refresh, and revocation lifecycle after bootstrap.