Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
211
apps/ios/Sources/Push/PushRegistrationManager.swift
Normal file
211
apps/ios/Sources/Push/PushRegistrationManager.swift
Normal file
@@ -0,0 +1,211 @@
|
||||
import CryptoKit
|
||||
import Foundation
|
||||
|
||||
private struct DirectGatewayPushRegistrationPayload: Encodable {
|
||||
var transport: String = PushTransportMode.direct.rawValue
|
||||
var token: String
|
||||
var topic: String
|
||||
var environment: String
|
||||
}
|
||||
|
||||
private struct RelayGatewayPushRegistrationPayload: Encodable {
|
||||
var transport: String = PushTransportMode.relay.rawValue
|
||||
var relayHandle: String
|
||||
var sendGrant: String
|
||||
var gatewayDeviceId: String
|
||||
var installationId: String
|
||||
var topic: String
|
||||
var environment: String
|
||||
var distribution: String
|
||||
var relayOrigin: String
|
||||
var tokenDebugSuffix: String?
|
||||
}
|
||||
|
||||
struct PushRelayGatewayIdentity: Codable {
|
||||
var deviceId: String
|
||||
var publicKey: String
|
||||
}
|
||||
|
||||
actor PushRegistrationManager {
|
||||
private let buildConfig: PushBuildConfig
|
||||
private let relayClient: PushRelayClient?
|
||||
|
||||
var usesRelayTransport: Bool {
|
||||
self.buildConfig.transport == .relay
|
||||
}
|
||||
|
||||
init(buildConfig: PushBuildConfig = .current) {
|
||||
self.buildConfig = buildConfig
|
||||
self.relayClient = buildConfig.relayBaseURL.map { PushRelayClient(baseURL: $0) }
|
||||
}
|
||||
|
||||
func makeGatewayRegistrationPayload(
|
||||
apnsTokenHex: String,
|
||||
topic: String,
|
||||
gatewayIdentity: PushRelayGatewayIdentity?)
|
||||
async throws -> String {
|
||||
switch self.buildConfig.transport {
|
||||
case .direct:
|
||||
return try Self.encodePayload(
|
||||
DirectGatewayPushRegistrationPayload(
|
||||
token: apnsTokenHex,
|
||||
topic: topic,
|
||||
environment: self.buildConfig.apnsEnvironment.rawValue))
|
||||
case .relay:
|
||||
guard let gatewayIdentity else {
|
||||
throw PushRelayError.relayMisconfigured("Missing gateway identity for relay registration")
|
||||
}
|
||||
return try await self.makeRelayPayload(
|
||||
apnsTokenHex: apnsTokenHex,
|
||||
topic: topic,
|
||||
gatewayIdentity: gatewayIdentity)
|
||||
}
|
||||
}
|
||||
|
||||
private func makeRelayPayload(
|
||||
apnsTokenHex: String,
|
||||
topic: String,
|
||||
gatewayIdentity: PushRelayGatewayIdentity)
|
||||
async throws -> String {
|
||||
guard self.buildConfig.distribution == .official else {
|
||||
throw PushRelayError.relayMisconfigured(
|
||||
"Relay transport requires an official push build mode")
|
||||
}
|
||||
try Self.validateRelayContract(
|
||||
relayProfile: self.buildConfig.relayProfile,
|
||||
apnsEnvironment: self.buildConfig.apnsEnvironment,
|
||||
proofPolicy: self.buildConfig.proofPolicy)
|
||||
GatewayDiagnostics.pushRelay.stage(
|
||||
"contract validated apns=\(self.buildConfig.apnsEnvironment.rawValue) "
|
||||
+ "profile=\(self.buildConfig.relayProfile.rawValue) "
|
||||
+ "proof=\(self.buildConfig.proofPolicy.rawValue)")
|
||||
guard let relayClient = self.relayClient else {
|
||||
throw PushRelayError.relayBaseURLMissing
|
||||
}
|
||||
guard let bundleId = Bundle.main.bundleIdentifier?.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
!bundleId.isEmpty
|
||||
else {
|
||||
throw PushRelayError.relayMisconfigured("Missing bundle identifier for relay registration")
|
||||
}
|
||||
guard let installationId = GatewaySettingsStore.loadStableInstanceID()?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
!installationId.isEmpty
|
||||
else {
|
||||
throw PushRelayError.relayMisconfigured("Missing stable installation ID for relay registration")
|
||||
}
|
||||
|
||||
let tokenHashHex = Self.sha256Hex(apnsTokenHex)
|
||||
let relayOrigin = relayClient.normalizedBaseURLString
|
||||
if let stored = PushRelayRegistrationStore.loadRegistrationState(),
|
||||
stored.installationId == installationId,
|
||||
stored.gatewayDeviceId == gatewayIdentity.deviceId,
|
||||
stored.relayOrigin == relayOrigin,
|
||||
stored.apnsEnvironment == self.buildConfig.apnsEnvironment.rawValue,
|
||||
stored.relayProfile == self.buildConfig.relayProfile.rawValue,
|
||||
stored.proofPolicy == self.buildConfig.proofPolicy.rawValue,
|
||||
stored.lastAPNsTokenHashHex == tokenHashHex,
|
||||
!Self.isExpired(stored.relayHandleExpiresAtMs)
|
||||
{
|
||||
GatewayDiagnostics.pushRelay.stage("using cached relay registration")
|
||||
return try Self.encodePayload(
|
||||
RelayGatewayPushRegistrationPayload(
|
||||
relayHandle: stored.relayHandle,
|
||||
sendGrant: stored.sendGrant,
|
||||
gatewayDeviceId: gatewayIdentity.deviceId,
|
||||
installationId: installationId,
|
||||
topic: topic,
|
||||
environment: self.buildConfig.apnsEnvironment.rawValue,
|
||||
distribution: self.buildConfig.distribution.rawValue,
|
||||
relayOrigin: relayOrigin,
|
||||
tokenDebugSuffix: stored.tokenDebugSuffix))
|
||||
}
|
||||
|
||||
GatewayDiagnostics.pushRelay.stage("relay registration cache miss")
|
||||
let response = try await relayClient.register(PushRelayRegistrationInput(
|
||||
installationId: installationId,
|
||||
bundleId: bundleId,
|
||||
appVersion: DeviceInfoHelper.appVersion(),
|
||||
environment: self.buildConfig.apnsEnvironment,
|
||||
relayProfile: self.buildConfig.relayProfile,
|
||||
proofPolicy: self.buildConfig.proofPolicy,
|
||||
distribution: self.buildConfig.distribution,
|
||||
apnsTokenHex: apnsTokenHex,
|
||||
gatewayIdentity: gatewayIdentity))
|
||||
let registrationState = PushRelayRegistrationStore.RegistrationState(
|
||||
relayHandle: response.relayHandle,
|
||||
sendGrant: response.sendGrant,
|
||||
relayOrigin: relayOrigin,
|
||||
gatewayDeviceId: gatewayIdentity.deviceId,
|
||||
relayHandleExpiresAtMs: response.expiresAtMs,
|
||||
tokenDebugSuffix: Self.normalizeTokenSuffix(response.tokenSuffix),
|
||||
lastAPNsTokenHashHex: tokenHashHex,
|
||||
installationId: installationId,
|
||||
lastTransport: self.buildConfig.transport.rawValue,
|
||||
apnsEnvironment: self.buildConfig.apnsEnvironment.rawValue,
|
||||
relayProfile: self.buildConfig.relayProfile.rawValue,
|
||||
proofPolicy: self.buildConfig.proofPolicy.rawValue)
|
||||
_ = PushRelayRegistrationStore.saveRegistrationState(registrationState)
|
||||
GatewayDiagnostics.pushRelay.stage("stored relay registration hasExpiry=\(response.expiresAtMs != nil)")
|
||||
return try Self.encodePayload(
|
||||
RelayGatewayPushRegistrationPayload(
|
||||
relayHandle: response.relayHandle,
|
||||
sendGrant: response.sendGrant,
|
||||
gatewayDeviceId: gatewayIdentity.deviceId,
|
||||
installationId: installationId,
|
||||
topic: topic,
|
||||
environment: self.buildConfig.apnsEnvironment.rawValue,
|
||||
distribution: self.buildConfig.distribution.rawValue,
|
||||
relayOrigin: relayOrigin,
|
||||
tokenDebugSuffix: registrationState.tokenDebugSuffix))
|
||||
}
|
||||
|
||||
private static func isExpired(_ expiresAtMs: Int64?) -> Bool {
|
||||
guard let expiresAtMs else { return true }
|
||||
let nowMs = Int64(Date().timeIntervalSince1970 * 1000)
|
||||
// Refresh shortly before expiry so reconnect-path republishes a live handle.
|
||||
return expiresAtMs <= nowMs + 60000
|
||||
}
|
||||
|
||||
private static func validateRelayContract(
|
||||
relayProfile: PushRelayProfile,
|
||||
apnsEnvironment: PushAPNsEnvironment,
|
||||
proofPolicy: PushProofPolicy)
|
||||
throws {
|
||||
switch relayProfile {
|
||||
case .production:
|
||||
guard apnsEnvironment == .production, proofPolicy == .appleStrict else {
|
||||
throw PushRelayError.relayMisconfigured(
|
||||
"production relay profile requires production APNs and appleStrict proof")
|
||||
}
|
||||
case .deviceSandbox:
|
||||
guard apnsEnvironment == .sandbox, proofPolicy == .appleDevelopment else {
|
||||
throw PushRelayError.relayMisconfigured(
|
||||
"deviceSandbox relay profile requires sandbox APNs and appleDevelopment proof")
|
||||
}
|
||||
case .simulatorSandbox:
|
||||
guard apnsEnvironment == .sandbox, proofPolicy == .internalSimulator else {
|
||||
throw PushRelayError.relayMisconfigured(
|
||||
"simulatorSandbox relay profile requires sandbox APNs and internalSimulator proof")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func sha256Hex(_ value: String) -> String {
|
||||
let digest = SHA256.hash(data: Data(value.utf8))
|
||||
return digest.map { String(format: "%02x", $0) }.joined()
|
||||
}
|
||||
|
||||
private static func normalizeTokenSuffix(_ value: String?) -> String? {
|
||||
guard let value else { return nil }
|
||||
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
||||
return trimmed.isEmpty ? nil : trimmed
|
||||
}
|
||||
|
||||
private static func encodePayload(_ payload: some Encodable) throws -> String {
|
||||
let data = try JSONEncoder().encode(payload)
|
||||
guard let json = String(data: data, encoding: .utf8) else {
|
||||
throw PushRelayError.relayMisconfigured("Failed to encode push registration payload as UTF-8")
|
||||
}
|
||||
return json
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user