Docs/API Reference/Octet.start

Octet.start(...)

The single bring-up call. It obtains the license by attesting the app (Apple App Attest, Android key attestation), brings up the proof pipeline, and returns a fully-usable OctetSdk handle. See Attested bootstrap for the licensing model.

Signature

public enum Octet {
    public static let sdkVersion: String  // "2.0.0"

    public static func start(
        config: OctetConfig,
        startPosition: Position? = nil
    ) async throws -> OctetSdk

    public static func attestationEnrolmentBundle() -> AttestationEnrolmentBundle?
}
object Octet {
    const val SDK_VERSION: String  // "2.0.0"

    suspend fun start(
        context: Context,
        config: OctetConfig,
        startPosition: Position? = null,
    ): OctetSdk

    fun attestationEnrolmentBundle(): AttestationEnrolmentBundle?
}

startPosition is an optional hint used internally during pipeline bring-up. Most integrators omit it.

What it does, in order

  1. Loads (or generates) a per-install UUID from secure storage (Keychain on iOS, EncryptedSharedPreferences on Android).
  2. Reads the app identity from the bundle (Bundle.main.bundleIdentifier / context.packageName).
  3. Reuses a valid cached lease if present. Otherwise bootstraps: attests the app and calls POST /v1/bootstrap to mint a device-bound license and lease. See Attested bootstrap.
  4. Brings up the internal proof pipeline.
  5. Attaches the resulting LicenseStatus to the returned OctetSdk.

OctetConfig

public struct OctetConfig: Sendable {
    public let licenseKey: String            // required by the config type, ignored at runtime; pass ""
    public var proofUploadUrl: String?       // opt-in proof upload; nil disables (default)
    public var telemetryEnabled: Bool        // aggregate usage counters; default true
    public var sandboxBypassToken: String?   // dev / CI / simulator bootstrap; nil disables
    public var advanced: AdvancedConfig

    public init(
        licenseKey: String,
        proofUploadUrl: String? = nil,
        telemetryEnabled: Bool = true,
        sandboxBypassToken: String? = nil,
        advanced: AdvancedConfig = AdvancedConfig()
    )

    public static let defaultActivationServerUrl: String  // "https://api.octetproof.com"
}
data class OctetConfig(
    val licenseKey: String,                  // required by the config type, ignored at runtime; pass ""
    val proofUploadUrl: String? = null,      // opt-in proof upload; null disables (default)
    val telemetryEnabled: Boolean = true,    // aggregate usage counters; default true
    val sandboxBypassToken: String? = null,  // dev / CI / simulator bootstrap; null disables
    val advanced: AdvancedConfig = AdvancedConfig(),
) {
    companion object {
        const val DEFAULT_ACTIVATION_SERVER_URL: String  // "https://api.octetproof.com"
    }
}

licenseKey is required by the config type but ignored by Octet.start, which activates through attested bootstrap. Pass an empty string on native (Swift and Kotlin). The React Native wrapper validates it as non-empty, so pass any non-empty placeholder there (for example "unused").

For local development, CI, and simulators, set sandboxBypassToken to bootstrap without hardware attestation.

AdvancedConfig

Still small. Battery profile, sensor tuning, and ML knobs stay internal. What is public: the activation server, log level, device-attestation cadence, and the per-platform attestation knobs.

public struct AdvancedConfig: Sendable {
    public var activationServerUrl: String        // default: production
    public var logLevel: LogLevel                 // default: .info
    public var enableCertPinning: Bool            // default: false
    public var attestationCadence: AttestationCadence  // default: .periodic(interval: 300)

    public init(
        activationServerUrl: String = OctetConfig.defaultActivationServerUrl,
        logLevel: LogLevel = .info,
        enableCertPinning: Bool = false,
        attestationCadence: AttestationCadence = .periodic(interval: 300)
    )
}

public enum LogLevel { case verbose, debug, info, warn, error }

