Octet.start(...)
The single bring-up call. Verifies the license key locally, activates against the Octet backend if needed, brings up the proof pipeline, and returns a fully-usable OctetSdk handle.
Signature
public enum Octet {
public static let sdkVersion: String // "1.2.1"
public static func start(
config: OctetConfig,
startPosition: Position? = nil
) async throws -> OctetSdk
public static func attestationEnrolmentBundle() -> AttestationEnrolmentBundle?
}
object Octet {
const val SDK_VERSION: String // "1.2.1"
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
- Loads (or generates) a per-install UUID from secure storage (Keychain on iOS,
EncryptedSharedPreferenceson Android). - Auto-detects the app id from the bundle (
Bundle.main.bundleIdentifier/context.packageName). - Verifies the license key's PASETO signature locally against the SDK's embedded public keys.
- Validates the cached activation token if present. Otherwise calls
POST /v1/activateto acquire one. - Brings up the internal proof pipeline.
- Attaches the resulting
LicenseStatusto the returnedOctetSdk.
OctetConfig
public struct OctetConfig: Sendable {
public let licenseKey: String
public var proofUploadUrl: String? // opt-in proof upload; nil disables (default)
public var telemetryEnabled: Bool // aggregate usage counters; default true
public var advanced: AdvancedConfig
public init(
licenseKey: String,
proofUploadUrl: String? = nil,
telemetryEnabled: Bool = true,
advanced: AdvancedConfig = AdvancedConfig()
)
public static let defaultActivationServerUrl: String // "https://api.octetproof.com"
}
data class OctetConfig(
val licenseKey: String,
val proofUploadUrl: String? = null, // opt-in proof upload; null disables (default)
val telemetryEnabled: Boolean = true, // aggregate usage counters; default true
val advanced: AdvancedConfig = AdvancedConfig(),
) {
companion object {
const val DEFAULT_ACTIVATION_SERVER_URL: String // "https://api.octetproof.com"
}
}
licenseKey is the only required field. It is a PASETO v4.public token in the wire form octet_live_v4.public.… (prod) or octet_test_… (staging).
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,
// Google Cloud project NUMBER for Play Integrity; null uses the Play-Console-linked 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 when pointing at a staging or local backend.
attestationCadence and the Android playIntegrityCloudProjectNumber are covered
in Device Attestation. enableCertPinning opts
into the bundled certificate pin set for api.octetproof.com. The Play Integrity
cloud-project knob is Android-only: App Attest on iOS has no equivalent.
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: "octet_live_v4.public.…"
// advanced left to defaults
)
let sdk = try await Octet.start(config: config)
lifecycleScope.launch {
val sdk = Octet.start(
context = applicationContext,
config = OctetConfig(licenseKey = "octet_live_v4.public.…")
)
}
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. |
UpgradeRequired is part of SDK-version upgrade gating, added in 1.2. The SDK reports its version and platform on every backend request, so the backend can refuse an out-of-support version at Octet.start, or return the softer LicenseStatus.upgradeRecommended and minSupportedVersion hints instead. Both paths are inert in 1.2, because the backend gates no version yet. See License Types for the field and case shapes.
See also
- License & Activation for the timeline model and activation flow.
OctetSdkfor whatstartreturns.- License Types for the full
LicenseErrorreference.