field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

TeraSessionRemoval.swift (4289B)


      1 import Foundation
      2 
      3 extension TeraSessionStore {
      4   func applySettingsReconfiguration() async -> TeraSessionPhase {
      5     guard !removalInProgress else { return phase }
      6     generation = generation.invalidated()
      7     let requestedGeneration = generation
      8     phase = .starting
      9     do {
     10       let identity = try await identityStore.loadAndMigrate()
     11       try ensureCurrent(requestedGeneration)
     12       guard identity.state == .unlocked else {
     13         return await start()
     14       }
     15       let configuration = try await configurationStore.load()
     16         try ensureCurrent(requestedGeneration)
     17       phase = try await startRuntime(
     18         configuration: configuration,
     19         identity: identity,
     20         generation: requestedGeneration.requireActive(),
     21         forceReconfiguration: true,
     22         adoptBootstrapSettings: false
     23       )
     24     } catch {
     25       guard generation == requestedGeneration else { return phase }
     26       phase = .failed(
     27         .local(
     28           operation: "session.settings_reconfiguration",
     29           code: "ios.session.settings_reconfiguration_failed",
     30           safeMessage: "Tera could not apply the saved settings."
     31         )
     32       )
     33     }
     34     return phase
     35   }
     36 
     37   func keyRemovalPage(author: String, cursor: String?) async throws -> TeraLegacyDraftPage {
     38     guard !removalInProgress, case let .running(snapshot) = phase,
     39           snapshot.identity.publicKeyHex == author else { throw TeraIdentityStoreError.unavailable }
     40     let page = try await runtimeClient.legacyDraftPage(limit: 50, cursor: cursor)
     41     guard page.authorPublicKey == author else { throw TeraIdentityStoreError.unavailable }
     42     return page
     43   }
     44 
     45   func removeSigningKey(author: String, requests: [TeraKeyRemovalRequest]) async -> TeraSessionPhase {
     46     guard !removalInProgress, requests.count <= 100,
     47           case let .running(snapshot) = phase, snapshot.identity.publicKeyHex == author
     48     else { return phase }
     49     removalInProgress = true
     50     defer { removalInProgress = false }
     51     generation = generation.invalidated()
     52     let requestedGeneration = generation
     53     do {
     54       try ensureCurrent(requestedGeneration)
     55       let identity = try await identityStore.loadAndMigrate()
     56       guard identity.state == .unlocked, identity.publicKeyHex == author else {
     57         throw TeraIdentityStoreError.unavailable
     58       }
     59       for request in requests {
     60         try ensureCurrent(requestedGeneration)
     61         try await runtimeClient.prepareRetractionForKeyRemoval(request)
     62       }
     63       try ensureCurrent(requestedGeneration)
     64       _ = try await runtimeClient.stop()
     65       try ensureCurrent(requestedGeneration)
     66       let removed = try await identityStore.removeSigningKey(expected: identity)
     67       // Do not discard an actual completed deletion because cancellation or
     68       // lifecycle invalidation arrived after the last reversible boundary.
     69       if removed.state == .absent {
     70         phase = .identityRequired
     71       }
     72     } catch {
     73       // All local data and already signed requests remain with their owner.
     74       // Re-read custody on retry, including a recoverable partial transaction.
     75       guard generation == requestedGeneration else { return phase }
     76       phase = .failed(.local(operation: "identity.key_removal", code: "ios.identity.key_removal_incomplete",
     77                              safeMessage: "Key removal did not finish. Local data and any signed requests are retained. Retry to check identity recovery before continuing."))
     78     }
     79     return phase
     80   }
     81 
     82   func stop() async -> TeraSessionPhase {
     83     generation = generation.invalidated()
     84     let requestedGeneration = generation
     85     do {
     86       _ = try await runtimeClient.stop()
     87       try ensureCurrent(requestedGeneration)
     88       await identityStore.lock()
     89       try ensureCurrent(requestedGeneration)
     90       try qualificationEvidenceStore?.cleanup()
     91       phase = .stopped
     92     } catch let TeraRuntimeClientError.shutdown(failure) {
     93       guard isCurrent(requestedGeneration) else { return phase }
     94       phase = .failed(failure)
     95     } catch {
     96       guard isCurrent(requestedGeneration) else { return phase }
     97       phase = .failed(
     98         .local(
     99           operation: "session.stop",
    100           code: "ios.session.stop_failed",
    101           safeMessage: "Tera could not finish shutting down."
    102         )
    103       )
    104     }
    105     return phase
    106   }
    107 }