public enum AttestationCadence: Sendable {
    case perSession
    case periodic(interval: TimeInterval)   // default; 5 minutes
    case perProof
}
data class AdvancedConfig(
    val activationServerUrl: String = OctetConfig.DEFAULT_ACTIVATION_SERVER_URL,
    val logLevel: LogLevel = LogLevel.INFO,
    // binds the per-proof Play Integrity verdict to a GCP project; null = Play Console project
    val playIntegrityCloudProjectNumber: Long? = null,
    val attestationCadence: AttestationCadence = AttestationCadence.Periodic(intervalSeconds = 300),
)

enum class LogLevel { VERBOSE, DEBUG, INFO, WARN, ERROR }

sealed class AttestationCadence {
    object PerSession : AttestationCadence()
    data class Periodic(val intervalSeconds: Int) : AttestationCadence()  // default; 5 minutes
    object PerProof : AttestationCadence()
}

Override activationServerUrl only if Octet gives you a specific URL to point at. It defaults to production. attestationCadence and the Android playIntegrityCloudProjectNumber are covered in Device Attestation. enableCertPinning opts into the bundled certificate pin set for api.octetproof.com. The Android playIntegrityCloudProjectNumber binds the per-proof Play Integrity verdict to a Google Cloud project. Leave it null to use the project linked in the Play Console.

attestationEnrolmentBundle()

Added in 1.2. Returns this device key's AttestationEnrolmentBundle, or nil before the device key has been attested. Attestation first happens on the install's first proof. The read is local (a Keychain entry on iOS, a Keystore entry on Android) and makes no network call.

Hand the bundle to a verifier's enrolment step so the verifier can establish this device's hardware root ahead of time, without waiting for the once-per-key attestation object to arrive on a submitted proof. This helps a verifier that was freshly deployed, scaled out, or migrated.

public struct AttestationEnrolmentBundle: Sendable {
    public func jsonString() -> String        // canonical v:1 envelope
    public func protoData() throws -> Data     // DeviceAttestation proto bytes
}
class AttestationEnrolmentBundle internal constructor(/* … */) {
    fun jsonString(): String
    fun protoData(): ByteArray
}

On iOS, App Attest produces the object once per key, so a verifier that has not yet seen a proof from this device cannot check its hardware root until the bundle arrives. On Android, every proof already carries the full Key Attestation certificate chain, so the bundle is a convenience mirror rather than a requirement.

Example

let config = OctetConfig(licenseKey: "")   // required by the type, ignored by start; the SDK attests the app
let sdk = try await Octet.start(config: config)
lifecycleScope.launch {
    // licenseKey required by the type, ignored by start; the SDK attests the app
    val config = OctetConfig(licenseKey = "")
    val sdk = Octet.start(context = applicationContext, config = config)
}

Failure modes

Octet.start(...) throws a typed LicenseError for every license-related failure. Other failures propagate as their native error types. The SDK does not throw a raw Error / Exception for license reasons.

LicenseError case Meaning
MalformedKey The key isn't a valid signed token.
NoActivation No cached activation, offline.
Expired Cached activation past the offline grace with the backend unreachable to refresh.
ActivationWindowClosed Fresh device trying to activate after day 90.
Revoked Admin revoke.
Network(message) / Network(cause) Transient network failure during activation.
ServerRejected(httpStatus, reason) Backend rejected for another reason (e.g., app_blocked).
UpgradeRequired(minVersion, message) Backend rejected this SDK version as out of support.
BootstrapFailed(reason) Attested bootstrap failed. reason is a BootstrapReason (attestation invalid, app not registered, sandbox-token issues, …).

BootstrapFailed carries a BootstrapReason. See License Types and Attested bootstrap. UpgradeRequired is SDK-version upgrade gating: the SDK reports its version on every backend request, and the backend can refuse an out-of-support version at Octet.start or return the softer LicenseStatus.upgradeRecommended and minSupportedVersion hints.

See also